/**
     * Get a site tree HTML listing which displays the nodes under the given criteria.
     *
     * @param string $className The class of the root object
     * @param string $rootID The ID of the root object.  If this is null then a complete tree will be
     *  shown
     * @param string $childrenMethod The method to call to get the children of the tree. For example,
     *  Children, AllChildrenIncludingDeleted, or AllHistoricalChildren
     * @param string $numChildrenMethod
     * @param callable $filterFunction
     * @param int $nodeCountThreshold
     * @return string Nested unordered list with links to each page
     */
    public function getSiteTreeFor($className, $rootID = null, $childrenMethod = null, $numChildrenMethod = null, $filterFunction = null, $nodeCountThreshold = 30)
    {
        // Filter criteria
        $filter = $this->getSearchFilter();
        // Default childrenMethod and numChildrenMethod
        if (!$childrenMethod) {
            $childrenMethod = $filter && $filter->getChildrenMethod() ? $filter->getChildrenMethod() : 'AllChildrenIncludingDeleted';
        }
        if (!$numChildrenMethod) {
            $numChildrenMethod = 'numChildren';
            if ($filter && $filter->getNumChildrenMethod()) {
                $numChildrenMethod = $filter->getNumChildrenMethod();
            }
        }
        if (!$filterFunction && $filter) {
            $filterFunction = function ($node) use($filter) {
                return $filter->isPageIncluded($node);
            };
        }
        // Get the tree root
        $record = $rootID ? $this->getRecord($rootID) : null;
        $obj = $record ? $record : singleton($className);
        // Get the current page
        // NOTE: This *must* be fetched before markPartialTree() is called, as this
        // causes the Hierarchy::$marked cache to be flushed (@see CMSMain::getRecord)
        // which means that deleted pages stored in the marked tree would be removed
        $currentPage = $this->currentPage();
        // Mark the nodes of the tree to return
        if ($filterFunction) {
            $obj->setMarkingFilterFunction($filterFunction);
        }
        $obj->markPartialTree($nodeCountThreshold, $this, $childrenMethod, $numChildrenMethod);
        // Ensure current page is exposed
        if ($currentPage) {
            $obj->markToExpose($currentPage);
        }
        // NOTE: SiteTree/CMSMain coupling :-(
        if (class_exists('SilverStripe\\CMS\\Model\\SiteTree')) {
            SiteTree::prepopulate_permission_cache('CanEditType', $obj->markedNodeIDs(), 'SilverStripe\\CMS\\Model\\SiteTree::can_edit_multiple');
        }
        // getChildrenAsUL is a flexible and complex way of traversing the tree
        $controller = $this;
        $recordController = $this->stat('tree_class') == 'SilverStripe\\CMS\\Model\\SiteTree' ? CMSPageEditController::singleton() : $this;
        $titleFn = function (&$child, $numChildrenMethod) use(&$controller, &$recordController, $filter) {
            $link = Controller::join_links($recordController->Link("show"), $child->ID);
            $node = LeftAndMain_TreeNode::create($child, $link, $controller->isCurrentPage($child), $numChildrenMethod, $filter);
            return $node->forTemplate();
        };
        // Limit the amount of nodes shown for performance reasons.
        // Skip the check if we're filtering the tree, since its not clear how many children will
        // match the filter criteria until they're queried (and matched up with previously marked nodes).
        $nodeThresholdLeaf = Config::inst()->get(Hierarchy::class, 'node_threshold_leaf');
        if ($nodeThresholdLeaf && !$filterFunction) {
            $nodeCountCallback = function ($parent, $numChildren) use(&$controller, $className, $nodeThresholdLeaf) {
                if ($className !== 'SilverStripe\\CMS\\Model\\SiteTree' || !$parent->ID || $numChildren >= $nodeThresholdLeaf) {
                    return null;
                }
                return sprintf('<ul><li class="readonly"><span class="item">' . '%s (<a href="%s" class="cms-panel-link" data-pjax-target="Content">%s</a>)' . '</span></li></ul>', _t('LeftAndMain.TooManyPages', 'Too many pages'), Controller::join_links($controller->LinkWithSearch($controller->Link()), '
							?view=list&ParentID=' . $parent->ID), _t('LeftAndMain.ShowAsList', 'show as list', 'Show large amount of pages in list instead of tree view'));
            };
        } else {
            $nodeCountCallback = null;
        }
        // If the amount of pages exceeds the node thresholds set, use the callback
        $html = null;
        if ($obj->ParentID && $nodeCountCallback) {
            $html = $nodeCountCallback($obj, $obj->{$numChildrenMethod}());
        }
        // Otherwise return the actual tree (which might still filter leaf thresholds on children)
        if (!$html) {
            $html = $obj->getChildrenAsUL("", $titleFn, CMSPagesController::singleton(), true, $childrenMethod, $numChildrenMethod, $nodeCountThreshold, $nodeCountCallback);
        }
        // Wrap the root if needs be.
        if (!$rootID) {
            $rootLink = $this->Link('show') . '/root';
            // This lets us override the tree title with an extension
            if ($this->hasMethod('getCMSTreeTitle') && ($customTreeTitle = $this->getCMSTreeTitle())) {
                $treeTitle = $customTreeTitle;
            } elseif (class_exists('SilverStripe\\SiteConfig\\SiteConfig')) {
                $siteConfig = SiteConfig::current_site_config();
                $treeTitle = Convert::raw2xml($siteConfig->Title);
            } else {
                $treeTitle = '...';
            }
            $html = "<ul><li id=\"record-0\" data-id=\"0\" class=\"Root nodelete\"><strong>{$treeTitle}</strong>" . $html . "</li></ul>";
        }
        return $html;
    }