Contao\DC_Table::generateTree PHP Method

generateTree() protected method

Recursively generate the tree and return it as HTML string
protected generateTree ( string $table, integer $id, array $arrPrevNext, boolean $blnHasSorting, integer $intMargin, array $arrClipboard = null, boolean $blnCircularReference = false, boolean $protectedPage = false, boolean $blnNoRecursion = false, array $arrFound = [] ) : string
$table string
$id integer
$arrPrevNext array
$blnHasSorting boolean
$intMargin integer
$arrClipboard array
$blnCircularReference boolean
$protectedPage boolean
$blnNoRecursion boolean
$arrFound array
return string
    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'] . '&amp;mode=1&amp;pid=' . $id . (!is_array($arrClipboard['id']) ? '&amp;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'] . '&amp;mode=2&amp;pid=' . $id . (!is_array($arrClipboard['id']) ? '&amp;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'] . '&amp;mode=1&amp;pid=' . $id . (!is_array($arrClipboard['id']) ? '&amp;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'] . '&amp;mode=2&amp;pid=' . $id . (!is_array($arrClipboard['id']) ? '&amp;id=' . $arrClipboard['id'] : '')) . '" title="' . \StringUtil::specialchars(sprintf($GLOBALS['TL_LANG'][$this->strTable]['pasteinto'][1], $id)) . '" onclick="Backend.getScrollOffset()">' . $imagePasteInto . '</a> ' : '';
                }
            }
        }
        $return .= ($_buttons ?: '&nbsp;') . '</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;
    }