core-bundle/src/Resources/contao/widgets/FileSelector.php
public function generate()
	{
		$this->import(BackendUser::class, 'User');
		$this->convertValuesToPaths();

		if ($this->extensions != '')
		{
			$this->arrValidFileTypes = StringUtil::trimsplit(',', strtolower($this->extensions));
		}

		/** @var AttributeBagInterface $objSessionBag */
		$objSessionBag = System::getContainer()->get('session')->getBag('contao_backend');

		// Store the keyword
		if (Input::post('FORM_SUBMIT') == 'item_selector')
		{
			$strKeyword = ltrim(Input::postRaw('keyword'), '*');

			// Make sure the regular expression is valid
			if ($strKeyword != '')
			{
				try
				{
					$this->Database->prepare("SELECT * FROM tl_files WHERE name REGEXP ?")
								   ->limit(1)
								   ->execute($strKeyword);
				}
				catch (\Exception $e)
				{
					$strKeyword = '';
				}
			}

			$objSessionBag->set('file_selector_search', $strKeyword);
			$this->reload();
		}

		$tree = '';
		$for = $objSessionBag->get('file_selector_search');
		$arrFound = array();

		// Search for a specific file
		if ($for != '')
		{
			// Wrap in a try catch block in case the regular expression is invalid (see #7743)
			try
			{
				$strPattern = "CAST(name AS CHAR) REGEXP ?";

				if (substr(Config::get('dbCollation'), -3) == '_ci')
				{
					$strPattern = "LOWER(CAST(name AS CHAR)) REGEXP LOWER(?)";
				}

				$strType = '';

				if (strpos($for, 'type:file') !== false)
				{
					$strType = " AND type='file'";
					$for = trim(str_replace('type:file', '', $for));
				}

				if (strpos($for, 'type:folder') !== false)
				{
					$strType = " AND type='folder'";
					$for = trim(str_replace('type:folder', '', $for));
				}

				$objRoot = $this->Database->prepare("SELECT path, type, extension FROM tl_files WHERE $strPattern $strType")
										  ->execute($for);

				if ($objRoot->numRows < 1)
				{
					$GLOBALS['TL_DCA']['tl_files']['list']['sorting']['root'] = array('');
				}
				else
				{
					$arrPaths = array();

					// Respect existing limitations
					if ($this->path != '')
					{
						while ($objRoot->next())
						{
							if (strncmp($this->path . '/', $objRoot->path . '/', \strlen($this->path) + 1) === 0)
							{
								if ($objRoot->type == 'folder' || empty($this->arrValidFileTypes) || \in_array($objRoot->extension, $this->arrValidFileTypes))
								{
									$arrFound[] = $objRoot->path;
								}

								$arrPaths[] = ($objRoot->type == 'folder') ? $objRoot->path : \dirname($objRoot->path);
							}
						}
					}
					elseif ($this->User->isAdmin)
					{
						// Show all files to admins
						while ($objRoot->next())
						{
							if ($objRoot->type == 'folder' || empty($this->arrValidFileTypes) || \in_array($objRoot->extension, $this->arrValidFileTypes))
							{
								$arrFound[] = $objRoot->path;
							}

							$arrPaths[] = ($objRoot->type == 'folder') ? $objRoot->path : \dirname($objRoot->path);
						}
					}
					else
					{
						if (\is_array($this->User->filemounts))
						{
							while ($objRoot->next())
							{
								// Show only mounted folders to regular users
								foreach ($this->User->filemounts as $path)
								{
									if (strncmp($path . '/', $objRoot->path . '/', \strlen($path) + 1) === 0)
									{
										if ($objRoot->type == 'folder' || empty($this->arrValidFileTypes) || \in_array($objRoot->extension, $this->arrValidFileTypes))
										{
											$arrFound[] = $objRoot->path;
										}

										$arrPaths[] = ($objRoot->type == 'folder') ? $objRoot->path : \dirname($objRoot->path);
									}
								}
							}
						}
					}

					$GLOBALS['TL_DCA']['tl_files']['list']['sorting']['root'] = array_unique($arrPaths);
				}
			}
			catch (\Exception $e) {}
		}

		$strNode = $objSessionBag->get('tl_files_picker');

		// Unset the node if it is not within the path (see #5899)
		if ($strNode != '' && $this->path != '')
		{
			if (strncmp($strNode . '/', $this->path . '/', \strlen($this->path) + 1) !== 0)
			{
				$objSessionBag->remove('tl_files_picker');
			}
		}

		// Add the breadcrumb menu
		if (Input::get('do') != 'files')
		{
			Backend::addFilesBreadcrumb('tl_files_picker');
		}

		$rootDir = System::getContainer()->getParameter('kernel.project_dir');

		// Root nodes (breadcrumb menu)
		if (!empty($GLOBALS['TL_DCA']['tl_files']['list']['sorting']['root']))
		{
			$root = $GLOBALS['TL_DCA']['tl_files']['list']['sorting']['root'];

			// Allow only those roots that are within the custom path
			if ($this->path != '')
			{
				$root = array_intersect(preg_grep('/^' . preg_quote($this->path, '/') . '(?:$|\/)/', $root), $root);

				if (empty($root))
				{
					// Set all folders inside the custom path as root nodes
					$root = array_map(function ($el) { return $this->path . '/' . $el; }, scan($rootDir . '/' . $this->path));

					// Hide the breadcrumb
					$GLOBALS['TL_DCA']['tl_file']['list']['sorting']['breadcrumb'] = '';
				}
			}

			$nodes = $this->eliminateNestedPaths($root);

			foreach ($nodes as $node)
			{
				$tree .= $this->renderFiletree($rootDir . '/' . $node, 0, true, true, $arrFound);
			}
		}

		// Show a custom path (see #4926)
		elseif ($this->path != '')
		{
			$tree .= $this->renderFiletree($rootDir . '/' . $this->path, 0, false, $this->isProtectedPath($this->path), $arrFound);
		}

		// Start from root
		elseif ($this->User->isAdmin)
		{
			$tree .= $this->renderFiletree($rootDir . '/' . Config::get('uploadPath'), 0, false, true, $arrFound);
		}

		// Show mounted files to regular users
		else
		{
			$nodes = $this->eliminateNestedPaths($this->User->filemounts);

			foreach ($nodes as $node)
			{
				$tree .= $this->renderFiletree($rootDir . '/' . $node, 0, true, true, $arrFound);
			}
		}

		// Select all checkboxes
		if ($this->fieldType == 'checkbox')
		{
			$strReset = "\n" . '    <li class="tl_folder"><div class="tl_left">&nbsp;</div> <div class="tl_right"><label for="check_all_' . $this->strId . '" class="tl_change_selected">' . $GLOBALS['TL_LANG']['MSC']['selectAll'] . '</label> <input type="checkbox" id="check_all_' . $this->strId . '" class="tl_tree_checkbox" value="" onclick="Backend.toggleCheckboxGroup(this,\'' . $this->strName . '\')"></div><div style="clear:both"></div></li>';
		}
		// Reset radio button selection
		else
		{
			$strReset = "\n" . '    <li class="tl_folder"><div class="tl_left">&nbsp;</div> <div class="tl_right"><label for="reset_' . $this->strId . '" class="tl_change_selected">' . $GLOBALS['TL_LANG']['MSC']['resetSelected'] . '</label> <input type="radio" name="' . $this->strName . '" id="reset_' . $this->strName . '" class="tl_tree_radio" value="" onfocus="Backend.getScrollOffset()"></div><div style="clear:both"></div></li>';
		}

		// Return the tree
		return '<ul class="tl_listing tree_view picker_selector'.(($this->strClass != '') ? ' ' . $this->strClass : '').'" id="'.$this->strId.'" data-callback="reloadFiletree">
    <li class="tl_folder_top"><div class="tl_left">'.Image::getHtml($GLOBALS['TL_DCA']['tl_files']['list']['sorting']['icon'] ?: 'filemounts.svg').' '.$GLOBALS['TL_LANG']['MOD']['files'][0].'</div> <div class="tl_right">&nbsp;</div><div style="clear:both"></div></li><li class="parent" id="'.$this->strId.'_parent"><ul>'.$tree.$strReset.'
  </ul></li></ul>';
	}
public function generate()
	{
		$this->import(BackendUser::class, 'User');
		$this->convertValuesToPaths();

		if ($this->extensions != '')
		{
			$this->arrValidFileTypes = StringUtil::trimsplit(',', strtolower($this->extensions));
		}

		/** @var AttributeBagInterface $objSessionBag */
		$objSessionBag = System::getContainer()->get('session')->getBag('contao_backend');

		// Store the keyword
		if (Input::post('FORM_SUBMIT') == 'item_selector')
		{
			$strKeyword = ltrim(Input::postRaw('keyword'), '*');

			// Make sure the regular expression is valid
			if ($strKeyword != '')
			{
				try
				{
					$this->Database->prepare("SELECT * FROM tl_files WHERE name REGEXP ?")
								   ->limit(1)
								   ->execute($strKeyword);
				}
				catch (\Exception $e)
				{
					$strKeyword = '';
				}
			}

			$objSessionBag->set('file_selector_search', $strKeyword);
			$this->reload();
		}

		$tree = '';
		$for = $objSessionBag->get('file_selector_search');
		$arrFound = array();

		// Search for a specific file
		if ($for != '')
		{
			// Wrap in a try catch block in case the regular expression is invalid (see #7743)
			try
			{
				$strPattern = "CAST(name AS CHAR) REGEXP ?";

				if (substr(Config::get('dbCollation'), -3) == '_ci')
				{
					$strPattern = "LOWER(CAST(name AS CHAR)) REGEXP LOWER(?)";
				}

				$strType = '';

				if (strpos($for, 'type:file') !== false)
				{
					$strType = " AND type='file'";
					$for = trim(str_replace('type:file', '', $for));
				}

				if (strpos($for, 'type:folder') !== false)
				{
					$strType = " AND type='folder'";
					$for = trim(str_replace('type:folder', '', $for));
				}

				$objRoot = $this->Database->prepare("SELECT path, type, extension FROM tl_files WHERE $strPattern $strType")
										  ->execute($for);

				if ($objRoot->numRows < 1)
				{
					$GLOBALS['TL_DCA']['tl_files']['list']['sorting']['root'] = array('');
				}
				else
				{
					$arrPaths = array();

					// Respect existing limitations
					if ($this->path != '')
					{
						while ($objRoot->next())
						{
							if (strncmp($this->path . '/', $objRoot->path . '/', \strlen($this->path) + 1) === 0)
							{
								if ($objRoot->type == 'folder' || empty($this->arrValidFileTypes) || \in_array($objRoot->extension, $this->arrValidFileTypes))
								{
									$arrFound[] = $objRoot->path;
								}

								$arrPaths[] = ($objRoot->type == 'folder') ? $objRoot->path : \dirname($objRoot->path);
							}
						}
					}
					elseif ($this->User->isAdmin)
					{
						// Show all files to admins
						while ($objRoot->next())
						{
							if ($objRoot->type == 'folder' || empty($this->arrValidFileTypes) || \in_array($objRoot->extension, $this->arrValidFileTypes))
							{
								$arrFound[] = $objRoot->path;
							}

							$arrPaths[] = ($objRoot->type == 'folder') ? $objRoot->path : \dirname($objRoot->path);
						}
					}
					else
					{
						if (\is_array($this->User->filemounts))
						{
							while ($objRoot->next())
							{
								// Show only mounted folders to regular users
								foreach ($this->User->filemounts as $path)
								{
									if (strncmp($path . '/', $objRoot->path . '/', \strlen($path) + 1) === 0)
									{
										if ($objRoot->type == 'folder' || empty($this->arrValidFileTypes) || \in_array($objRoot->extension, $this->arrValidFileTypes))
										{
											$arrFound[] = $objRoot->path;
										}

										$arrPaths[] = ($objRoot->type == 'folder') ? $objRoot->path : \dirname($objRoot->path);
									}
								}
							}
						}
					}

					$GLOBALS['TL_DCA']['tl_files']['list']['sorting']['root'] = array_unique($arrPaths);
				}
			}
			catch (\Exception $e) {}
		}

		$strNode = $objSessionBag->get('tl_files_picker');

		// Unset the node if it is not within the path (see #5899)
		if ($strNode != '' && $this->path != '')
		{
			if (strncmp($strNode . '/', $this->path . '/', \strlen($this->path) + 1) !== 0)
			{
				$objSessionBag->remove('tl_files_picker');
			}
		}

		// Add the breadcrumb menu
		if (Input::get('do') != 'files')
		{
			Backend::addFilesBreadcrumb('tl_files_picker');
		}

		$rootDir = System::getContainer()->getParameter('kernel.project_dir');

		// Root nodes (breadcrumb menu)
		if (!empty($GLOBALS['TL_DCA']['tl_files']['list']['sorting']['root']))
		{
			$root = $GLOBALS['TL_DCA']['tl_files']['list']['sorting']['root'];

			// Allow only those roots that are within the custom path
			if ($this->path != '')
			{
				$root = array_intersect(preg_grep('/^' . preg_quote($this->path, '/') . '(?:$|\/)/', $root), $root);

				if (empty($root))
				{
					// Set all folders inside the custom path as root nodes
					$root = array_map(function ($el) { return $this->path . '/' . $el; }, scan($rootDir . '/' . $this->path));

					// Hide the breadcrumb
					$GLOBALS['TL_DCA']['tl_file']['list']['sorting']['breadcrumb'] = '';
				}
			}

			$nodes = $this->eliminateNestedPaths($root);

			foreach ($nodes as $node)
			{
				$tree .= $this->renderFiletree($rootDir . '/' . $node, 0, true, true, $arrFound);
			}
		}

		// Show a custom path (see #4926)
		elseif ($this->path != '')
		{
			$tree .= $this->renderFiletree($rootDir . '/' . $this->path, 0, false, $this->isProtectedPath($this->path), $arrFound);
		}

		// Start from root
		elseif ($this->User->isAdmin)
		{
			$tree .= $this->renderFiletree($rootDir . '/' . Config::get('uploadPath'), 0, false, true, $arrFound);
		}

		// Show mounted files to regular users
		else
		{
			$nodes = $this->eliminateNestedPaths($this->User->filemounts);

			foreach ($nodes as $node)
			{
				$tree .= $this->renderFiletree($rootDir . '/' . $node, 0, true, true, $arrFound);
			}
		}

		// Select all checkboxes
		if ($this->fieldType == 'checkbox')
		{
			$strReset = "\n" . '    <li class="tl_folder"><div class="tl_left">&nbsp;</div> <div class="tl_right"><label for="check_all_' . $this->strId . '" class="tl_change_selected">' . $GLOBALS['TL_LANG']['MSC']['selectAll'] . '</label> <input type="checkbox" id="check_all_' . $this->strId . '" class="tl_tree_checkbox" value="" onclick="Backend.toggleCheckboxGroup(this,\'' . $this->strName . '\')"></div><div style="clear:both"></div></li>';
		}
		// Reset radio button selection
		else
		{
			$strReset = "\n" . '    <li class="tl_folder"><div class="tl_left">&nbsp;</div> <div class="tl_right"><label for="reset_' . $this->strId . '" class="tl_change_selected">' . $GLOBALS['TL_LANG']['MSC']['resetSelected'] . '</label> <input type="radio" name="' . $this->strName . '" id="reset_' . $this->strName . '" class="tl_tree_radio" value="" onfocus="Backend.getScrollOffset()"></div><div style="clear:both"></div></li>';
		}

		// Return the tree
		return '<ul class="tl_listing tree_view picker_selector'.(($this->strClass != '') ? ' ' . $this->strClass : '').'" id="'.$this->strId.'" data-callback="reloadFiletree">
    <li class="tl_folder_top"><div class="tl_left">'.Image::getHtml($GLOBALS['TL_DCA']['tl_files']['list']['sorting']['icon'] ?: 'filemounts.svg').' '.$GLOBALS['TL_LANG']['MOD']['files'][0].'</div> <div class="tl_right">&nbsp;</div><div style="clear:both"></div></li><li class="parent" id="'.$this->strId.'_parent"><ul>'.$tree.$strReset.'
  </ul></li></ul>';
	}
Generate the widget and return it as string

@return string