URLSegment . "/$action"; } /** * Returns link/current, depending on whether you're on the current page. * This is useful for css styling of menus. * @return string Either 'link' or 'current'. */ public function LinkOrCurrent() { return $this->isCurrent() ? "current" : "link"; } /** * Returns link/section, depending on whether you're on the current section. * This is useful for css styling of menus. * @return string Either 'link' or 'section'. */ public function LinkOrSection() { return $this->isSection() ? "section" : "link"; } /** * Returns link/current/section, depending if you're not in the current section, * you're on the current page, or you're in the current section * but not on the current page. * @return string Either 'link', 'current' or 'section'. */ public function LinkingMode() { $this->prepareCurrentAndSection(); if($this->ID == self::$currentPageID) { return "current"; } else if(in_array($this->ID, self::$currentSectionIDs)) { return "section"; } else { return "link"; } } /** * Get the URL segment for this page, eg 'home' * @return string The URL segment */ public function ElementName() { return $this->URLSegment; } /** * Check if this page is in the given current section. * @param string $sectionName Name of the section to check. * @return boolean True if we are in the given section. */ public function InSection($sectionName) { $page = Director::currentPage(); while($page) { if($sectionName == $page->URLSegment) return true; $page = $page->Parent; } return false; } /** * Returns comments on this page. This will only show comments that * have been marked as spam if "?showspam=1" is appended to the URL. * @return DataObjectSet Comments on this page. */ public function Comments() { $spamfilter = isset($_GET['showspam']) ? '' : 'AND IsSpam=0'; $unmoderatedfilter = Permission::check('ADMIN') ? '' : 'AND NeedsModeration = 0'; $comments = DataObject::get("PageComment", "ParentID = '" . Convert::raw2sql($this->ID) . "' $spamfilter $unmoderatedfilter", "Created DESC"); return $comments ? $comments : new DataObjectSet(); } /** * Create a duplicate of this node. Doesn't affect joined data - create a custom overloading of this * if you need such behaviour. * @return SiteTree The duplicated object. */ public function duplicate($doWrite = true) { $page = parent::duplicate($doWrite); $page->CheckedPublicationDifferences = $page->AddedToStage = true; return $page; } /** * Duplicates each child of this node recursively and returns the duplicate node. * @return SiteTree The duplicated object. */ public function duplicateWithChildren() { $clone = $this->duplicate(); $children = $this->AllChildren(); if($children) { foreach($children as $child) { $childClone = method_exists( $child, 'duplicateWithChildren' ) ? $child->duplicateWithChildren() : $child->duplicate(); $childClone->ParentID = $clone->ID; $childClone->write(); } } return $clone; } /** * Duplicate this node and its children as a child of the node with the given id * @param int $id ID of the new node's new parent */ public function duplicateAsChild( $id ) { $newSiteTree = $this->duplicate(); $newSiteTree->ParentID = $id; $newSiteTree->write(); } /** * An array of this pages URL segment and it's parents. * This is generated by prepareCurrentAndSection for use by * isCurrent() and isSection() * @var array */ protected static $currentSectionIDs; /** * The current page ID. * This is generated by prepareCurrentAndSection for use by * isCurrent() and isSection() * @var int */ protected static $currentPageID; /** * This function is used for isCurrent() and isSection() to prepare * the cached answers. */ protected function prepareCurrentAndSection() { if(!self::$currentPageID) { self::$currentPageID = Director::currentPage() ? Director::currentPage()->ID : null; if(!isset(self::$currentPageID)) { self::$currentPageID = -1; $nextID = isset(Director::currentPage()->Parent->ID) ? Director::currentPage()->Parent->ID : null; } else { $nextID = SiteTree::$currentPageID; } $table = (Versioned::current_stage() == "Live") ? "SiteTree_Live" : "SiteTree"; SiteTree::$currentSectionIDs = array(); while($nextID) { self::$currentSectionIDs[] = $nextID; $nextID = DB::query("SELECT ParentID FROM SiteTree WHERE ID = $nextID")->value(); } } } /** * Check if this is the currently viewed page. * @return boolean True if this is the current page. */ public function isCurrent() { $this->prepareCurrentAndSection(); return $this->ID == SiteTree::$currentPageID; } /** * Check if the currently viewed page is in this section. * @return boolean True if the currently viewed page is in this section. */ public function isSection() { $this->prepareCurrentAndSection(); return in_array($this->ID, self::$currentSectionIDs); } /** * Return a breadcrumb trail to this page. * @param int $maxDepth The maximum depth to traverse. * @param boolean $unlinked Do not make page names links * @param string $stopAtPageType ClassName of a page to stop the upwards traversal. * @return string The breadcrumb trail. */ public function Breadcrumbs($maxDepth = 20, $unlinked = false, $stopAtPageType = false) { $page = $this; $parts = array(); $i = 0; while(($page && (sizeof($parts) < $maxDepth)) || ($stopAtPageType && $page->ClassName != $stopAtPageType)) { if($page->ShowInMenus || ($page->ID == $this->ID)) { if($page->URLSegment == 'home') { $hasHome = true; } $parts[] = (($page->ID == $this->ID) || $unlinked) ? Convert::raw2xml($page->Title) : ("Link() . "\">" . Convert::raw2xml($page->Title) . ""); } $page = $page->Parent; } return implode(" » ", array_reverse($parts)); } /** * Get the parent of this page. * @return SiteTree Parent of this page. */ public function getParent() { if($this->getField("ParentID")) return DataObject::get_one("SiteTree", "`SiteTree`.ID = " . $this->getField("ParentID")); } /** * Make this page a child of another page. * @param SiteTree|int $item Either the parent object, or the parent id */ public function setParent($item) { if(is_object($item)) { $this->setField("ParentID", $item->ID); } else { $this->setField("ParentID", $item); } } /** * Return a string of the form "parent - page" or "grandparent - parent - page". * @param int $level The maximum amount of levels to traverse. * @param string $seperator Seperating string * @return string The resulting string */ function NestedTitle($level = 2, $separator = " - ") { $item = $this; while($item && $level > 0) { $parts[] = $item->Title; $item = $item->Parent; $level--; } return implode($separator, array_reverse($parts)); } /** * Returns true if the member is allowed to do the given action. * * @param string $perm The permission to be checked, such as 'View'. * @param Member $member The member whose permissions need checking. * Defaults to the currently logged in user. * * @return boolean True if the the member is allowed to do the given * action. * * @todo Check we get a endless recursion if we use parent::can() */ function can($perm, $member = null) { if(!isset($member)) { $member = Member::currentUser(); } // Users with ADMIN permission can always do this if($member && Permission::check('ADMIN')) { return true; } switch(strtolower($perm)) { case 'edit': if($this->Editors == 'LoggedInUsers' && $member) return true; if($member && $this->Editors == 'OnlyTheseUsers' && $member->isInGroup($this->EditorsGroup)) return true; break; case 'view': case 'view_page': if($this->Viewers == 'Anyone') return true; if($member && $this->Viewers == 'LoggedInUsers') return true; if($member && $this->Viewers == 'OnlyTheseUsers' && $member->isInGroup($this->ViewersGroup)) return true; break; } return false; } /** * This function should return true if the current user can add children * to this page. * * It can be overloaded to customise the security model for an * application. * * @return boolean True if the current user can add children. */ public function canAddChildren() { return $this->canEdit() && $this->stat('allowed_children') != 'none'; } /** * This function should return true if the current user can delete this page. * It can be overloaded to customise the security model for an application. * @return boolean True if the current user can delete this page. */ public function canDelete() { return $this->stat('can_create') != false; } /** * This function should return true if the current user can create new pages of this class. * It can be overloaded to customise the security model for an application. * @return boolean True if the current user can create pages on this class. */ public function canCreate() { return $this->stat('can_create') != false || Director::isDev(); } /** * This function should return true if the current user can edit this page. * It can be overloaded to customise the security model for an application. * @return boolean True if the current user can edit this page. */ public function canEdit() { return $this->can('Edit'); } /** * This function should return true if the current user can publish this page. * It can be overloaded to customise the security model for an application. * @return boolean True if the current user can publish this page. */ public function canPublish() { return $this->canEdit(); } /** * Collate selected descendants of this page. * $condition will be evaluated on each descendant, and if it is succeeds, that item will be added * to the $collator array. * @param string $condition The PHP condition to be evaluated. The page will be called $item * @param array $collator An array, passed by reference, to collect all of the matching descendants. */ public function collateDescendants($condition, &$collator) { if($children = $this->Children()) { foreach($children as $item) { if(eval("return $condition;")) $collator[] = $item; $item->collateDescendants($condition, $collator); } return true; } } /** * Return the title, description and keywords metatags. * @param boolean|string $includeTitle Show default
Welcome to SilverStripe! This is the default homepage. You can edit this page by opening the CMS.
"; $homepage->URLSegment = "home"; $homepage->Status = "Published"; $homepage->write(); $homepage->publish("Stage", "Live"); $homepage->flushCache(); if(!Database::$supressOutput) { echo "You can fill this page out with your own content, or delete it and create your own pages.
You can fill this page out with your own content, or delete it and create your own pages.
done"; } /** * Replace a URL in html content with a new URL. * @param string $old The old URL * @param string $new The new URL */ function rewriteLink($old, $new) { $fields = $this->getCMSFields(null)->dataFields(); foreach($fields as $field) { if(is_a($field, 'HtmlEditorField')) { $fieldName = $field->Name(); $field->setValue($this->$fieldName); $field->rewriteLink($old, $new); $field->saveInto($this); } } } //------------------------------------------------------------------------------------// /** * Holds callback functions to be called when getCMSFields() is called * * @var array */ static $cms_additions = array(); /** * Allows modules to extend the cms editing form for all pages in the site * * @param mixed $function the name of your function, either as a string, * or in the form array('class','function) */ static function ExtendCMS($function) { self::$cms_additions[] = $function; } /** * Returns a FieldSet with which to create the CMS editing form. * You can override this in your child classes to add extra fields - first * get the parent fields using parent::getCMSFields(), then use addFieldToTab() * on the FieldSet. * @return FieldSet The fields to be displayed in the CMS. */ function getCMSFields() { require_once("forms/Form.php"); Requirements::javascript("cms/javascript/SitetreeAccess.js"); // Backlink report if($this->hasMethod('BackLinkTracking')) { $links = $this->BackLinkTracking(); if($links->exists()) { foreach($links as $link) { $backlinks[] = "
This page hasn't been linked to from any pages.
"; } // Status / message // Create a status message for multiple parents if($this->ID && is_numeric($this->ID)) { $linkedPages = DataObject::get("VirtualPage", "CopyContentFromID = $this->ID"); } if(isset($linkedPages)) { foreach($linkedPages as $linkedPage) { $parentPage = $linkedPage->Parent; $parentPageTitle = $parentPage->Title; if($parentPage->ID) { $parentPageLinks[] = "ID\">{$parentPage->Title}"; } else { $parentPageLinks[] = "ID\">Site Content (Top Level)"; } } $lastParent = array_pop($parentPageLinks); $parentList = "'$lastParent'"; if(count( $parentPageLinks ) > 0) { $parentList = "'" . implode("', '", $parentPageLinks) . "' and " . $parentList; } $statusMessage[] = "This content also appears on the virtual pages in the $parentList sections."; } if($this->HasBrokenLink || $this->HasBrokenFile) { $statusMessage[] = "This page has broken links."; } $message = "STATUS: $this->StatusUse this page as the 'home page' for the following domains: (separate multiple domains with commas)
"), new TextField("HomepageForDomain", "Domain(s)") ), new TabSet("Reports", new Tab("BackLinks", new LiteralField("Backlinks", $backlinks) ) ), new Tab("Access", new HeaderField("Who can display this?", 2), new OptionsetField("Viewers", "", array("Anyone" => "Anyone", "LoggedInUsers" => "Logged-in users", "OnlyTheseUsers" => "Only these people (choose from list)")), new DropdownField("ViewersGroup", "Group", Group::map()), new HeaderField("Who can edit this?", 2), new OptionsetField("Editors", "", array("LoggedInUsers" => "Logged-in users", "OnlyTheseUsers" => "Only these people (choose from list)")), new DropdownField("EditorsGroup", "Group", Group::map()) ) ), new NamedLabelField("Status", $message, "pageStatusMessage", true) ); foreach(self::$cms_additions as $extension) { $fields = call_user_func($extension,$fields); } $this->extend('updateCMSFields', $fields); return $fields; } /** * Get the actions available in the CMS for this page - eg Save, Publish. * @return DataObjectSet The available actions for this page. */ function getCMSActions() { $actions = array(); if($this->isPublished() && $this->canPublish()) { $actions[] = FormAction::create('unpublish', 'Unpublish')->describe("Remove this page from the published site"); } if($this->stagesDiffer('Stage','Live')) { if($this->isPublished() && $this->canEdit()) { $actions[] = FormAction::create('rollback', 'Cancel draft changes')->describe("Delete your draft and revert to the currently published page"); } } if($this->canPublish()) $actions[] = new FormAction('publish', 'Save & Publish'); return new DataObjectSet($actions); } /** * Check if this page is new - that is, if it has yet to have been written * to the database. * @return boolean True if this page is new. */ function isNew() { /** * This check was a problem for a self-hosted site, and may indicate a bug in the interpreter on their server, or a bug here * Changing the condition from empty( $this->ID ) to !$this->ID && !$this->record['ID'] fixed this. */ if(empty($this->ID)) return true; if(is_numeric($this->ID)) return false; return stripos($this->ID, 'new') === 0; } /** * Check if this page has been published. * @return boolean True if this page has been published. */ function isPublished() { if($this->isNew()) return false; return DB::query("SELECT ID FROM `SiteTree_Live` WHERE ID = $this->ID")->value() ? true : false; } /** * Look for ghost parents */ function MultipleParents() { $parents = new GhostPage_ComponentSet($this->Parent); $parents->setOwner($this); $ghostPages = DataObject::get("GhostPage", "LinkedPageID = '$this->ID'"); if($ghostPages) foreach($ghostPages as $ghostPage) { // Ignore root ghost-pages if($p = $ghostPage->getParent()) $parents->push($p); } return $parents; } /** * Get the class dropdown used in the CMS to change the class of a page. * This returns the list of options in the drop as a Map from class name * to text in dropdown. * @return array */ function getClassDropdown() { $classes = ClassInfo::getValidSubClasses('SiteTree'); array_shift($classes); foreach($classes as $class) { $instance = singleton($class); if((($instance instanceof HiddenClass) || !$instance->canCreate()) && ($class != $this->class)) continue; $addAction = $instance->uninherited('add_action', true); if(!$addAction) $addAction = "a $class"; $result[$class] = ($class == $this->class) ? "Currently $addAction" : "Change to $addAction"; } return $result; } /** * Returns an array of the class names of classes that are allowed * to be children of this class. * @return array */ function allowedChildren() { $candidates = $this->stat('allowed_children'); if($candidates && $candidates != "none" && $candidates != "SiteTree_root") { foreach($candidates as $candidate) { if(substr($candidate,0,1) == '*') { $allowedChildren[] = substr($candidate,1); } else { $subclasses = ClassInfo::subclassesFor($candidate); foreach($subclasses as $subclass) { if($subclass != "SiteTree_root") $allowedChildren[] = $subclass; } } } return $allowedChildren; } } /** * Returns the class name of the default class for children * of this page. * @return string */ function defaultChild() { $default = $this->stat('default_child'); $allowed = $this->allowedChildren(); if($allowed) { if(!$default || !in_array($default, $allowed)) $default = reset($allowed); return $default; } } /** * Returns the class name of the default class for the parent * of this page. * @return string */ function defaultParent() { return $this->stat('default_parent'); } /** * Function to clean up the currently loaded page after a reorganise has been called. * It should return a piece of JavaScript to be executed on the client side, to clean * up the results of the reorganise. */ function cmsCleanup_parentChanged() { } /** * Get the title for use in menus for this page. If the MenuTitle * field is set it returns that, else it returns the Title field. * @return string */ function getMenuTitle(){ if($value = $this->getField("MenuTitle")) { return $value; } else { return $this->getField("Title"); } } /** * Set the menu title for this page. * @param string $value */ function setMenuTitle($value) { if($value == $this->getField("Title")) { $this->setField("MenuTitle", null); } else { $this->setField("MenuTitle", $value); } } /** * TitleWithStatus will return the title in an ,