/**
  * Get a site tree HTML listing which displays the nodes under the given criteria.
  * 
  * @param $className The class of the root object
  * @param $rootID The ID of the root object.  If this is null then a complete tree will be
  *  shown
  * @param $childrenMethod The method to call to get the children of the tree. For example,
  *  Children, AllChildrenIncludingDeleted, or AllHistoricalChildren
  * @return String Nested unordered list with links to each page
  */
 function getSiteTreeFor($className, $rootID = null, $childrenMethod = null, $numChildrenMethod = null, $filterFunction = null, $minNodeCount = 30)
 {
     // Filter criteria
     $params = $this->request->getVar('q');
     if (isset($params['FilterClass']) && ($filterClass = $params['FilterClass'])) {
         if (!is_subclass_of($filterClass, 'CMSSiteTreeFilter')) {
             throw new Exception(sprintf('Invalid filter class passed: %s', $filterClass));
         }
         $filter = new $filterClass($params);
     } else {
         $filter = null;
     }
     // Default childrenMethod and numChildrenMethod
     if (!$childrenMethod) {
         $childrenMethod = $filter && $filter->getChildrenMethod() ? $filter->getChildrenMethod() : 'AllChildrenIncludingDeleted';
     }
     if (!$numChildrenMethod) {
         $numChildrenMethod = 'numChildren';
     }
     if (!$filterFunction) {
         $filterFunction = $filter ? array($filter, 'isPageIncluded') : null;
     }
     // Get the tree root
     $record = $rootID ? $this->getRecord($rootID) : null;
     $obj = $record ? $record : singleton($className);
     // Mark the nodes of the tree to return
     if ($filterFunction) {
         $obj->setMarkingFilterFunction($filterFunction);
     }
     $obj->markPartialTree($minNodeCount, $this, $childrenMethod, $numChildrenMethod);
     // Ensure current page is exposed
     if ($p = $this->currentPage()) {
         $obj->markToExpose($p);
     }
     // NOTE: SiteTree/CMSMain coupling :-(
     if (class_exists('SiteTree')) {
         SiteTree::prepopulate_permission_cache('CanEditType', $obj->markedNodeIDs(), 'SiteTree::can_edit_multiple');
     }
     // getChildrenAsUL is a flexible and complex way of traversing the tree
     $controller = $this;
     $recordController = $this->stat('tree_class') == 'SiteTree' ? singleton('CMSPageEditController') : $this;
     $titleFn = function (&$child) use(&$controller, &$recordController) {
         $link = Controller::join_links($recordController->Link("show"), $child->ID);
         return LeftAndMain_TreeNode::create($child, $link, $controller->isCurrentPage($child))->forTemplate();
     };
     $html = $obj->getChildrenAsUL("", $titleFn, singleton('CMSPagesController'), true, $childrenMethod, $numChildrenMethod, $minNodeCount);
     // 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('SiteConfig')) {
             $siteConfig = SiteConfig::current_site_config();
             $treeTitle = $siteConfig->Title;
         } else {
             $treeTitle = '...';
         }
         $html = "<ul><li id=\"record-0\" data-id=\"0\" class=\"Root nodelete\"><strong>{$treeTitle}</strong>" . $html . "</li></ul>";
     }
     return $html;
 }
    /**
     * Get a site tree HTML listing which displays the nodes under the given criteria.
     * 
     * @param $className The class of the root object
     * @param $rootID The ID of the root object.  If this is null then a complete tree will be
     *  shown
     * @param $childrenMethod The method to call to get the children of the tree. For example,
     *  Children, AllChildrenIncludingDeleted, or AllHistoricalChildren
     * @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
        $params = $this->request->getVar('q');
        if (isset($params['FilterClass']) && ($filterClass = $params['FilterClass'])) {
            if (!is_subclass_of($filterClass, 'CMSSiteTreeFilter')) {
                throw new Exception(sprintf('Invalid filter class passed: %s', $filterClass));
            }
            $filter = new $filterClass($params);
        } else {
            $filter = null;
        }
        // Default childrenMethod and numChildrenMethod
        if (!$childrenMethod) {
            $childrenMethod = $filter && $filter->getChildrenMethod() ? $filter->getChildrenMethod() : 'AllChildrenIncludingDeleted';
        }
        if (!$numChildrenMethod) {
            $numChildrenMethod = 'numChildren';
        }
        if (!$filterFunction) {
            $filterFunction = $filter ? array($filter, 'isPageIncluded') : null;
        }
        // Get the tree root
        $record = $rootID ? $this->getRecord($rootID) : null;
        $obj = $record ? $record : singleton($className);
        // 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 ($p = $this->currentPage()) {
            $obj->markToExpose($p);
        }
        // NOTE: SiteTree/CMSMain coupling :-(
        if (class_exists('SiteTree')) {
            SiteTree::prepopulate_permission_cache('CanEditType', $obj->markedNodeIDs(), 'SiteTree::can_edit_multiple');
        }
        // getChildrenAsUL is a flexible and complex way of traversing the tree
        $controller = $this;
        $recordController = $this->stat('tree_class') == 'SiteTree' ? singleton('CMSPageEditController') : $this;
        $titleFn = function (&$child) use(&$controller, &$recordController) {
            $link = Controller::join_links($recordController->Link("show"), $child->ID);
            return LeftAndMain_TreeNode::create($child, $link, $controller->isCurrentPage($child))->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', 'node_threshold_leaf');
        if ($nodeThresholdLeaf && !$filterFunction) {
            $nodeCountCallback = function ($parent, $numChildren) use(&$controller, $className, $nodeThresholdLeaf) {
                if ($className == 'SiteTree' && $parent->ID && $numChildren > $nodeThresholdLeaf) {
                    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, singleton('CMSPagesController'), 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('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;
    }