Source of file CacheableSiteTree.php
Size: 12,761 Bytes - Last Modified: 2021-12-24T06:44:18+00:00
/var/www/docs.ssmods.com/process/src/code/view/CacheableSiteTree.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451 | <?php /** * * Object-cached representation of a SilverStripe {@link SiteTree} object. * * Warning: The API surface of a cacheable object differs from its SilverStripe * core counterpart. While developers have the opportunity to fine-tune what properties * and methods are cached via YML config (See README) do not rely on core methods * always being callable. * * @author Deviate Ltd 2014-2015 http://www.deviate.net.nz * @package silverstripe-cachable */ class CacheableSiteTree extends CacheableData { /** * * @var array */ private static $cacheable_fields = array( "MenuTitle", "URLSegment", "ParentID", "ClassName", "Sort", "ShowInMenus", "CanViewType", ); /** * * @var array */ private static $cacheable_functions = array( "Link", "ViewerGroups", "getSourceQueryParams", ); /** * * @var array */ private $Children = array(); /** * * @var CacheableSiteTree */ private $Parent; /** * * @var boolean */ private $_cached_is_section = null; /** * * @return SiteConfig */ public function getSiteConfig() { if ($this->hasMethod('alternateSiteConfig')) { $altConfig = $this->alternateSiteConfig(); if ($altConfig) { return $altConfig; } } if ($nav = Config::inst()->get('Cacheable', '_cached_navigation')) { $site_map = $nav->get_site_map(); $site_config = $nav->get_site_config(); if (isset($site_map[$this->ID]) && $site_config->exists()) { return $site_config; } } return SiteConfig::current_site_config(); } /** * * @param mixed string|int $key * @return mixed */ public function getCachedSourceQueryParam($key) { if (isset($this->getSourceQueryParams[$key])) { return $this->getSourceQueryParams[$key]; } return null; } /** * * @param Member $member * @return boolean */ public function canView($member = null) { if (!$member || !(is_a($member, 'Member')) || is_numeric($member)) { $member = Member::currentUserID(); } // admin override if ($member && Permission::checkMember($member, array("ADMIN", "SITETREE_VIEW_ALL"))) { return true; } // make sure we were loaded off an allowed stage // Were we definitely loaded directly off Live during our query? $fromLive = true; foreach (array('mode' => 'stage', 'stage' => 'live') as $param => $match) { $fromLive = $fromLive && strtolower((string) $this->getCachedSourceQueryParam("Versioned.$param")) == $match; } if (!$fromLive && !Session::get('unsecuredDraftSite') && !Permission::checkMember($member, array('CMS_ACCESS_LeftAndMain', 'CMS_ACCESS_CMSMain', 'VIEW_DRAFT_CONTENT'))) { // If we weren't definitely loaded from live, and we can't view non-live content, we need to // check to make sure this version is the live version and so can be viewed if (Versioned::get_versionnumber_by_stage($this->ClassName, 'Live', $this->ID) != $this->Version) { return false; } } // Orphaned pages (in the current stage) are unavailable, except for admins via the CMS if ($this->isOrphaned()) { return false; } // Standard mechanism for accepting permission changes from extensions $extended = $this->extendedCan('canView', $member); if ($extended !== null) { return $extended; } // check for empty spec if (!$this->CanViewType || $this->CanViewType == 'Anyone') { return true; } // check for inherit if ($this->CanViewType == 'Inherit') { if ($parent = $this->getParent()) { return $parent->canView($member); } else { return $this->getSiteConfig()->canView($member); } } // check for any logged-in users if ($this->CanViewType == 'LoggedInUsers' && $member) { return true; } // check for specific groups if ($member && is_numeric($member)) { $member = DataObject::get_by_id('Member', $member); } if ( $this->CanViewType == 'OnlyTheseUsers' && $member && $member->inGroups($this->ViewerGroups) ) { return true; } return false; } /** * * @param CacheableData $data * @return void */ public function addChild(CacheableData $data) { $this->Children[$data->ID] = $data; } /** * * "Removes" an item from the Children array, but by default not using PHP's * unset() function becuase this will cause PHP to reset its internal array * pointer and we need to maintain array state. Use the $force param to unset. * * @param int $childID * @param boolean $force Invoke PHP's unset() function on the selected item. * @return void */ public function removeChild($childID, $force = true) { if (isset($this->Children[$childID])) { if ($force === true) { unset($this->Children[$childID]); } else { $this->Children[$childID] = CacheableSiteTree::create(); // dummy } } } /** * * @param CacheableData $data */ public function setParent(CacheableData $data) { $this->Parent = $data; } /** * * @return CacheableData */ public function getParent() { return $this->Parent; } /** * * @return array */ public function getAllChildren() { return $this->Children; } /** * * Get all child nodes of the current node and apply an optional filter. The * default is to return all children using ShowInMenus=1 if no/dummy/bad filter * is passed. * * @param boolean $showInMenusFilter Whether or not to apply the ShowInMenus filter * @param array $filter * @return ArrayList */ public function getChildren($showInMenusFilter = true, $filter = array()) { $children = new ArrayList($this->Children); // If $showInMenusFilter is true, _always_ apply 'ShowInMenus' => 1 if ($showInMenusFilter === true) { $filter = array('ShowInMenus' => 1); } if ($filter) { $children = $children->filter($filter); } $visible = array(); foreach ($children as $child) { if ($child->canView()) { $visible[] = $child; } } return new ArrayList($visible); } /** * Return "link" or "current" depending on if this is the {@link SiteTree::isCurrent()} current page. * * @return string */ public function LinkOrCurrent() { return $this->isCurrent() ? 'current' : 'link'; } /** * Return "link" or "section" depending on if this is the {@link SiteTree::isSeciton()} current section. * * @return string */ public function LinkOrSection() { return $this->isSection() ? 'section' : 'link'; } /** * * @return string */ public function LinkingMode() { if ($this->isCurrent()) { return 'current'; } elseif ($this->isSection()) { return 'section'; } else { return 'link'; } } /** * * @return boolean */ public function isSection() { $isSection = false; if ($this->_cached_is_section === null) { if ($this->isCurrent()) { $isSection = true; } else { if ($navigation = $this->CachedNavigation()) { $currentPage = Director::get_current_page(); $ancestors = $navigation->getAncestores($currentPage->ID); $isSection = $currentPage instanceof SiteTree && in_array($this->ID, $ancestors->column()); } } $this->_cached_is_section = $isSection; } return $this->_cached_is_section; } /** * * @return boolean */ public function isCurrent() { return $this->ID ? $this->ID == Director::get_current_page()->ID : $this === Director::get_current_page(); } /** * * @return boolean */ public function isOrphaned() { // Always false for root pages if (empty($this->ParentID)) { return false; } $parent = $this->getParent(); return !$parent || !$parent->exists() || $parent->isOrphaned(); } /** * * @return string */ public function debug_simple() { $message = "<h5>cacheable data: " . get_class($this) . "</h5><ul>"; $message .= "<il>ID: " . $this->ID . ". Title: " . $this->Title . ". ClassName: " . $this->ClassName . ". Link: ".$this->Link.".</il>"; $parent = $this->getParent(); if ($parent && $parent->exists()) { $message .= "<il>Parent ID: " . $parent->ID . ". Title: " . $parent->Title . ". ClassName" . $parent->ClassName . "</il>"; } $message .= "</ul>"; return $message; } /** * * @param int $level * @return ArrayList */ public function Menu($level = 1) { if ($nav = $this->CachedNavigation()) { if ($level == 1) { $root_elements = new ArrayList($nav->get_root_elements()); $result = $root_elements->filter(array( "ShowInMenus" => 1 )); } else { $dataID = Director::get_current_page()->ID; $site_map = $nav->get_site_map(); if (isset($site_map[$dataID])) { $parent = $site_map[$dataID]; $stack = array($parent); if ($parent) { while ($parent = $parent->getParent()) { array_unshift($stack, $parent); } } if (isset($stack[$level - 2])) { $elements = new ArrayList($stack[$level - 2]->getAllChildren()); $result = $elements->filter( array( "ShowInMenus" => 1, ) ); } } } $visible = array(); // Remove all entries the can not be viewed by the current user // We might need to create a show in menu permission if (isset($result)) { foreach ($result as $page) { if ($page->canView()) { $visible[] = $page; } } } return new ArrayList($visible); } } /** * * @return CachedNavigation */ public function CachedNavigation() { if ($cachedNavigiation = Config::inst()->get('Cacheable', '_cached_navigation')) { if ($cachedNavigiation->isUnlocked() && $cachedNavigiation->get_completed()) { return $cachedNavigiation; } } } /** * * Return a MenuTitle depending on if we have the Fluent module enabled, and the * current selected locale. * * It is also obviously contingent on each of the MenuTitle_<locale> fields * having been configured correctly. * * This method obviously _only_ deals with the MenuTitle_<locale> field. It is * therefore not at all DRY in the sense that there is no answer to the following * question: What do we do for other properties-as-fields, configured and * translated via Fluent? * * @return string */ public function MenuTitle() { if ($this->hasFluent()) { $localeCurrent = Fluent::current_locale(); $fieldCurrent = 'MenuTitle_' . $localeCurrent; if (isset($this->$fieldCurrent) && $title = $this->$fieldCurrent) { return $title; } } return $this->MenuTitle; } } |