/**
* Recursively render the filetree
*
* @param string $path
* @param integer $intMargin
* @param boolean $mount
* @param boolean $blnProtected
* @param array $arrFound
*
* @return string
*/
protected function renderFiletree($path, $intMargin, $mount = false, $blnProtected = true, $arrFound = array())
{
// Invalid path
if (!is_dir($path)) {
return '';
}
// Make sure that $this->varValue is an array (see #3369)
if (!is_array($this->varValue)) {
$this->varValue = array($this->varValue);
}
static $session;
/** @var AttributeBagInterface $objSessionBag */
$objSessionBag = \System::getContainer()->get('session')->getBag('contao_backend');
$session = $objSessionBag->all();
$flag = substr($this->strField, 0, 2);
$node = 'tree_' . $this->strTable . '_' . $this->strField;
$xtnode = 'tree_' . $this->strTable . '_' . $this->strName;
// Get session data and toggle nodes
if (\Input::get($flag . 'tg')) {
$session[$node][\Input::get($flag . 'tg')] = isset($session[$node][\Input::get($flag . 'tg')]) && $session[$node][\Input::get($flag . 'tg')] == 1 ? 0 : 1;
$objSessionBag->replace($session);
$this->redirect(preg_replace('/(&(amp;)?|\\?)' . $flag . 'tg=[^& ]*/i', '', \Environment::get('request')));
}
$return = '';
$intSpacing = 20;
$files = array();
$folders = array();
$level = $intMargin / $intSpacing + 1;
// Mount folder
if ($mount) {
$folders = array($path);
} else {
foreach (scan($path) as $v) {
if (strncmp($v, '.', 1) === 0) {
continue;
}
if (is_dir($path . '/' . $v)) {
$folders[] = $path . '/' . $v;
} else {
$files[] = $path . '/' . $v;
}
}
}
natcasesort($folders);
$folders = array_values($folders);
natcasesort($files);
$files = array_values($files);
// Sort descending (see #4072)
if ($this->sort == 'desc') {
$folders = array_reverse($folders);
$files = array_reverse($files);
}
$folderClass = $this->files || $this->filesOnly ? 'tl_folder' : 'tl_file';
// Process folders
for ($f = 0, $c = count($folders); $f < $c; $f++) {
$content = scan($folders[$f]);
$currentFolder = str_replace(TL_ROOT . '/', '', $folders[$f]);
$countFiles = count($content);
// Check whether there are subfolders or files
foreach ($content as $file) {
if (strncmp($file, '.', 1) === 0) {
--$countFiles;
} elseif (!$this->files && !$this->filesOnly && is_file($folders[$f] . '/' . $file)) {
--$countFiles;
} elseif (!empty($arrFound) && !in_array($currentFolder . '/' . $file, $arrFound) && !preg_grep('/^' . preg_quote($currentFolder . '/' . $file, '/') . '\\//', $arrFound)) {
--$countFiles;
}
}
if (!empty($arrFound) && $countFiles < 1 && !in_array($currentFolder, $arrFound)) {
continue;
}
$tid = md5($folders[$f]);
$folderAttribute = 'style="margin-left:20px"';
$session[$node][$tid] = is_numeric($session[$node][$tid]) ? $session[$node][$tid] : 0;
$currentFolder = str_replace(TL_ROOT . '/', '', $folders[$f]);
$blnIsOpen = !empty($arrFound) || $session[$node][$tid] == 1 || count(preg_grep('/^' . preg_quote($currentFolder, '/') . '\\//', $this->varValue)) > 0;
$return .= "\n " . '<li class="' . $folderClass . ' toggle_select hover-div"><div class="tl_left" style="padding-left:' . $intMargin . 'px">';
// Add a toggle button if there are childs
if ($countFiles > 0) {
$folderAttribute = '';
$img = $blnIsOpen ? 'folMinus.svg' : 'folPlus.svg';
$alt = $blnIsOpen ? $GLOBALS['TL_LANG']['MSC']['collapseNode'] : $GLOBALS['TL_LANG']['MSC']['expandNode'];
$return .= '<a href="' . \Backend::addToUrl($flag . 'tg=' . $tid) . '" title="' . \StringUtil::specialchars($alt) . '" onclick="return AjaxRequest.toggleFiletree(this,\'' . $xtnode . '_' . $tid . '\',\'' . $currentFolder . '\',\'' . $this->strField . '\',\'' . $this->strName . '\',' . $level . ')">' . \Image::getHtml($img, '', 'style="margin-right:2px"') . '</a>';
}
$protected = $blnProtected;
// Check whether the folder is public
if ($protected === true && array_search('.public', $content) !== false) {
$protected = false;
}
$folderImg = $protected ? 'folderCP.svg' : 'folderC.svg';
$folderLabel = $this->files || $this->filesOnly ? '<strong>' . \StringUtil::specialchars(basename($currentFolder)) . '</strong>' : \StringUtil::specialchars(basename($currentFolder));
// Add the current folder
$return .= \Image::getHtml($folderImg, '', $folderAttribute) . ' <a href="' . \Backend::addToUrl('fn=' . $this->urlEncode($currentFolder)) . '" title="' . \StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['selectNode']) . '">' . $folderLabel . '</a></div> <div class="tl_right">';
// Add a checkbox or radio button
if (!$this->filesOnly) {
switch ($this->fieldType) {
case 'checkbox':
$return .= '<input type="checkbox" name="' . $this->strName . '[]" id="' . $this->strName . '_' . md5($currentFolder) . '" class="tl_tree_checkbox" value="' . \StringUtil::specialchars($currentFolder) . '" onfocus="Backend.getScrollOffset()"' . $this->optionChecked($currentFolder, $this->varValue) . '>';
break;
case 'radio':
$return .= '<input type="radio" name="' . $this->strName . '" id="' . $this->strName . '_' . md5($currentFolder) . '" class="tl_tree_radio" value="' . \StringUtil::specialchars($currentFolder) . '" onfocus="Backend.getScrollOffset()"' . $this->optionChecked($currentFolder, $this->varValue) . '>';
break;
}
}
$return .= '</div><div style="clear:both"></div></li>';
// Call the next node
if ($blnIsOpen) {
$return .= '<li class="parent" id="' . $xtnode . '_' . $tid . '"><ul class="level_' . $level . '">';
$return .= $this->renderFiletree($folders[$f], $intMargin + $intSpacing, false, $protected, $arrFound);
$return .= '</ul></li>';
}
}
// Process files
if ($this->files || $this->filesOnly) {
for ($h = 0, $c = count($files); $h < $c; $h++) {
$thumbnail = '';
$currentFile = str_replace(TL_ROOT . '/', '', $files[$h]);
$currentEncoded = $this->urlEncode($currentFile);
$objFile = new \File($currentFile);
if (!empty($this->arrValidFileTypes) && !in_array($objFile->extension, $this->arrValidFileTypes)) {
continue;
}
// Ignore files not matching the search criteria
if (!empty($arrFound) && !in_array($currentFile, $arrFound)) {
continue;
}
$return .= "\n " . '<li class="tl_file toggle_select hover-div"><div class="tl_left" style="padding-left:' . ($intMargin + $intSpacing) . 'px">';
$thumbnail .= ' <span class="tl_gray">(' . $this->getReadableSize($objFile->filesize);
if ($objFile->width && $objFile->height) {
$thumbnail .= ', ' . $objFile->width . 'x' . $objFile->height . ' px';
}
$thumbnail .= ')</span>';
// Generate thumbnail
if ($objFile->isImage && $objFile->viewHeight > 0 && \Config::get('thumbnails') && ($objFile->isSvgImage || $objFile->height <= \Config::get('gdMaxImgHeight') && $objFile->width <= \Config::get('gdMaxImgWidth'))) {
$imageObj = \Image::create($currentEncoded, array(400, $objFile->height && $objFile->height < 50 ? $objFile->height : 50, 'box'));
$importantPart = $imageObj->getImportantPart();
$thumbnail .= '<br>' . \Image::getHtml($imageObj->executeResize()->getResizedPath(), '', 'style="margin:0 0 2px -19px"');
if ($importantPart['x'] > 0 || $importantPart['y'] > 0 || $importantPart['width'] < $objFile->width || $importantPart['height'] < $objFile->height) {
$thumbnail .= ' ' . \Image::getHtml($imageObj->setZoomLevel(100)->setTargetWidth(320)->setTargetHeight($objFile->height && $objFile->height < 40 ? $objFile->height : 40)->executeResize()->getResizedPath(), '', 'style="margin:0 0 2px 0;vertical-align:bottom"');
}
}
$return .= \Image::getHtml($objFile->icon, $objFile->mime) . ' ' . \StringUtil::convertEncoding(\StringUtil::specialchars(basename($currentFile)), \Config::get('characterSet')) . $thumbnail . '</div> <div class="tl_right">';
// Add checkbox or radio button
switch ($this->fieldType) {
case 'checkbox':
$return .= '<input type="checkbox" name="' . $this->strName . '[]" id="' . $this->strName . '_' . md5($currentFile) . '" class="tl_tree_checkbox" value="' . \StringUtil::specialchars($currentFile) . '" onfocus="Backend.getScrollOffset()"' . $this->optionChecked($currentFile, $this->varValue) . '>';
break;
case 'radio':
$return .= '<input type="radio" name="' . $this->strName . '" id="' . $this->strName . '_' . md5($currentFile) . '" class="tl_tree_radio" value="' . \StringUtil::specialchars($currentFile) . '" onfocus="Backend.getScrollOffset()"' . $this->optionChecked($currentFile, $this->varValue) . '>';
break;
}
$return .= '</div><div style="clear:both"></div></li>';
}
}
return $return;
}