diff --git a/core/model/Database.php b/core/model/Database.php index f14d64399..7b1a01477 100755 --- a/core/model/Database.php +++ b/core/model/Database.php @@ -234,9 +234,9 @@ abstract class Database extends Object { foreach($indexSchema as $indexName => $indexDetails) { $this->requireIndex($table, $indexName, $indexDetails); } - } + } } - + /** * If the given table exists, move it out of the way by renaming it to _obsolete_(tablename). * @param string $table The table name. @@ -246,7 +246,7 @@ abstract class Database extends Object { if(isset($this->tableList[strtolower($table)])) { while($this->tableList[strtolower("_obsolete_{$table}$suffix")]) { $suffix = $suffix ? ($suffix+1) : 2; - } + } $this->renameTable($table, "_obsolete_{$table}$suffix"); Database::alteration_message("Table $table: renamed to _obsolete_{$table}$suffix","obsolete"); } @@ -340,17 +340,17 @@ abstract class Database extends Object { case "update": $sql = "update `$table` SET $fieldList where $writeInfo[where]"; $this->query($sql); - + // If numAffectedRecord = 0, then we want to run instert instead if(!$this->affectedRows()) { if(!isset($writeInfo['fields']['ID']) && isset($writeInfo['id'])) { $fieldList .= ", ID = $writeInfo[id]"; } $sql = "insert into `$table` SET $fieldList"; - $this->query($sql, null); - } + $this->query($sql, null); + } break; - + case "insert": if(!isset($writeInfo['fields']['ID']) && isset($writeInfo['id'])) { $fieldList .= ", ID = $writeInfo[id]"; @@ -358,7 +358,7 @@ abstract class Database extends Object { $sql = "insert into `$table` SET $fieldList"; $this->query($sql); break; - + default: $sql = null; user_error("Database::manipulate() Can't recognise command '$writeInfo[command]'", E_USER_ERROR); @@ -367,7 +367,7 @@ abstract class Database extends Object { } } - /** + /** * Error handler for database errors. * All database errors will call this function to report the error. It isn't a static function; * it will be called on the object itself and as such can be overridden in a subclass. @@ -438,7 +438,7 @@ abstract class Query extends Object implements Iterator { * @var int */ private $rowNum = -1; - + /** * Return an array containing all values in the leftmost column. * @return array @@ -475,7 +475,7 @@ abstract class Query extends Object implements Iterator { } return $column; } - + /** * Returns the next record in the iterator. * @return array @@ -483,7 +483,7 @@ abstract class Query extends Object implements Iterator { public function record() { return $this->next(); } - + /** * Returns the first column of the first record. * @return string @@ -493,7 +493,7 @@ abstract class Query extends Object implements Iterator { return reset($record); } } - + /** * Return an HTML table containing the full result-set */ @@ -545,7 +545,7 @@ abstract class Query extends Object implements Iterator { return $this->currentRecord; } } - + /** * Iterator function implementation. Return the first item of this iterator. * @return array @@ -569,7 +569,7 @@ abstract class Query extends Object implements Iterator { * @return array */ public function next() { - $this->currentRecord = $this->nextRecord(); + $this->currentRecord = $this->nextRecord(); $this->rowNum++; return $this->currentRecord; } @@ -581,7 +581,7 @@ abstract class Query extends Object implements Iterator { public function valid() { return $this->current() !== false; } - + /** * Return the next record in the query result. * @return array @@ -602,4 +602,4 @@ abstract class Query extends Object implements Iterator { abstract function seek($rowNum); } -?> +?> \ No newline at end of file diff --git a/core/model/SiteTree.php b/core/model/SiteTree.php index d7309a4df..481bdd949 100644 --- a/core/model/SiteTree.php +++ b/core/model/SiteTree.php @@ -1,5 +1,4 @@ "Varchar(255)", + "Title" => "Varchar(255)", + "MenuTitle" => "Varchar(100)", + "Content" => "HTMLText", + "MetaTitle" => "Varchar(255)", + "MetaDescription" => "Varchar(255)", + "MetaKeywords" => "Varchar(255)", + "ShowInMenus" => "Boolean", + "ShowInSearch" => "Boolean", + "HomepageForDomain" => "Varchar(100)", + "ProvideComments" => "Boolean", + "Sort" => "Int", + "LegacyURL" => "Varchar(255)", + "HasBrokenFile" => "Boolean", + "HasBrokenLink" => "Boolean", + "Status" => "Varchar", + "ReportClass" => "Varchar", + "Priority" => "Float", + + "Viewers" => "Enum('Anyone, LoggedInUsers, OnlyTheseUsers', 'Anyone')", + "Editors" => "Enum('LoggedInUsers, OnlyTheseUsers', 'LoggedInUsers')", + "ViewersGroup" => "Int", + "EditorsGroup" => "Int" + ); + + static $indexes = array( + "SearchFields" => "fulltext (Title, MenuTitle, Content, MetaTitle, MetaDescription, MetaKeywords)", + "TitleSearchFields" => "fulltext (Title)" + ); + + static $has_many = array( + "Comments" => "PageComment" + ); + + static $many_many = array( + "LinkTracking" => "SiteTree", + "ImageTracking" => "File" + ); + + static $belongs_many_many = array( + "BackLinkTracking" => "SiteTree" + ); + + static $many_many_extraFields = array( + "LinkTracking" => array("FieldName" => "Varchar"), + "ImageTracking" => array("FieldName" => "Varchar") + ); + + static $casting = array( + "Breadcrumbs" => "HTMLText", + "LastEdited" => "Datetime", + "Created" => "Datetime", + ); + + static $defaults = array( + "ShowInMenus" => 1, + "ShowInSearch" => 1, + "Status" => "New page", + "CanCreateChildren" => array(10), + "Priority" => 0.5, + + "Viewers" => "Anyone", + "Editors" => "LoggedInUsers" + ); + + static $has_one = array( + "Parent" => "SiteTree" + ); + + static $versioning = array( + "Stage", "Live" + ); + + static $default_sort = "Sort"; + + /** + * The text shown in the create page dropdown. If + * this is not set, default to "Create a ClassName". + * @var string + */ + static $add_action = null; + + /** + * If this is false, the class cannot be created in the CMS. + * @var boolean + */ + static $can_create = true; + + + /** + * Icon to use in the CMS + * + * This should be the base filename. The suffixes -file.gif, + * -openfolder.gif and -closedfolder.gif will be appended to the base name + * that you provide there. + * If you prefer, you can pass an array: + * array("jsparty\tree\images\page", $option). + * $option can be either "file" or "folder" to force the icon to always + * be a file or folder, regardless of whether the page has children or not + * + * @var string|array + */ + static $icon = array("jsparty/tree/images/page", "file"); + + + static $extensions = array( + "Hierarchy", + "Versioned('Stage', 'Live')", + ); + + /** * Get the URL for this page. + * * @param string $action An action to include in the link * @return string The URL for this page */ @@ -25,6 +188,8 @@ class SiteTree extends DataObject { return Director::baseURL() . $this->URLSegment . "/$action"; } + + /** * Get the absolute URL for this page by stage */ @@ -37,25 +202,30 @@ class SiteTree extends DataObject { /** * 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"; + 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"; + 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 + * 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() { @@ -70,31 +240,38 @@ class SiteTree extends DataObject { } } + /** * 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; + 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() { @@ -105,9 +282,11 @@ class SiteTree extends DataObject { 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. + * 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) { @@ -116,8 +295,11 @@ class SiteTree extends DataObject { return $page; } + /** - * Duplicates each child of this node recursively and returns the duplicate node. + * Duplicates each child of this node recursively and returns the + * duplicate node. + * * @return SiteTree The duplicated object. */ public function duplicateWithChildren() { @@ -126,7 +308,9 @@ class SiteTree extends DataObject { if($children) { foreach($children as $child) { - $childClone = method_exists( $child, 'duplicateWithChildren' ) ? $child->duplicateWithChildren() : $child->duplicate(); + $childClone = method_exists($child, 'duplicateWithChildren') + ? $child->duplicateWithChildren() + : $child->duplicate(); $childClone->ParentID = $clone->ID; $childClone->write(); } @@ -135,31 +319,40 @@ class SiteTree extends DataObject { return $clone; } + /** - * Duplicate this node and its children as a child of the node with the given id + * 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 ) { + 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. @@ -169,12 +362,16 @@ class SiteTree extends DataObject { 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; + $nextID = isset(Director::currentPage()->Parent->ID) + ? Director::currentPage()->Parent->ID + : null; } else { $nextID = SiteTree::$currentPageID; } - $table = (Versioned::current_stage() == "Live") ? "SiteTree_Live" : "SiteTree"; + $table = (Versioned::current_stage() == "Live") + ? "SiteTree_Live" + : "SiteTree"; SiteTree::$currentSectionIDs = array(); while($nextID) { @@ -184,8 +381,10 @@ class SiteTree extends DataObject { } } + /** * Check if this is the currently viewed page. + * * @return boolean True if this is the current page. */ public function isCurrent() { @@ -193,8 +392,10 @@ class SiteTree extends DataObject { 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() { @@ -202,23 +403,29 @@ class SiteTree extends DataObject { 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) { + public function Breadcrumbs($maxDepth = 20, $unlinked = false, + $stopAtPageType = false) { $page = $this; $parts = array(); $i = 0; - while(($page && (sizeof($parts) < $maxDepth)) || ($stopAtPageType && $page->ClassName != $stopAtPageType)) { + 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) . ""); + $parts[] = (($page->ID == $this->ID) || $unlinked) + ? Convert::raw2xml($page->Title) + : ("Link() . "\">" . Convert::raw2xml($page->Title) . ""); } $page = $page->Parent; } @@ -226,18 +433,23 @@ class SiteTree extends DataObject { 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")); + 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 + * + * @param SiteTree|int $item Either the parent object, or the parent ID */ public function setParent($item) { if(is_object($item)) { @@ -247,8 +459,11 @@ class SiteTree extends DataObject { } } + /** - * Return a string of the form "parent - page" or "grandparent - parent - page". + * 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 @@ -262,8 +477,14 @@ class SiteTree extends DataObject { } return implode($separator, array_reverse($parts)); } - - /** + + /** + * 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. + * * Returns true if the member is allowed to do the given action. * * @param string $perm The permission to be checked, such as 'View'. @@ -279,26 +500,31 @@ class SiteTree extends DataObject { if(!isset($member)) { $member = Member::currentUser(); } - - // Users with ADMIN permission can always do this - if($member && Permission::check('ADMIN')) { + if($member && $member->isAdmin()) { 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': + switch(strtolower($perm)) { + case 'edit': + if((($this->Editors == 'LoggedInUsers' && $member) || + ($this->Editors == 'OnlyTheseUsers' && $member && + $member->isInGroup($this->EditorsGroup))) == false) + return false; + 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; + if((($this->Viewers == 'Anyone') || + ($this->Viewers == 'LoggedInUsers' && $member) || + ($this->Viewers == 'OnlyTheseUsers' && $member && + $member->isInGroup($this->ViewersGroup))) == false) + return false; + break; } - return false; + return true; + + //return parent::can($perm, $member); } @@ -315,27 +541,57 @@ class SiteTree extends DataObject { 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. + * This function should return true if the current user can view this + * page. + * + * It can be overloaded to customise the security model for an + * application. + * + * @return boolean True if the current user can view this page. + */ + public function canView() { + return $this->can('view'); + } + + + /** + * 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. + * 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. + * 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() { @@ -343,8 +599,12 @@ class SiteTree extends DataObject { } /** - * 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. + * 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() { @@ -353,10 +613,14 @@ class SiteTree extends DataObject { /** * 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. + * + * {@link $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()) { @@ -368,25 +632,35 @@ class SiteTree extends DataObject { } } + /** * Return the title, description and keywords metatags. * @param boolean|string $includeTitle Show default
done"; } + /** * Replace a URL in html content with a new URL. * @param string $old The old URL @@ -588,9 +897,11 @@ class SiteTree extends DataObject { /** * 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. + * 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() { @@ -600,17 +911,22 @@ class SiteTree extends DataObject { // 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)) { @@ -632,8 +948,9 @@ class SiteTree extends DataObject { $lastParent = array_pop($parentPageLinks); $parentList = "'$lastParent'"; - if(count( $parentPageLinks ) > 0) { - $parentList = "'" . implode("', '", $parentPageLinks) . "' and " . $parentList; + if(count($parentPageLinks) > 0) { + $parentList = "'" . implode("', '", $parentPageLinks) . "' and " + . $parentList; } $statusMessage[] = "This content also appears on the virtual pages in the $parentList sections."; @@ -648,6 +965,7 @@ class SiteTree extends DataObject { $message .= "NOTE: " . implode("