protected function generateTree($table, $id, $arrPrevNext, $blnHasSorting, $intMargin = 0, $arrClipboard = null, $blnCircularReference = false, $protectedPage = false, $blnNoRecursion = false, $arrFound = array())
{
static $session;
/** @var AttributeBagInterface $objSessionBag */
$objSessionBag = \System::getContainer()->get('session')->getBag('contao_backend');
$session = $objSessionBag->all();
$node = $GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['mode'] == 6 ? $this->strTable . '_' . $table . '_tree' : $this->strTable . '_tree';
// Toggle nodes
if (\Input::get('ptg')) {
$session[$node][\Input::get('ptg')] = isset($session[$node][\Input::get('ptg')]) && $session[$node][\Input::get('ptg')] == 1 ? 0 : 1;
$objSessionBag->replace($session);
$this->redirect(preg_replace('/(&(amp;)?|\\?)ptg=[^& ]*/i', '', \Environment::get('request')));
}
$objRow = $this->Database->prepare("SELECT * FROM " . $table . " WHERE id=?")->limit(1)->execute($id);
// Return if there is no result
if ($objRow->numRows < 1) {
$objSessionBag->replace($session);
return '';
}
$return = '';
$intSpacing = 20;
$childs = array();
// Add the ID to the list of current IDs
if ($this->strTable == $table) {
$this->current[] = $objRow->id;
}
// Check whether there are child records
if (!$blnNoRecursion) {
if ($GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['mode'] == 5 || $this->strTable != $table) {
$objChilds = $this->Database->prepare("SELECT id FROM " . $table . " WHERE pid=?" . (!empty($arrFound) ? " AND id IN(" . implode(',', array_map('intval', $arrFound)) . ")" : '') . ($blnHasSorting ? " ORDER BY sorting" : ''))->execute($id);
if ($objChilds->numRows) {
$childs = $objChilds->fetchEach('id');
}
}
}
$blnProtected = false;
// Check whether the page is protected
if ($table == 'tl_page') {
$blnProtected = $objRow->protected || $protectedPage ? true : false;
}
$session[$node][$id] = is_int($session[$node][$id]) ? $session[$node][$id] : 0;
$mouseover = $GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['mode'] == 5 || $table == $this->strTable ? ' toggle_select hover-div' : '';
$return .= "\n " . '<li class="' . ($GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['mode'] == 5 && $objRow->type == 'root' || $table != $this->strTable ? 'tl_folder' : 'tl_file') . ' click2edit' . $mouseover . ' cf"><div class="tl_left" style="padding-left:' . ($intMargin + $intSpacing) . 'px">';
// Calculate label and add a toggle button
$args = array();
$folderAttribute = 'style="margin-left:20px"';
$showFields = $GLOBALS['TL_DCA'][$table]['list']['label']['fields'];
$level = $intMargin / $intSpacing + 1;
if (!empty($childs)) {
$folderAttribute = '';
$img = !empty($arrFound) || $session[$node][$id] == 1 ? 'folMinus.svg' : 'folPlus.svg';
$alt = !empty($arrFound) || $session[$node][$id] == 1 ? $GLOBALS['TL_LANG']['MSC']['collapseNode'] : $GLOBALS['TL_LANG']['MSC']['expandNode'];
$return .= '<a href="' . $this->addToUrl('ptg=' . $id) . '" title="' . \StringUtil::specialchars($alt) . '" onclick="Backend.getScrollOffset();return AjaxRequest.toggleStructure(this,\'' . $node . '_' . $id . '\',' . $level . ',' . $GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['mode'] . ')">' . \Image::getHtml($img, '', 'style="margin-right:2px"') . '</a>';
}
foreach ($showFields as $k => $v) {
// Decrypt the value
if ($GLOBALS['TL_DCA'][$table]['fields'][$v]['eval']['encrypt']) {
$objRow->{$v} = \Encryption::decrypt(\StringUtil::deserialize($objRow->{$v}));
}
if (strpos($v, ':') !== false) {
list($strKey, $strTable) = explode(':', $v);
list($strTable, $strField) = explode('.', $strTable);
$objRef = $this->Database->prepare("SELECT " . $strField . " FROM " . $strTable . " WHERE id=?")->limit(1)->execute($objRow->{$strKey});
$args[$k] = $objRef->numRows ? $objRef->{$strField} : '';
} elseif (in_array($GLOBALS['TL_DCA'][$table]['fields'][$v]['flag'], array(5, 6, 7, 8, 9, 10))) {
$args[$k] = \Date::parse(\Config::get('datimFormat'), $objRow->{$v});
} elseif ($GLOBALS['TL_DCA'][$table]['fields'][$v]['inputType'] == 'checkbox' && !$GLOBALS['TL_DCA'][$table]['fields'][$v]['eval']['multiple']) {
$args[$k] = $objRow->{$v} != '' ? isset($GLOBALS['TL_DCA'][$table]['fields'][$v]['label'][0]) ? $GLOBALS['TL_DCA'][$table]['fields'][$v]['label'][0] : $v : '';
} else {
$args[$k] = $GLOBALS['TL_DCA'][$table]['fields'][$v]['reference'][$objRow->{$v}] ?: $objRow->{$v};
}
}
$label = vsprintf(strlen($GLOBALS['TL_DCA'][$table]['list']['label']['format']) ? $GLOBALS['TL_DCA'][$table]['list']['label']['format'] : '%s', $args);
// Shorten the label if it is too long
if ($GLOBALS['TL_DCA'][$table]['list']['label']['maxCharacters'] > 0 && $GLOBALS['TL_DCA'][$table]['list']['label']['maxCharacters'] < Utf8::strlen(strip_tags($label))) {
$label = trim(\StringUtil::substrHtml($label, $GLOBALS['TL_DCA'][$table]['list']['label']['maxCharacters'])) . ' …';
}
$label = preg_replace('/\\(\\) ?|\\[\\] ?|\\{\\} ?|<> ?/', '', $label);
// Call the label_callback ($row, $label, $this)
if (is_array($GLOBALS['TL_DCA'][$table]['list']['label']['label_callback'])) {
$strClass = $GLOBALS['TL_DCA'][$table]['list']['label']['label_callback'][0];
$strMethod = $GLOBALS['TL_DCA'][$table]['list']['label']['label_callback'][1];
$this->import($strClass);
$return .= $this->{$strClass}->{$strMethod}($objRow->row(), $label, $this, $folderAttribute, false, $blnProtected);
} elseif (is_callable($GLOBALS['TL_DCA'][$table]['list']['label']['label_callback'])) {
$return .= $GLOBALS['TL_DCA'][$table]['list']['label']['label_callback']($objRow->row(), $label, $this, $folderAttribute, false, $blnProtected);
} else {
$return .= \Image::getHtml('iconPLAIN.svg', '') . ' ' . $label;
}
$return .= '</div> <div class="tl_right">';
$previous = $GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['mode'] == 6 ? $arrPrevNext['pp'] : $arrPrevNext['p'];
$next = $GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['mode'] == 6 ? $arrPrevNext['nn'] : $arrPrevNext['n'];
$_buttons = '';
// Regular buttons ($row, $table, $root, $blnCircularReference, $childs, $previous, $next)
if ($this->strTable == $table) {
$_buttons .= \Input::get('act') == 'select' ? '<input type="checkbox" name="IDS[]" id="ids_' . $id . '" class="tl_tree_checkbox" value="' . $id . '">' : $this->generateButtons($objRow->row(), $table, $this->root, $blnCircularReference, $childs, $previous, $next);
}
// Paste buttons
if ($arrClipboard !== false && \Input::get('act') != 'select') {
$_buttons .= ' ';
// Call paste_button_callback(&$dc, $row, $table, $blnCircularReference, $arrClipboard, $childs, $previous, $next)
if (is_array($GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['paste_button_callback'])) {
$strClass = $GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['paste_button_callback'][0];
$strMethod = $GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['paste_button_callback'][1];
$this->import($strClass);
$_buttons .= $this->{$strClass}->{$strMethod}($this, $objRow->row(), $table, $blnCircularReference, $arrClipboard, $childs, $previous, $next);
} elseif (is_callable($GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['paste_button_callback'])) {
$_buttons .= $GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['paste_button_callback']($this, $objRow->row(), $table, $blnCircularReference, $arrClipboard, $childs, $previous, $next);
} else {
$imagePasteAfter = \Image::getHtml('pasteafter.svg', sprintf($GLOBALS['TL_LANG'][$this->strTable]['pasteafter'][1], $id));
$imagePasteInto = \Image::getHtml('pasteinto.svg', sprintf($GLOBALS['TL_LANG'][$this->strTable]['pasteinto'][1], $id));
// Regular tree (on cut: disable buttons of the page all its childs to avoid circular references)
if ($GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['mode'] == 5) {
$_buttons .= $arrClipboard['mode'] == 'cut' && ($blnCircularReference || $arrClipboard['id'] == $id) || $arrClipboard['mode'] == 'cutAll' && ($blnCircularReference || in_array($id, $arrClipboard['id'])) || !empty($GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['root']) && !$GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['rootPaste'] && in_array($id, $this->root) ? \Image::getHtml('pasteafter_.svg') . ' ' : '<a href="' . $this->addToUrl('act=' . $arrClipboard['mode'] . '&mode=1&pid=' . $id . (!is_array($arrClipboard['id']) ? '&id=' . $arrClipboard['id'] : '')) . '" title="' . \StringUtil::specialchars(sprintf($GLOBALS['TL_LANG'][$this->strTable]['pasteafter'][1], $id)) . '" onclick="Backend.getScrollOffset()">' . $imagePasteAfter . '</a> ';
$_buttons .= $arrClipboard['mode'] == 'cut' && ($blnCircularReference || $arrClipboard['id'] == $id) || $arrClipboard['mode'] == 'cutAll' && ($blnCircularReference || in_array($id, $arrClipboard['id'])) ? \Image::getHtml('pasteinto_.svg') . ' ' : '<a href="' . $this->addToUrl('act=' . $arrClipboard['mode'] . '&mode=2&pid=' . $id . (!is_array($arrClipboard['id']) ? '&id=' . $arrClipboard['id'] : '')) . '" title="' . \StringUtil::specialchars(sprintf($GLOBALS['TL_LANG'][$this->strTable]['pasteinto'][1], $id)) . '" onclick="Backend.getScrollOffset()">' . $imagePasteInto . '</a> ';
} else {
$_buttons .= $this->strTable == $table ? $arrClipboard['mode'] == 'cut' && ($blnCircularReference || $arrClipboard['id'] == $id) || $arrClipboard['mode'] == 'cutAll' && ($blnCircularReference || in_array($id, $arrClipboard['id'])) ? \Image::getHtml('pasteafter_.svg') : '<a href="' . $this->addToUrl('act=' . $arrClipboard['mode'] . '&mode=1&pid=' . $id . (!is_array($arrClipboard['id']) ? '&id=' . $arrClipboard['id'] : '')) . '" title="' . \StringUtil::specialchars(sprintf($GLOBALS['TL_LANG'][$this->strTable]['pasteafter'][1], $id)) . '" onclick="Backend.getScrollOffset()">' . $imagePasteAfter . '</a> ' : '';
$_buttons .= $this->strTable != $table ? '<a href="' . $this->addToUrl('act=' . $arrClipboard['mode'] . '&mode=2&pid=' . $id . (!is_array($arrClipboard['id']) ? '&id=' . $arrClipboard['id'] : '')) . '" title="' . \StringUtil::specialchars(sprintf($GLOBALS['TL_LANG'][$this->strTable]['pasteinto'][1], $id)) . '" onclick="Backend.getScrollOffset()">' . $imagePasteInto . '</a> ' : '';
}
}
}
$return .= ($_buttons ?: ' ') . '</div></li>';
// Add the records of the table itself
if ($table != $this->strTable) {
$objChilds = $this->Database->prepare("SELECT id FROM " . $this->strTable . " WHERE pid=?" . ($blnHasSorting ? " ORDER BY sorting" : ''))->execute($id);
if ($objChilds->numRows) {
$ids = $objChilds->fetchEach('id');
for ($j = 0, $c = count($ids); $j < $c; $j++) {
$return .= $this->generateTree($this->strTable, $ids[$j], array('pp' => $ids[$j - 1], 'nn' => $ids[$j + 1]), $blnHasSorting, $intMargin + $intSpacing + 20, $arrClipboard, false, $j < count($ids) - 1 || !empty($childs), $blnNoRecursion, $arrFound);
}
}
}
// Begin a new submenu
if (!$blnNoRecursion) {
if (!empty($arrFound) || !empty($childs) && $session[$node][$id] == 1) {
$return .= '<li class="parent" id="' . $node . '_' . $id . '"><ul class="level_' . $level . '">';
}
// Add the records of the parent table
if (!empty($arrFound) || $session[$node][$id] == 1) {
if (is_array($childs)) {
for ($k = 0, $c = count($childs); $k < $c; $k++) {
$return .= $this->generateTree($table, $childs[$k], array('p' => $childs[$k - 1], 'n' => $childs[$k + 1]), $blnHasSorting, $intMargin + $intSpacing, $arrClipboard, $GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['mode'] == 5 && $childs[$k] == $arrClipboard['id'] || $blnCircularReference ? true : false, $blnProtected || $protectedPage, $blnNoRecursion, $arrFound);
}
}
}
// Close the submenu
if (!empty($arrFound) || !empty($childs) && $session[$node][$id] == 1) {
$return .= '</ul></li>';
}
}
$objSessionBag->replace($session);
return $return;
}