vendor/madeyourday/contao-rocksolid-slider/src/Module/Slider.php line 432

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\RockSolidSlider\Module;
  9. use Contao\BackendTemplate;
  10. use Contao\Config;
  11. use Contao\CoreBundle\File\Metadata;
  12. use Contao\File;
  13. use Contao\FilesModel;
  14. use Contao\Model\Collection;
  15. use Contao\Module;
  16. use Contao\ModuleModel;
  17. use Contao\StringUtil;
  18. use Contao\System;
  19. use MadeYourDay\RockSolidSlider\Model\SlideModel;
  20. use MadeYourDay\RockSolidSlider\Model\SliderModel;
  21. use MadeYourDay\RockSolidSlider\Model\ContentModel;
  22. use Symfony\Component\HttpFoundation\Request;
  23. /**
  24.  * Slider Frontend Module
  25.  *
  26.  * @author Martin Auswöger <martin@madeyourday.net>
  27.  */
  28. class Slider extends Module
  29. {
  30.     /**
  31.      * @var string Template
  32.      */
  33.     protected $strTemplate 'rsts_default';
  34.     /**
  35.      * @return string
  36.      */
  37.     public function generate()
  38.     {
  39.         // Display a wildcard in the back end
  40.         if (System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest(System::getContainer()->get('request_stack')->getCurrentRequest() ?? Request::create(''))) {
  41.             $template = new BackendTemplate('be_wildcard');
  42.             $template->wildcard '### ROCKSOLID SLIDER ###';
  43.             $template->title $this->name;
  44.             $template->id $this->id;
  45.             $template->link $this->name;
  46.             $template->href StringUtil::specialcharsUrl(System::getContainer()->get('router')->generate('contao_backend', ['do' => 'themes''table' => 'tl_module''act' => 'edit''id'=> $this->id]));
  47.             if ($this->objModel->rsts_id && ($slider SliderModel::findByPk($this->objModel->rsts_id)) !== null) {
  48.                 $template->id $slider->id;
  49.                 $template->link $slider->name;
  50.                 $template->href StringUtil::specialcharsUrl(System::getContainer()->get('router')->generate('contao_backend', ['do' => 'rocksolid_slider''table' => 'tl_rocksolid_slide''id'=> $slider->id]));
  51.             }
  52.             return $template->parse();
  53.         }
  54.         if (
  55.             $this->rsts_import_settings
  56.             && $this->rsts_import_settings_from
  57.             && ($settingsModule ModuleModel::findByPk($this->rsts_import_settings_from))
  58.         ) {
  59.             $this->objModel->imgSize $settingsModule->imgSize;
  60.             $this->objModel->fullsize $settingsModule->fullsize;
  61.         }
  62.         if ($this->rsts_content_type === 'rsts_news') {
  63.             $newsModule = new SliderNews($this->objModel$this->strColumn);
  64.             $this->newsArticles $newsModule->getNewsArticles();
  65.             if (!count($this->newsArticles)) {
  66.                 // Return if there are no news articles
  67.                 return '';
  68.             }
  69.         }
  70.         else if ($this->rsts_content_type === 'rsts_events') {
  71.             $eventsModule = new SliderEvents($this->objModel$this->strColumn);
  72.             $this->eventItems $eventsModule->getEventItems();
  73.             if (!count($this->eventItems)) {
  74.                 // Return if there are no events
  75.                 return '';
  76.             }
  77.         }
  78.         else if ($this->rsts_content_type === 'rsts_settings') {
  79.             return '';
  80.         }
  81.         else if ($this->rsts_content_type === 'rsts_images' || !$this->rsts_id) {
  82.             $this->multiSRC StringUtil::deserialize($this->multiSRC);
  83.             if (!is_array($this->multiSRC) || !count($this->multiSRC)) {
  84.                 // Return if there are no images
  85.                 return '';
  86.             }
  87.         }
  88.         else {
  89.             $this->slider SliderModel::findByPk($this->rsts_id);
  90.             // Return if there is no slider
  91.             if (! $this->slider || $this->slider->id !== $this->rsts_id) {
  92.                 return '';
  93.             }
  94.             if ($this->slider->type === 'image') {
  95.                 $this->multiSRC StringUtil::deserialize($this->slider->multiSRC);
  96.             }
  97.         }
  98.         $this->files FilesModel::findMultipleByUuids($this->multiSRC);
  99.         if (
  100.             $this->rsts_import_settings
  101.             && $this->rsts_import_settings_from
  102.             && ($settingsModule ModuleModel::findByPk($this->rsts_import_settings_from))
  103.         ) {
  104.             $exclude = array('rsts_import_settings''rsts_import_settings_from''rsts_content_type''rsts_id');
  105.             $include = array('imgSize''fullsize');
  106.             foreach ($settingsModule->row() as $key => $value) {
  107.                 if (
  108.                     (substr($key05) === 'rsts_' || in_array($key$include))
  109.                     && !in_array($key$exclude)
  110.                 ) {
  111.                     $this->arrData[$key] = $value;
  112.                 }
  113.             }
  114.             $settingsCssId StringUtil::deserialize($settingsModule->cssIDtrue);
  115.             if (!empty($settingsCssId[1])) {
  116.                 $this->arrData['cssID'][1] = (
  117.                     empty($this->arrData['cssID'][1]) ? '' $this->arrData['cssID'][1] . ' '
  118.                 ) . $settingsCssId[1];
  119.             }
  120.         }
  121.         if ($this->rsts_template) {
  122.             $this->strTemplate $this->rsts_template;
  123.         }
  124.         return parent::generate();
  125.     }
  126.     /**
  127.      * Generate the module
  128.      */
  129.     protected function compile()
  130.     {
  131.         global $objPage;
  132.         $images = array();
  133.         if ($this->files) {
  134.             $files $this->files;
  135.             $filesExpaned = array();
  136.             // Get all images
  137.             while ($files->next()) {
  138.                 if ($files->type === 'file') {
  139.                     $filesExpaned[] = $files->current();
  140.                 }
  141.                 else {
  142.                     $subFiles FilesModel::findByPid($files->uuid);
  143.                     while ($subFiles && $subFiles->next()) {
  144.                         if ($subFiles->type === 'file'){
  145.                             $filesExpaned[] = $subFiles->current();
  146.                         }
  147.                     }
  148.                 }
  149.             }
  150.             foreach ($filesExpaned as $files) {
  151.                 // Continue if the files has been processed or does not exist
  152.                 if (isset($images[$files->path]) || ! file_exists(System::getContainer()->getParameter('kernel.project_dir') . '/' $files->path)) {
  153.                     continue;
  154.                 }
  155.                 $file = new File($files->pathtrue);
  156.                 if (!$file->isGdImage && !$file->isImage) {
  157.                     continue;
  158.                 }
  159.                 $arrMeta $this->getMetaData($files->meta$objPage->language);
  160.                 // Add the image
  161.                 $images[$files->path] = array
  162.                 (
  163.                     'id'        => $files->id,
  164.                     'uuid'      => isset($files->uuid) ? $files->uuid null,
  165.                     'name'      => $file->basename,
  166.                     'singleSRC' => $files->path,
  167.                     'alt'       => $arrMeta['alt'] ?? null,
  168.                     'title'     => $arrMeta['title'] ?? null,
  169.                     'imageUrl'  => $arrMeta['link'] ?? null,
  170.                     'caption'   => $arrMeta['caption'] ?? null,
  171.                     'fullsize'  => $this->fullsize,
  172.                 );
  173.             }
  174.             $images array_values($images);
  175.             foreach ($images as $key => $image) {
  176.                 $newImage = new \stdClass();
  177.                 $image['size'] = isset($this->imgSize) ? $this->imgSize $this->size;
  178.                 $this->applyImageToTemplate($newImage$imagenullsubstr(md5('mod_rocksolid_slider_' $this->id), 06), FilesModel::findByPk($image['id']));
  179.                 if ($this->rsts_navType === 'thumbs') {
  180.                     $newImage->thumb = new \stdClass;
  181.                     $image['size'] = $this->rsts_thumbs_imgSize;
  182.                     $this->applyImageToTemplate($newImage->thumb$image);
  183.                 }
  184.                 $images[$key] = $newImage;
  185.             }
  186.         }
  187.         // use custom skin if specified
  188.         if (trim($this->arrData['rsts_customSkin'])) {
  189.             $this->arrData['rsts_skin'] = trim($this->arrData['rsts_customSkin']);
  190.         }
  191.         $this->Template->images $images;
  192.         $slides = array();
  193.         if (isset($this->newsArticles)) {
  194.             foreach ($this->newsArticles as $newsArticle) {
  195.                 $slides[] = array(
  196.                     'text' => $newsArticle,
  197.                 );
  198.             }
  199.         }
  200.         else if (isset($this->eventItems)) {
  201.             foreach ($this->eventItems as $eventItem) {
  202.                 $slides[] = array(
  203.                     'text' => $eventItem,
  204.                 );
  205.             }
  206.         }
  207.         else if (isset($this->slider->id) && $this->slider->type === 'content') {
  208.             $slides $this->parseSlides(SlideModel::findPublishedByPid($this->slider->id));
  209.         }
  210.         $this->Template->slides $slides;
  211.         $options = array();
  212.         // strings
  213.         foreach (array(
  214.             'type',
  215.             'direction',
  216.             'cssPrefix',
  217.             'skin',
  218.             'width',
  219.             'height',
  220.             'navType',
  221.             'scaleMode',
  222.             'imagePosition',
  223.             'deepLinkPrefix',
  224.             'thumbs_width',
  225.             'thumbs_height',
  226.             'thumbs_scaleMode',
  227.             'thumbs_imagePosition',
  228.         ) as $key) {
  229.             if (! empty($this->arrData['rsts_' $key])) {
  230.                 $options[$key] = $this->arrData['rsts_' $key];
  231.             }
  232.         }
  233.         // strings / boolean
  234.         foreach (array('centerContent') as $key) {
  235.             if (! empty($this->arrData['rsts_' $key])) {
  236.                 $options[$key] = $this->arrData['rsts_' $key];
  237.                 if ($options[$key] === 'false') {
  238.                     $options[$key] = false;
  239.                 }
  240.                 if ($options[$key] === 'true') {
  241.                     $options[$key] = true;
  242.                 }
  243.             }
  244.         }
  245.         // boolean
  246.         foreach (array(
  247.             'random',
  248.             'loop',
  249.             'videoAutoplay',
  250.             'autoplayProgress',
  251.             'pauseAutoplayOnHover',
  252.             'keyboard',
  253.             'captions',
  254.             'controls',
  255.             'thumbControls',
  256.             'combineNavItems',
  257.             'thumbs_controls',
  258.         ) as $key) {
  259.             $options[$key] = (bool) $this->arrData['rsts_' $key];
  260.         }
  261.         // positive numbers
  262.         foreach (array(
  263.             'preloadSlides',
  264.             'duration',
  265.             'autoplay',
  266.             'autoplayRestart',
  267.             'slideMaxCount',
  268.             'slideMinSize',
  269.             'slideMaxSize',
  270.             'rowMaxCount',
  271.             'rowMinSize',
  272.             'prevNextSteps',
  273.             'visibleAreaMax',
  274.             'thumbs_duration',
  275.             'thumbs_slideMaxCount',
  276.             'thumbs_slideMinSize',
  277.             'thumbs_slideMaxSize',
  278.             'thumbs_rowMaxCount',
  279.             'thumbs_rowMinSize',
  280.             'thumbs_prevNextSteps',
  281.             'thumbs_visibleAreaMax',
  282.         ) as $key) {
  283.             if (! empty($this->arrData['rsts_' $key]) && $this->arrData['rsts_' $key] > 0) {
  284.                 $options[$key] = (float) $this->arrData['rsts_' $key];
  285.             }
  286.         }
  287.         // percentages
  288.         foreach (array('visibleArea''thumbs_visibleArea') as $key) {
  289.             if (!empty($this->arrData['rsts_' $key])) {
  290.                 $options[$key] = $this->arrData['rsts_' $key] / 100;
  291.             }
  292.         }
  293.         // percentages including zero
  294.         foreach (array('visibleAreaAlign') as $key) {
  295.             if (!empty($this->arrData['rsts_' $key])) {
  296.                 $options[$key] = $this->arrData['rsts_' $key] / 100;
  297.             }
  298.             else {
  299.                 $options[$key] = 0;
  300.             }
  301.         }
  302.         // ratios
  303.         foreach (array('rowSlideRatio''thumbs_rowSlideRatio') as $key) {
  304.             if (!empty($this->arrData['rsts_' $key])) {
  305.                 $ratio explode('x'$this->arrData['rsts_' $key], 2);
  306.                 if (empty($ratio[1])) {
  307.                     $ratio floatval($ratio[0]);
  308.                 }
  309.                 else {
  310.                     $ratio floatval($ratio[1]) / floatval($ratio[0]);
  311.                 }
  312.                 $options[$key] = $ratio;
  313.             }
  314.         }
  315.         // gap size
  316.         foreach (array('gapSize''thumbs_gapSize') as $key) {
  317.             if (isset($this->arrData['rsts_' $key]) && $this->arrData['rsts_' $key] !== '') {
  318.                 if (substr($this->arrData['rsts_' $key], -1) === '%') {
  319.                     $options[$key] = $this->arrData['rsts_' $key];
  320.                 }
  321.                 else {
  322.                     $options[$key] = (float) $this->arrData['rsts_' $key];
  323.                 }
  324.             }
  325.         }
  326.         foreach ($options as $key => $value) {
  327.             if (substr($key07) === 'thumbs_') {
  328.                 $options['thumbs'][substr($key7)] = $value;
  329.                 unset($options[$key]);
  330.             }
  331.         }
  332.         if (empty($this->arrData['rsts_thumbs']) && isset($options['thumbs'])) {
  333.             unset($options['thumbs']);
  334.         }
  335.         $this->Template->options $options;
  336.         $assetsDir 'bundles/rocksolidslider';
  337.         $GLOBALS['TL_JAVASCRIPT'][] = $assetsDir '/js/rocksolid-slider.min.js|static';
  338.         $GLOBALS['TL_CSS'][] = $assetsDir '/css/rocksolid-slider.min.css||static';
  339.         $skinPath $assetsDir '/css/' . (empty($this->arrData['rsts_skin']) ? 'default' $this->arrData['rsts_skin']) . '-skin.min.css';
  340.         if (file_exists(System::getContainer()->getParameter('contao.web_dir') . '/' $skinPath)) {
  341.             $GLOBALS['TL_CSS'][] = $skinPath '||static';
  342.         }
  343.     }
  344.     /**
  345.      * Parse slides
  346.      *
  347.      * @param  Collection $objSlides slides retrieved from the database
  348.      * @return array                        parsed slides
  349.      */
  350.     protected function parseSlides($objSlides)
  351.     {
  352.         global $objPage;
  353.         $slides = array();
  354.         $pids = array();
  355.         $idIndexes = array();
  356.         if (! $objSlides) {
  357.             return $slides;
  358.         }
  359.         while ($objSlides->next()) {
  360.             $slide $objSlides->row();
  361.             $slide['text'] = '';
  362.             if ($slide['type'] === 'content') {
  363.                 $pids[] = $slide['id'];
  364.                 $idIndexes[(int)$slide['id']] = count($slides);
  365.             }
  366.             if (
  367.                 in_array($slide['type'], array('image''video')) &&
  368.                 trim($slide['singleSRC']) &&
  369.                 ($file FilesModel::findByUuid($slide['singleSRC'])) &&
  370.                 ($fileObject = new File($file->pathtrue)) &&
  371.                 ($fileObject->isGdImage || $fileObject->isImage)
  372.             ) {
  373.                 $meta $this->getMetaData($file->meta$objPage->language);
  374.                 $slide['image'] = new \stdClass;
  375.                 $this->applyImageToTemplate($slide['image'], array(
  376.                     'id' => $file->id,
  377.                     'name' => $fileObject->basename,
  378.                     'singleSRC' => $file->path,
  379.                     'alt' => $meta['alt'] ?? null,
  380.                     'title' => $meta['title'] ?? null,
  381.                     'imageUrl' => $meta['link'] ?? null,
  382.                     'caption' => $meta['caption'] ?? null,
  383.                     'size' => isset($this->imgSize) ? $this->imgSize $this->size,
  384.                     'fullsize' => $this->fullsize,
  385.                 ), nullsubstr(md5('mod_rocksolid_slider_' $this->id), 06), $file);
  386.             }
  387.             if ($slide['type'] === 'video' && $slide['videoURL'] && empty($slide['image'])) {
  388.                 $slide['image'] = new \stdClass;
  389.                 if (preg_match(
  390.                     '(^
  391.                         https?://  # http or https
  392.                         (?:
  393.                             www\\.youtube\\.com/(?:watch\\?v=|v/|embed/)  # Different URL formats
  394.                             | youtu\\.be/  # Short YouTube domain
  395.                         )
  396.                         ([0-9a-z_\\-]{11})  # YouTube ID
  397.                         (?:$|&|/)  # End or separator
  398.                     )ix',
  399.                     html_entity_decode($slide['videoURL']), $matches)
  400.                 ) {
  401.                     $video $matches[1];
  402.                     $slide['image']->src '//img.youtube.com/vi/' $video '/0.jpg';
  403.                 }
  404.                 else {
  405.                     // Grey dummy image
  406.                     $slide['image']->src 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAJCAMAAAAM9FwAAAAAA1BMVEXGxsbd/8BlAAAAFUlEQVR42s3BAQEAAACAkP6vdiO6AgCZAAG/wrlvAAAAAElFTkSuQmCC';
  407.                 }
  408.                 $slide['image']->imgSize '';
  409.                 $slide['image']->alt '';
  410.                 $slide['image']->title '';
  411.                 $slide['image']->picture = array(
  412.                     'img' => array('src' => $slide['image']->src'srcset' => $slide['image']->src),
  413.                     'sources' => array(),
  414.                 );
  415.             }
  416.             if ($slide['type'] !== 'video' && $slide['videoURL']) {
  417.                 $slide['videoURL'] = '';
  418.             }
  419.             if ($slide['type'] === 'video' && $slide['videos']) {
  420.                 $videoFiles StringUtil::deserialize($slide['videos'], true);
  421.                 $videoFiles FilesModel::findMultipleByUuids($videoFiles) ?? [];
  422.                 $videos = array();
  423.                 foreach ($videoFiles as $file) {
  424.                     $videos[] = $file;
  425.                 }
  426.                 $slide['videos'] = $videos;
  427.             }
  428.             else {
  429.                 $slide['videos'] = null;
  430.             }
  431.             if (
  432.                 trim($slide['backgroundImage']) &&
  433.                 ($file FilesModel::findByUuid($slide['backgroundImage'])) &&
  434.                 ($fileObject = new File($file->pathtrue)) &&
  435.                 ($fileObject->isGdImage || $fileObject->isImage)
  436.             ) {
  437.                 $meta $this->getMetaData($file->meta$objPage->language);
  438.                 $slide['backgroundImage'] = new \stdClass;
  439.                 $this->applyImageToTemplate($slide['backgroundImage'], array(
  440.                     'id' => $file->id,
  441.                     'name' => $fileObject->basename,
  442.                     'singleSRC' => $file->path,
  443.                     'alt' => $meta['alt'] ?? null,
  444.                     'title' => $meta['title'] ?? null,
  445.                     'imageUrl' => $meta['link'] ?? null,
  446.                     'caption' => $meta['caption'] ?? null,
  447.                     'size' => $slide['backgroundImageSize'] ?? null,
  448.                 ));
  449.             }
  450.             else {
  451.                 $slide['backgroundImage'] = null;
  452.             }
  453.             if ($slide['backgroundVideos']) {
  454.                 $videoFiles StringUtil::deserialize($slide['backgroundVideos'], true);
  455.                 $videoFiles FilesModel::findMultipleByUuids($videoFiles) ?? [];
  456.                 $videos = array();
  457.                 foreach ($videoFiles as $file) {
  458.                     $videos[] = $file;
  459.                 }
  460.                 $slide['backgroundVideos'] = $videos;
  461.             }
  462.             if ($this->rsts_navType === 'thumbs') {
  463.                 $slide['thumb'] = new \stdClass;
  464.                 if (
  465.                     trim($slide['thumbImage']) &&
  466.                     ($file FilesModel::findByUuid($slide['thumbImage'])) &&
  467.                     ($fileObject = new File($file->pathtrue)) &&
  468.                     ($fileObject->isGdImage || $fileObject->isImage)
  469.                 ) {
  470.                     $this->applyImageToTemplate($slide['thumb'], array(
  471.                         'id' => $file->id,
  472.                         'name' => $fileObject->basename,
  473.                         'singleSRC' => $file->path,
  474.                         'size' => $this->rsts_thumbs_imgSize,
  475.                     ));
  476.                 }
  477.                 elseif (
  478.                     in_array($slide['type'], array('image''video')) &&
  479.                     trim($slide['singleSRC']) &&
  480.                     ($file FilesModel::findByUuid($slide['singleSRC'])) &&
  481.                     ($fileObject = new File($file->pathtrue)) &&
  482.                     ($fileObject->isGdImage || $fileObject->isImage)
  483.                 ) {
  484.                     $this->applyImageToTemplate($slide['thumb'], array(
  485.                         'id' => $file->id,
  486.                         'name' => $fileObject->basename,
  487.                         'singleSRC' => $file->path,
  488.                         'size' => $this->rsts_thumbs_imgSize,
  489.                     ));
  490.                 }
  491.                 elseif (!empty($slide['image']->src)) {
  492.                     $slide['thumb'] = clone $slide['image'];
  493.                 }
  494.                 elseif (!empty($slide['backgroundImage']->src)) {
  495.                     $slide['thumb'] = clone $slide['backgroundImage'];
  496.                 }
  497.             }
  498.             $slides[] = $slide;
  499.         }
  500.         if (count($pids)) {
  501.             $slideContents ContentModel::findPublishedByPidsAndTable($pidsSlideModel::getTable());
  502.             if ($slideContents) {
  503.                 while ($slideContents->next()) {
  504.                     $slides[$idIndexes[(int)$slideContents->pid]]['text'] .= $this->getContentElement($slideContents->current());
  505.                 }
  506.             }
  507.         }
  508.         return $slides;
  509.     }
  510.     private function applyImageToTemplate($template, array $rowData$maxWidth null$lightboxGroupIdentifier null, ?FilesModel $filesModel null): void
  511.     {
  512.         // Helper: Create metadata from the specified row data
  513.         $createMetadataOverwriteFromRowData = static function (bool $interpretAsContentModel) use ($rowData)
  514.         {
  515.             if ($interpretAsContentModel)
  516.             {
  517.                 // This will be null if "overwriteMeta" is not set
  518.                 return (new \Contao\ContentModel())->setRow($rowData)->getOverwriteMetadata();
  519.             }
  520.             // Manually create metadata that always contains certain properties (BC)
  521.             return new Metadata(array(
  522.                 Metadata::VALUE_ALT => $rowData['alt'] ?? '',
  523.                 Metadata::VALUE_TITLE => $rowData['imageTitle'] ?? '',
  524.                 Metadata::VALUE_URL => System::getContainer()->get('contao.insert_tag.parser')->replaceInline($rowData['imageUrl'] ?? ''),
  525.                 'linkTitle' => (string) ($rowData['linkTitle'] ?? ''),
  526.             ));
  527.         };
  528.         // Helper: Create fallback template data with (mostly) empty fields (used if resource acquisition fails)
  529.         $createFallBackTemplateData = static function () use ($filesModel$rowData)
  530.         {
  531.             $templateData = array(
  532.                 'width' => null,
  533.                 'height' => null,
  534.                 'picture' => array(
  535.                     'img' => array(
  536.                         'src' => '',
  537.                         'srcset' => '',
  538.                     ),
  539.                     'sources' => array(),
  540.                     'alt' => '',
  541.                     'title' => '',
  542.                 ),
  543.                 'singleSRC' => $rowData['singleSRC'],
  544.                 'src' => '',
  545.                 'linkTitle' => '',
  546.                 'margin' => '',
  547.                 'addImage' => true,
  548.                 'addBefore' => true,
  549.                 'fullsize' => false,
  550.             );
  551.             if (null !== $filesModel)
  552.             {
  553.                 // Set empty metadata
  554.                 $templateData array_replace_recursive(
  555.                     $templateData,
  556.                     array(
  557.                         'alt' => '',
  558.                         'caption' => '',
  559.                         'imageTitle' => '',
  560.                         'imageUrl' => '',
  561.                     )
  562.                 );
  563.             }
  564.             return $templateData;
  565.         };
  566.         $figureBuilder System::getContainer()->get('contao.image.studio')->createFigureBuilder();
  567.         // Set image resource
  568.         if (null !== $filesModel)
  569.         {
  570.             // Make sure model points to the same resource (BC)
  571.             $filesModel = clone $filesModel;
  572.             $filesModel->path $rowData['singleSRC'];
  573.             // Use source + metadata from files model (if not overwritten)
  574.             $figureBuilder
  575.                 ->fromFilesModel($filesModel)
  576.                 ->setMetadata($createMetadataOverwriteFromRowData(true));
  577.             $includeFullMetadata true;
  578.         }
  579.         else
  580.         {
  581.             // Always ignore file metadata when building from path (BC)
  582.             $figureBuilder
  583.                 ->fromPath($rowData['singleSRC'], false)
  584.                 ->setMetadata($createMetadataOverwriteFromRowData(false));
  585.             $includeFullMetadata false;
  586.         }
  587.         // Set size and lightbox configuration
  588.         $size $rowData['size'] ?? null;
  589.         $lightboxSize StringUtil::deserialize($rowData['lightboxSize'] ?? null) ?: null;
  590.         $figure $figureBuilder
  591.             ->setSize($size)
  592.             ->setLightboxGroupIdentifier($lightboxGroupIdentifier)
  593.             ->setLightboxSize($lightboxSize)
  594.             ->enableLightbox((bool) ($rowData['fullsize'] ?? false))
  595.             ->buildIfResourceExists();
  596.         if (null === $figure)
  597.         {
  598.             System::getContainer()->get('monolog.logger.contao.error')->error('Image "' $rowData['singleSRC'] . '" could not be processed: ' $figureBuilder->getLastException()->getMessage());
  599.             // Fall back to apply a sparse data set instead of failing (BC)
  600.             foreach ($createFallBackTemplateData() as $key => $value)
  601.             {
  602.                 $template->$key $value;
  603.             }
  604.             return;
  605.         }
  606.         // Build result and apply it to the template
  607.         $figure->applyLegacyTemplateData($templatenull$rowData['floating'] ?? null$includeFullMetadata);
  608.         // Fall back to manually specified link title or empty string if not set (backwards compatibility)
  609.         $template->linkTitle ??= StringUtil::specialchars($rowData['title'] ?? '');
  610.     }
  611. }