vendor/metamodels/core/src/Helper/ToolboxFile.php line 177

Open in your IDE?
  1. <?php
  2. /**
  3.  * This file is part of MetaModels/core.
  4.  *
  5.  * (c) 2012-2019 The MetaModels team.
  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    MetaModels/core
  13.  * @author     Christian Schiffler <c.schiffler@cyberspectrum.de>
  14.  * @author     Andreas Isaak <info@andreas-isaak.de>
  15.  * @author     Andreas Nölke <zero@brothers-project.de>
  16.  * @author     David Maack <david.maack@arcor.de>
  17.  * @author     Stefan Heimes <stefan_heimes@hotmail.com>
  18.  * @author     Christopher Boelter <christopher@boelter.eu>
  19.  * @author     Ingolf Steinhardt <info@e-spin.de>
  20.  * @author     Sven Baumann <baumann.sv@gmail.com>
  21.  * @copyright  2012-2019 The MetaModels team.
  22.  * @license    https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later
  23.  * @filesource
  24.  */
  25. namespace MetaModels\Helper;
  26. use Contao\Controller;
  27. use Contao\CoreBundle\Image\ImageFactoryInterface;
  28. use Contao\Dbafs;
  29. use Contao\Environment;
  30. use Contao\File;
  31. use Contao\FilesModel;
  32. use Contao\Input;
  33. use Contao\PageError403;
  34. use Contao\Picture;
  35. use Contao\StringUtil;
  36. use Contao\System;
  37. use Contao\Validator;
  38. use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents;
  39. use ContaoCommunityAlliance\Contao\Bindings\Events\Image\ResizeImageEvent;
  40. use ContaoCommunityAlliance\UrlBuilder\UrlBuilder;
  41. use InvalidArgumentException;
  42. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  43. /**
  44.  * This class provides various methods for handling file collection within Contao.
  45.  *
  46.  * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
  47.  */
  48. class ToolboxFile
  49. {
  50.     /**
  51.      * The event dispatcher.
  52.      *
  53.      * @var EventDispatcherInterface
  54.      *
  55.      * @deprecated The event dispatcher will get removed in 3.0 as we now use the image factory.
  56.      */
  57.     private $dispatcher;
  58.     /**
  59.      * The project root dir.
  60.      *
  61.      * @var string
  62.      */
  63.     private $rootDir;
  64.     /**
  65.      * The image factory for resizing.
  66.      *
  67.      * @var ImageFactoryInterface
  68.      */
  69.     private $imageFactory;
  70.     /**
  71.      * Allowed file extensions.
  72.      *
  73.      * @var array
  74.      */
  75.     protected $acceptedExtensions;
  76.     /**
  77.      * Base language, used for retrieving meta.txt information.
  78.      *
  79.      * @var string
  80.      */
  81.     protected $baseLanguage;
  82.     /**
  83.      * The fallback language, used for retrieving meta.txt information.
  84.      *
  85.      * @var string
  86.      */
  87.     protected $fallbackLanguage;
  88.     /**
  89.      * Determines if we want to generate images or not.
  90.      *
  91.      * @var boolean
  92.      */
  93.     protected $blnShowImages;
  94.     /**
  95.      * Image resize information.
  96.      *
  97.      * @var array
  98.      */
  99.     protected $resizeImages;
  100.     /**
  101.      * The id to use in lightboxes.
  102.      *
  103.      * @var string
  104.      */
  105.     protected $strLightboxId;
  106.     /**
  107.      * The files to process in this instance.
  108.      *
  109.      * @var array
  110.      */
  111.     protected $foundFiles = array();
  112.     /**
  113.      * The pending paths to collect from DB.
  114.      *
  115.      * @var string[]
  116.      */
  117.     protected $pendingPaths = array();
  118.     /**
  119.      * The pending uuids to collect from DB.
  120.      *
  121.      * @var array
  122.      */
  123.     protected $pendingIds = array();
  124.     /**
  125.      * Meta information for files.
  126.      *
  127.      * @var array
  128.      */
  129.     protected $metaInformation;
  130.     /**
  131.      * File id mapping for files.
  132.      *
  133.      * @var string[]
  134.      */
  135.     protected $uuidMap = array();
  136.     /**
  137.      * Buffered file information.
  138.      *
  139.      * @var array
  140.      */
  141.     protected $outputBuffer;
  142.     /**
  143.      * Buffered modification timestamps.
  144.      *
  145.      * @var array
  146.      */
  147.     protected $modifiedTime;
  148.     /**
  149.      * Create a new instance.
  150.      *
  151.      * @param ImageFactoryInterface|EventDispatcherInterface|null $imageFactory The image factory to use).
  152.      * @param string|null                                         $rootDir      The root path of the installation.
  153.      *
  154.      * @SuppressWarnings(PHPMD.Superglobals)
  155.      * @SuppressWarnings(PHPMD.CamelCaseVariableName)
  156.      */
  157.     public function __construct($imageFactory nullstring $rootDir null)
  158.     {
  159.         switch (true) {
  160.             case ($imageFactory instanceof ImageFactoryInterface) && !empty($rootDir):
  161.                 $this->imageFactory $imageFactory;
  162.                 $this->rootDir      $rootDir;
  163.                 break;
  164.             // This is the deprecated fallback (remove in MetaModels 3.0).
  165.             case $imageFactory instanceof EventDispatcherInterface:
  166.                 // @codingStandardsIgnoreStart
  167.                 @trigger_error(
  168.                     'Passing an "EventDispatcherInterface" is deprecated, use a "ImageFactoryInterface" instead.',
  169.                     E_USER_DEPRECATED
  170.                 );
  171.                 // @codingStandardsIgnoreEnd
  172.                 $this->dispatcher $imageFactory;
  173.                 break;
  174.             // This is another deprecated fallback (remove in MetaModels 3.0).
  175.             default:
  176.                 // @codingStandardsIgnoreStart
  177.                 @trigger_error(
  178.                     'Not passing an "ImageFactoryInterface" and root path is deprecated.',
  179.                     E_USER_DEPRECATED
  180.                 );
  181.                 // @codingStandardsIgnoreEnd
  182.                 $this->dispatcher System::getContainer()->get('event_dispatcher');
  183.         }
  184.         // Initialize some values to sane base.
  185.         if (isset($GLOBALS['TL_CONFIG']) && isset($GLOBALS['TL_CONFIG']['allowedDownload'])) {
  186.             $this->setAcceptedExtensions(StringUtil::trimsplit(','$GLOBALS['TL_CONFIG']['allowedDownload']));
  187.         }
  188.         if (isset($_SESSION) && !is_array($_SESSION['metaModels_downloads'])) {
  189.             $_SESSION['metaModels_downloads'] = [];
  190.         }
  191.     }
  192.     /**
  193.      * Set the allowed file extensions.
  194.      *
  195.      * @param string|array $acceptedExtensions The list of accepted file extensions.
  196.      *
  197.      * @return void
  198.      *
  199.      * @SuppressWarnings(PHPMD.Superglobals)
  200.      * @SuppressWarnings(PHPMD.CamelCaseVariableName)
  201.      */
  202.     public function setAcceptedExtensions($acceptedExtensions)
  203.     {
  204.         // We must not allow file extensions that are globally disabled.
  205.         $allowedDownload StringUtil::trimsplit(','$GLOBALS['TL_CONFIG']['allowedDownload']);
  206.         if (!is_array($acceptedExtensions)) {
  207.             $acceptedExtensions StringUtil::trimsplit(','$acceptedExtensions);
  208.         }
  209.         $this->acceptedExtensions array_map('strtolower'array_intersect($allowedDownload$acceptedExtensions));
  210.     }
  211.     /**
  212.      * Retrieve the allowed file extensions.
  213.      *
  214.      * @return array
  215.      */
  216.     public function getAcceptedExtensions()
  217.     {
  218.         return $this->acceptedExtensions;
  219.     }
  220.     /**
  221.      * Set the base language.
  222.      *
  223.      * @param string $baseLanguage The base language to use.
  224.      *
  225.      * @return ToolboxFile
  226.      */
  227.     public function setBaseLanguage($baseLanguage)
  228.     {
  229.         $this->baseLanguage $baseLanguage;
  230.         return $this;
  231.     }
  232.     /**
  233.      * Retrieve the base language.
  234.      *
  235.      * @return string
  236.      */
  237.     public function getBaseLanguage()
  238.     {
  239.         return $this->baseLanguage;
  240.     }
  241.     /**
  242.      * Set the fallback language.
  243.      *
  244.      * @param string $fallbackLanguage The fallback language to use.
  245.      *
  246.      * @return ToolboxFile
  247.      */
  248.     public function setFallbackLanguage($fallbackLanguage)
  249.     {
  250.         $this->fallbackLanguage $fallbackLanguage;
  251.         return $this;
  252.     }
  253.     /**
  254.      * Retrieve the fallback language.
  255.      *
  256.      * @return string
  257.      */
  258.     public function getFallbackLanguage()
  259.     {
  260.         return $this->fallbackLanguage;
  261.     }
  262.     /**
  263.      * Set to show/prepare images or not.
  264.      *
  265.      * @param boolean $blnShowImages True to show images, false otherwise.
  266.      *
  267.      * @return ToolboxFile
  268.      */
  269.     public function setShowImages($blnShowImages)
  270.     {
  271.         $this->blnShowImages $blnShowImages;
  272.         return $this;
  273.     }
  274.     /**
  275.      * Retrieve the flag if images shall be rendered as images.
  276.      *
  277.      * @return boolean
  278.      *
  279.      * @SuppressWarnings(PHPMD.BooleanGetMethodName)
  280.      */
  281.     public function getShowImages()
  282.     {
  283.         return $this->blnShowImages;
  284.     }
  285.     /**
  286.      * Set the resize information.
  287.      *
  288.      * @param array $resizeImages The resize information. Array of 3 elements: 0: Width, 1: Height, 2: Mode.
  289.      *
  290.      * @return ToolboxFile
  291.      */
  292.     public function setResizeImages($resizeImages)
  293.     {
  294.         $this->resizeImages $resizeImages;
  295.         return $this;
  296.     }
  297.     /**
  298.      * Retrieve the resize information.
  299.      *
  300.      * @return array
  301.      */
  302.     public function getResizeImages()
  303.     {
  304.         return $this->resizeImages;
  305.     }
  306.     /**
  307.      * Sets the Id to use for the lightbox.
  308.      *
  309.      * @param string $strLightboxId The lightbox id to use.
  310.      *
  311.      * @return ToolboxFile
  312.      */
  313.     public function setLightboxId($strLightboxId)
  314.     {
  315.         $this->strLightboxId $strLightboxId;
  316.         return $this;
  317.     }
  318.     /**
  319.      * Retrieve the lightbox id to use.
  320.      *
  321.      * @return string
  322.      */
  323.     public function getLightboxId()
  324.     {
  325.         return $this->strLightboxId;
  326.     }
  327.     /**
  328.      * Add path to file or folder list.
  329.      *
  330.      * @param string $strPath The path to be added.
  331.      *
  332.      * @return ToolboxFile
  333.      */
  334.     public function addPath($strPath)
  335.     {
  336.         $this->pendingPaths[] = $strPath;
  337.         return $this;
  338.     }
  339.     /**
  340.      * Contao 3 DBAFS Support.
  341.      *
  342.      * @param string $strId String uuid of the file.
  343.      *
  344.      * @return ToolboxFile
  345.      */
  346.     public function addPathById($strId)
  347.     {
  348.         // Check if empty.
  349.         if (empty($strId)) {
  350.             return $this;
  351.         }
  352.         if (!Validator::isBinaryUuid($strId)) {
  353.             $this->pendingIds[] = StringUtil::uuidToBin($strId);
  354.             return $this;
  355.         }
  356.         $this->pendingIds[] = $strId;
  357.         return $this;
  358.     }
  359.     /**
  360.      * Walks the list of pending folders via ToolboxFile::addPath().
  361.      *
  362.      * @return void
  363.      */
  364.     protected function collectFiles()
  365.     {
  366.         $table FilesModel::getTable();
  367.         $conditions = array();
  368.         $parameters = array();
  369.         if (count($this->pendingIds)) {
  370.             $conditions[] = $table '.uuid IN(' .
  371.                 implode(','array_fill(0count($this->pendingIds), 'UNHEX(?)')) . ')';
  372.             $parameters   array_map('bin2hex'$this->pendingIds);
  373.             $this->pendingIds = array();
  374.         }
  375.         if (count($this->pendingPaths)) {
  376.             $slug $table '.path LIKE ?';
  377.             foreach ($this->pendingPaths as $pendingPath) {
  378.                 $conditions[] = $slug;
  379.                 $parameters[] = $pendingPath '%';
  380.             }
  381.             $this->pendingPaths = array();
  382.         }
  383.         if (!count($conditions)) {
  384.             return;
  385.         }
  386.         if ($files FilesModel::findBy(array(implode(' OR '$conditions)), $parameters)) {
  387.             $this->addFileModels($files);
  388.         }
  389.         if (count($this->pendingPaths)) {
  390.             // Run again.
  391.             $this->collectFiles();
  392.         }
  393.     }
  394.     /**
  395.      * Generate an URL for downloading the given file.
  396.      *
  397.      * @param string $strFile The file that shall be downloaded.
  398.      *
  399.      * @return string
  400.      *
  401.      * @SuppressWarnings(PHPMD.Superglobals)
  402.      * @SuppressWarnings(PHPMD.CamelCaseVariableName)
  403.      */
  404.     protected function getDownloadLink($strFile)
  405.     {
  406.         if (!isset($_SESSION['metaModels_downloads'][$strFile])) {
  407.             $_SESSION['metaModels_downloads'][$strFile] = md5(uniqid());
  408.         }
  409.         return UrlBuilder::fromUrl(Environment::get('request'))
  410.             ->setQueryParameter('file'urlencode($strFile))
  411.             ->setQueryParameter('fileKey'$_SESSION['metaModels_downloads'][$strFile])
  412.             ->getUrl();
  413.     }
  414.     /**
  415.      * Walk all files and fetch desired additional information like image sizes etc.
  416.      *
  417.      * @return void
  418.      *
  419.      * @SuppressWarnings(PHPMD.Superglobals)
  420.      * @SuppressWarnings(PHPMD.CamelCaseVariableName)
  421.      */
  422.     protected function fetchAdditionalData()
  423.     {
  424.         $this->modifiedTime = array();
  425.         $this->outputBuffer = array();
  426.         if (!$this->foundFiles) {
  427.             return;
  428.         }
  429.         foreach ($this->foundFiles as $strFile) {
  430.             $this->processFile($strFile);
  431.         }
  432.     }
  433.     /**
  434.      * Maps the sorting from the files to the source.
  435.      *
  436.      * All files from $arrFiles are being walked and the corresponding entry from source gets pulled in.
  437.      *
  438.      * Additionally, the css classes are applied to the returned 'source' array.
  439.      *
  440.      * This returns an array like: array('files' => array(), 'source' => array())
  441.      *
  442.      * @param array $arrFiles  The files to sort.
  443.      *
  444.      * @param array $arrSource The source list.
  445.      *
  446.      * @return array The mapped result.
  447.      */
  448.     protected function remapSorting($arrFiles$arrSource)
  449.     {
  450.         $files  = array();
  451.         $source = array();
  452.         foreach (array_keys($arrFiles) as $k) {
  453.             $files[]  = $arrFiles[$k];
  454.             $source[] = $arrSource[$k];
  455.         }
  456.         $this->addClasses($source);
  457.         return array
  458.         (
  459.             'files' => $files,
  460.             'source' => $source
  461.         );
  462.     }
  463.     /**
  464.      * Sorts the internal file list by a given condition.
  465.      *
  466.      * Allowed sort types are:
  467.      * name_asc  - Sort by filename ascending.
  468.      * name_desc - Sort by filename descending
  469.      * date_asc  - Sort by modification time ascending.
  470.      * date_desc - Sort by modification time descending.
  471.      * manual    - Sort by passed id array, the array must contain the binary ids of the files.
  472.      * random    - Shuffle all the files around.
  473.      *
  474.      * @param string $sortType The sort condition to be applied.
  475.      *
  476.      * @param array  $sortIds  The list of binary ids to sort by (sort type "manual" only).
  477.      *
  478.      * @return array The sorted file list.
  479.      */
  480.     public function sortFiles($sortType$sortIds = array())
  481.     {
  482.         switch ($sortType) {
  483.             case 'name_desc':
  484.                 return $this->sortByName(false);
  485.             case 'date_asc':
  486.                 return $this->sortByDate(true);
  487.             case 'date_desc':
  488.                 return $this->sortByDate(false);
  489.             case 'manual':
  490.                 return $this->sortByIdList($sortIds);
  491.             case 'random':
  492.                 return $this->sortByRandom();
  493.             default:
  494.             case 'name_asc':
  495.         }
  496.         return $this->sortByName(true);
  497.     }
  498.     /**
  499.      * Attach first, last and even/odd classes to the given array.
  500.      *
  501.      * @param array $arrSource The array reference of the array to which the classes shall be added to.
  502.      *
  503.      * @return void
  504.      */
  505.     protected function addClasses(&$arrSource)
  506.     {
  507.         $countFiles count($arrSource);
  508.         foreach (array_keys($arrSource) as $k) {
  509.             $arrSource[$k]['class'] = (($k == 0) ? ' first' '') .
  510.                 (($k == ($countFiles 1)) ? ' last' '') .
  511.                 ((($k 2) == 0) ? ' even' ' odd');
  512.         }
  513.     }
  514.     /**
  515.      * Sort by filename.
  516.      *
  517.      * @param boolean $blnAscending Flag to determine if sorting shall be applied ascending (default) or descending.
  518.      *
  519.      * @return array
  520.      */
  521.     protected function sortByName($blnAscending true)
  522.     {
  523.         $arrFiles $this->foundFiles;
  524.         if (!$arrFiles) {
  525.             return array('files' => array(), 'source' => array());
  526.         }
  527.         \uasort($arrFiles, ($blnAscending) ? '\basename_natcasecmp' '\basename_natcasercmp');
  528.         return $this->remapSorting($arrFiles$this->outputBuffer);
  529.     }
  530.     /**
  531.      * Sort by modification time.
  532.      *
  533.      * @param boolean $blnAscending Flag to determine if sorting shall be applied ascending (default) or descending.
  534.      *
  535.      * @return array
  536.      */
  537.     protected function sortByDate($blnAscending true)
  538.     {
  539.         $arrFiles $this->foundFiles;
  540.         $arrDates $this->modifiedTime;
  541.         if (!$arrFiles) {
  542.             return array('files' => array(), 'source' => array());
  543.         }
  544.         if ($blnAscending) {
  545.             array_multisort($arrFilesSORT_NUMERIC$arrDatesSORT_ASC);
  546.         } else {
  547.             array_multisort($arrFilesSORT_NUMERIC$arrDatesSORT_DESC);
  548.         }
  549.         return $this->remapSorting($arrFiles$this->outputBuffer);
  550.     }
  551.     /**
  552.      * Sort by passed id list.
  553.      *
  554.      * @param array $sortIds The list of binary ids to sort by.
  555.      *
  556.      * @return array
  557.      */
  558.     protected function sortByIdList($sortIds)
  559.     {
  560.         $fileMap $this->foundFiles;
  561.         if (!$fileMap) {
  562.             return array('files' => array(), 'source' => array());
  563.         }
  564.         $fileKeys array_flip(array_keys($this->uuidMap));
  565.         $sorted   = array();
  566.         foreach ($sortIds as $sortStringId) {
  567.             $key          $fileKeys[$sortStringId];
  568.             $sorted[$key] = $fileMap[$key];
  569.             unset($fileMap[$key]);
  570.         }
  571.         // Add anything not sorted yet to the end.
  572.         $sorted += $fileMap;
  573.         return $this->remapSorting($sorted$this->outputBuffer);
  574.     }
  575.     /**
  576.      * Shuffle the file list.
  577.      *
  578.      * @return array
  579.      */
  580.     protected function sortByRandom()
  581.     {
  582.         $arrFiles  $this->foundFiles;
  583.         $arrSource $this->outputBuffer;
  584.         if (!$arrFiles) {
  585.             return array('files' => array(), 'source' => array());
  586.         }
  587.         $keys  array_keys($arrFiles);
  588.         $files = array();
  589.         shuffle($keys);
  590.         foreach ($keys as $key) {
  591.             $files[$key] = $arrFiles[$key];
  592.         }
  593.         return $this->remapSorting($files$arrSource);
  594.     }
  595.     /**
  596.      * Returns the file list.
  597.      *
  598.      * NOTE: you must call resolveFiles() beforehand as otherwise folders are not being evaluated.
  599.      *
  600.      * @return array
  601.      */
  602.     public function getFiles()
  603.     {
  604.         return $this->foundFiles;
  605.     }
  606.     /**
  607.      * Process all folders and resolve to a valid file list.
  608.      *
  609.      * @return ToolboxFile
  610.      */
  611.     public function resolveFiles()
  612.     {
  613.         // Step 1.: fetch all files.
  614.         $this->collectFiles();
  615.         // Step 1.1.: Check if any file is to be served.
  616.         $this->checkDownloads();
  617.         // Step 2.: fetch additional information like modification time etc. and prepare the output buffer.
  618.         $this->fetchAdditionalData();
  619.         return $this;
  620.     }
  621.     /**
  622.      * Check if a file download is desired.
  623.      *
  624.      * See https://github.com/MetaModels/attribute_file/issues/6
  625.      * See https://github.com/MetaModels/core/issues/1014
  626.      *
  627.      * @return void
  628.      *
  629.      * @SuppressWarnings(PHPMD.Superglobals)
  630.      * @SuppressWarnings(PHPMD.CamelCaseVariableName)
  631.      */
  632.     private function checkDownloads()
  633.     {
  634.         // If images are to be shown, get out.
  635.         if ($this->getShowImages()) {
  636.             return;
  637.         }
  638.         if (($file Input::get('file')) && ($key Input::get('fileKey'))) {
  639.             // Check key and return 403 if mismatch.
  640.             if (!(array_key_exists($file$_SESSION['metaModels_downloads'])
  641.                 && $_SESSION['metaModels_downloads'][$file] === $key)) {
  642.                 $objHandler = new $GLOBALS['TL_PTY']['error_403']();
  643.                 /** @var PageError403 $objHandler */
  644.                 $objHandler->generate($file);
  645.             }
  646.             // Send the file to the browser if check succeeded.
  647.             Controller::sendFileToBrowser($file);
  648.         }
  649.     }
  650.     /**
  651.      * Translate the file ID to file path.
  652.      *
  653.      * @param string $varValue The file id.
  654.      *
  655.      * @return string
  656.      */
  657.     public static function convertValueToPath($varValue)
  658.     {
  659.         if (empty($varValue)) {
  660.             return '';
  661.         }
  662.         $objFiles FilesModel::findByPk($varValue);
  663.         if ($objFiles !== null) {
  664.             return $objFiles->path;
  665.         }
  666.         return '';
  667.     }
  668.     /**
  669.      * Convert an array of values handled by MetaModels to a value to be stored in the database (array of bin uuid).
  670.      *
  671.      * The input array must have the following layout:
  672.      * array(
  673.      *   'bin'   => array() // list of the binary ids.
  674.      *   'value' => array() // list of the uuids.
  675.      *   'path'  => array() // list of the paths.
  676.      *   'meta'  => array() // list of the meta data.
  677.      * )
  678.      *
  679.      * @param array $values The values to convert.
  680.      *
  681.      * @return array
  682.      *
  683.      * @throws InvalidArgumentException When the input array is invalid.
  684.      */
  685.     public static function convertValuesToDatabase($values)
  686.     {
  687.         if (!(isset($values['bin']) && isset($values['value']) && isset($values['path']))) {
  688.             throw new InvalidArgumentException('Invalid file array');
  689.         }
  690.         $bin = array();
  691.         foreach ($values['bin'] as $value) {
  692.             $bin[] = $value;
  693.         }
  694.         return $bin;
  695.     }
  696.     /**
  697.      * Convert an array of values stored in the database (array of bin uuid) to a value to be handled by MetaModels.
  698.      *
  699.      * The output array will have the following layout:
  700.      * array(
  701.      *   'bin'   => array() // list of the binary ids.
  702.      *   'value' => array() // list of the uuids.
  703.      *   'path'  => array() // list of the paths.
  704.      *   'meta'  => array() // list of the meta data.
  705.      * )
  706.      *
  707.      * @param array $values The binary uuid values to convert.
  708.      *
  709.      * @return array
  710.      *
  711.      * @throws InvalidArgumentException When the input array is invalid.
  712.      */
  713.     public static function convertValuesToMetaModels($values)
  714.     {
  715.         if (!is_array($values)) {
  716.             throw new InvalidArgumentException('Invalid uuid list.');
  717.         }
  718.         // Convert UUIDs to binary and clean empty values out.
  719.         $values array_filter(array_map(function ($fileId) {
  720.             return Validator::isStringUuid($fileId) ? StringUtil::uuidToBin($fileId) : $fileId;
  721.         }, $values));
  722.         $result = array(
  723.             'bin'   => array(),
  724.             'value' => array(),
  725.             'path'  => array(),
  726.             'meta'  => array()
  727.         );
  728.         if (empty($values)) {
  729.             return $result;
  730.         }
  731.         $models FilesModel::findMultipleByUuids($values);
  732.         if ($models === null) {
  733.             return $result;
  734.         }
  735.         foreach ($models as $value) {
  736.             $result['bin'][]   = $value->uuid;
  737.             $result['value'][] = StringUtil::binToUuid($value->uuid);
  738.             $result['path'][]  = $value->path;
  739.             $result['meta'][]  = StringUtil::deserialize($value->metatrue);
  740.         }
  741.         return $result;
  742.     }
  743.     /**
  744.      * Convert an uuid or path to a value to be handled by MetaModels.
  745.      *
  746.      * The output array will have the following layout:
  747.      * array(
  748.      *   'bin'   => array() // list of the binary ids.
  749.      *   'value' => array() // list of the uuids.
  750.      *   'path'  => array() // list of the paths.
  751.      *   'meta'  => array() // list of the meta data.
  752.      * )
  753.      *
  754.      * @param array $values The binary uuids or paths to convert.
  755.      *
  756.      * @return array
  757.      *
  758.      * @throws InvalidArgumentException When any of the input is not a valid uuid or an non existent file.
  759.      */
  760.     public static function convertUuidsOrPathsToMetaModels($values)
  761.     {
  762.         $values array_filter((array) $values);
  763.         if (empty($values)) {
  764.             return array(
  765.                 'bin'   => array(),
  766.                 'value' => array(),
  767.                 'path'  => array(),
  768.                 'meta'  => array()
  769.             );
  770.         }
  771.         foreach ($values as $key => $value) {
  772.             if (!(Validator::isUuid($value))) {
  773.                 $file FilesModel::findByPath($value) ?: Dbafs::addResource($value);
  774.                 if (!$file) {
  775.                     throw new InvalidArgumentException('Invalid value.');
  776.                 }
  777.                 $values[$key] = $file->uuid;
  778.             }
  779.         }
  780.         return self::convertValuesToMetaModels($values);
  781.     }
  782.     /**
  783.      * Add the passed file model collection to the current buffer if the extension is allowed.
  784.      *
  785.      * Must either be called from within collectFiles or collectFiles must be called later on as this method
  786.      * will add models of type folder to the list of pending paths to allow for recursive inclusion.
  787.      *
  788.      * @param FilesModel[] $files     The files to add.
  789.      *
  790.      * @param array        $skipPaths List of directories not to be added to the list of pending directories.
  791.      *
  792.      * @return void
  793.      */
  794.     private function addFileModels($files$skipPaths = array())
  795.     {
  796.         $baseLanguage     $this->getBaseLanguage();
  797.         $fallbackLanguage $this->getFallbackLanguage();
  798.         foreach ($files as $file) {
  799.             if ('folder' === $file->type && !in_array($file->path$skipPaths)) {
  800.                 $this->pendingPaths[] = $file->path '/';
  801.                 continue;
  802.             }
  803.             if (is_file(TL_ROOT DIRECTORY_SEPARATOR $file->path) &&
  804.                 in_array(strtolower(pathinfo($file->pathPATHINFO_EXTENSION)), $this->acceptedExtensions)
  805.             ) {
  806.                 $path                       $file->path;
  807.                 $this->foundFiles[]         = $path;
  808.                 $this->uuidMap[$file->uuid] = $path;
  809.                 $meta                       StringUtil::deserialize($file->metatrue);
  810.                 if (isset($meta[$baseLanguage])) {
  811.                     $this->metaInformation[dirname($path)][basename($path)] = $meta[$baseLanguage];
  812.                 } elseif (isset($meta[$fallbackLanguage])) {
  813.                     $this->metaInformation[dirname($path)][basename($path)] = $meta[$fallbackLanguage];
  814.                 }
  815.             }
  816.         }
  817.     }
  818.     /**
  819.      * Process a single file.
  820.      *
  821.      * @param string $fileName The file to fetch data for.
  822.      *
  823.      * @return void
  824.      */
  825.     private function processFile($fileName)
  826.     {
  827.         $file  = new File($fileName);
  828.         $meta  $this->metaInformation[dirname($fileName)][$file->basename];
  829.         $title strlen($meta['title']) ? $meta['title'] : StringUtil::specialchars($file->basename);
  830.         if (strlen($meta['caption'])) {
  831.             $altText $meta['caption'];
  832.         } else {
  833.             $altText ucfirst(str_replace('_'' 'preg_replace('/^[0-9]+_/'''$file->filename)));
  834.         }
  835.         $information = [
  836.             'file'      => $fileName,
  837.             'mtime'     => $file->mtime,
  838.             'alt'       => $altText,
  839.             'caption'   => (!empty($meta['caption']) ? $meta['caption'] : ''),
  840.             'title'     => $title,
  841.             'metafile'  => $meta,
  842.             'icon'      => 'assets/contao/images/' $file->icon,
  843.             'extension' => $file->extension,
  844.             'size'      => $file->filesize,
  845.             'sizetext'  => sprintf('(%s)'Controller::getReadableSize($file->filesize2)),
  846.             'url'       => StringUtil::specialchars($this->getDownloadLink($fileName))
  847.         ];
  848.         // Prepare GD images.
  849.         if ($information['isGdImage'] = $file->isGdImage) {
  850.             $information['src'] = urldecode($this->resizeImage($fileName));
  851.             if (file_exists(TL_ROOT '/' $information['src'])) {
  852.                 $size              getimagesize(TL_ROOT '/' $information['src']);
  853.                 $information['lb'] = 'lb' $this->getLightboxId();
  854.                 $information['w']  = $size[0];
  855.                 $information['h']  = $size[1];
  856.                 $information['wh'] = $size[3];
  857.             }
  858.         }
  859.         // Prepare SVG images.
  860.         if ($information['isSvgImage'] = $file->isSvgImage) {
  861.             $information['src'] = $fileName;
  862.         }
  863.         // Prepare the picture for provide the image size.
  864.         if ($information['isPicture'] = (int) $this->resizeImages[2]) {
  865.             $picture Picture::create($file$this->getResizeImages())->getTemplateData();
  866.             $picture['alt']   = $altText;
  867.             $picture['title'] = $title;
  868.             $information['picture'] = $picture;
  869.         }
  870.         $this->modifiedTime[] = $file->mtime;
  871.         $this->outputBuffer[] = $information;
  872.     }
  873.     /**
  874.      * Resize the image if needed.
  875.      *
  876.      * @param string $fileName The file to resize.
  877.      *
  878.      * @return null|string
  879.      */
  880.     private function resizeImage($fileName)
  881.     {
  882.         list($width$height$mode) = $this->getResizeImages();
  883.         if ($this->getShowImages() && ($width || $height || $mode)) {
  884.             if ($this->imageFactory) {
  885.                 $image $this->imageFactory->create(
  886.                     $this->rootDir '/' $fileName,
  887.                     [$width$height$mode]
  888.                 );
  889.                 return $image->getUrl($this->rootDir);
  890.             }
  891.             $event = new ResizeImageEvent($fileName$width$height$mode);
  892.             $this->dispatcher->dispatch(ContaoEvents::IMAGE_RESIZE$event);
  893.             return $event->getResultImage();
  894.         }
  895.         return $fileName;
  896.     }
  897. }