/** * 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; }
public function setUp() { //nest config and injector for each test so they are effectively sandboxed per test Config::nest(); Injector::nest(); $this->originalReadingMode = Versioned::get_reading_mode(); // We cannot run the tests on this abstract class. if (get_class($this) == "SapphireTest") { $this->markTestSkipped(sprintf('Skipping %s ', get_class($this))); return; } // Mark test as being run $this->originalIsRunningTest = self::$is_running_test; self::$is_running_test = true; // i18n needs to be set to the defaults or tests fail i18n::set_locale(Config::inst()->get('i18n', 'default_locale')); i18n::config()->date_format = null; i18n::config()->time_format = null; // Set default timezone consistently to avoid NZ-specific dependencies date_default_timezone_set('UTC'); // Remove password validation $this->originalMemberPasswordValidator = Member::password_validator(); $this->originalRequirements = Requirements::backend(); Member::set_password_validator(null); Config::inst()->update('Cookie', 'report_errors', false); if (class_exists('SilverStripe\\CMS\\Controllers\\RootURLController')) { RootURLController::reset(); } if (class_exists('Translatable')) { Translatable::reset(); } Versioned::reset(); DataObject::reset(); if (class_exists('SilverStripe\\CMS\\Model\\SiteTree')) { SiteTree::reset(); } Hierarchy::reset(); if (Controller::has_curr()) { Controller::curr()->setSession(Injector::inst()->create('Session', array())); } Security::$database_is_ready = null; // Add controller-name auto-routing Config::inst()->update('Director', 'rules', array('$Controller//$Action/$ID/$OtherID' => '*')); $fixtureFile = static::get_fixture_file(); $prefix = defined('SS_DATABASE_PREFIX') ? SS_DATABASE_PREFIX : 'ss_'; // Todo: this could be a special test model $this->model = DataModel::inst(); // Set up fixture if ($fixtureFile || $this->usesDatabase) { if (!self::using_temp_db()) { self::create_temp_db(); } singleton('SilverStripe\\ORM\\DataObject')->flushCache(); self::empty_temp_db(); foreach ($this->requireDefaultRecordsFrom as $className) { $instance = singleton($className); if (method_exists($instance, 'requireDefaultRecords')) { $instance->requireDefaultRecords(); } if (method_exists($instance, 'augmentDefaultRecords')) { $instance->augmentDefaultRecords(); } } if ($fixtureFile) { $pathForClass = $this->getCurrentAbsolutePath(); $fixtureFiles = is_array($fixtureFile) ? $fixtureFile : array($fixtureFile); $i = 0; foreach ($fixtureFiles as $fixtureFilePath) { // Support fixture paths relative to the test class, rather than relative to webroot // String checking is faster than file_exists() calls. $isRelativeToFile = strpos('/', $fixtureFilePath) === false || preg_match('/^\\.\\./', $fixtureFilePath); if ($isRelativeToFile) { $resolvedPath = realpath($pathForClass . '/' . $fixtureFilePath); if ($resolvedPath) { $fixtureFilePath = $resolvedPath; } } $fixture = Injector::inst()->create('YamlFixture', $fixtureFilePath); $fixture->writeInto($this->getFixtureFactory()); $this->fixtures[] = $fixture; // backwards compatibility: Load first fixture into $this->fixture if ($i == 0) { $this->fixture = $fixture; } $i++; } } $this->logInWithPermission("ADMIN"); } // Preserve memory settings $this->originalMemoryLimit = ini_get('memory_limit'); // turn off template debugging Config::inst()->update('SSViewer', 'source_file_comments', false); // Clear requirements Requirements::clear(); // Set up email $this->originalMailer = Email::mailer(); $this->mailer = new TestMailer(); Injector::inst()->registerService($this->mailer, 'Mailer'); Config::inst()->remove('Email', 'send_all_emails_to'); }
public function setUp() { //nest config and injector for each test so they are effectively sandboxed per test Config::nest(); Injector::nest(); $this->originalReadingMode = Versioned::get_reading_mode(); // We cannot run the tests on this abstract class. if (get_class($this) == __CLASS__) { $this->markTestSkipped(sprintf('Skipping %s ', get_class($this))); return; } // Mark test as being run $this->originalIsRunningTest = self::$is_running_test; self::$is_running_test = true; // i18n needs to be set to the defaults or tests fail i18n::set_locale(i18n::config()->get('default_locale')); i18n::config()->date_format = null; i18n::config()->time_format = null; // Set default timezone consistently to avoid NZ-specific dependencies date_default_timezone_set('UTC'); // Remove password validation $this->originalMemberPasswordValidator = Member::password_validator(); $this->originalRequirements = Requirements::backend(); Member::set_password_validator(null); Cookie::config()->update('report_errors', false); if (class_exists('SilverStripe\\CMS\\Controllers\\RootURLController')) { RootURLController::reset(); } if (class_exists('Translatable')) { Translatable::reset(); } Versioned::reset(); DataObject::reset(); if (class_exists('SilverStripe\\CMS\\Model\\SiteTree')) { SiteTree::reset(); } Hierarchy::reset(); if (Controller::has_curr()) { Controller::curr()->setSession(Session::create(array())); } Security::$database_is_ready = null; // Add controller-name auto-routing // @todo Fix to work with namespaced controllers Director::config()->update('rules', array('$Controller//$Action/$ID/$OtherID' => '*')); $fixtureFiles = $this->getFixturePaths(); // Todo: this could be a special test model $this->model = DataModel::inst(); // Set up fixture if ($fixtureFiles || $this->usesDatabase) { if (!self::using_temp_db()) { self::create_temp_db(); } DataObject::singleton()->flushCache(); self::empty_temp_db(); foreach ($this->requireDefaultRecordsFrom as $className) { $instance = singleton($className); if (method_exists($instance, 'requireDefaultRecords')) { $instance->requireDefaultRecords(); } if (method_exists($instance, 'augmentDefaultRecords')) { $instance->augmentDefaultRecords(); } } foreach ($fixtureFiles as $fixtureFilePath) { $fixture = YamlFixture::create($fixtureFilePath); $fixture->writeInto($this->getFixtureFactory()); } $this->logInWithPermission("ADMIN"); } // Preserve memory settings $this->originalMemoryLimit = ini_get('memory_limit'); // turn off template debugging SSViewer::config()->update('source_file_comments', false); // Clear requirements Requirements::clear(); // Set up email $this->mailer = new TestMailer(); Injector::inst()->registerService($this->mailer, 'SilverStripe\\Control\\Email\\Mailer'); Email::config()->remove('send_all_emails_to'); }