From 8424f9c4961d760c4649502b10896a7ec0415d32 Mon Sep 17 00:00:00 2001 From: Sam Minnee Date: Wed, 12 May 2010 05:28:11 +0000 Subject: [PATCH] API CHANGE: Moved site tree permission extension to a 3-state system (true, false, null, where null means "no effect") git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/branches/2.4@104669 467b73ca-7a2a-4603-9d3b-597d59a354a9 --- core/model/DataObject.php | 32 ++++++++++++++++++++++++ core/model/SiteTree.php | 51 +++++++++++++++++++-------------------- 2 files changed, 57 insertions(+), 26 deletions(-) diff --git a/core/model/DataObject.php b/core/model/DataObject.php index d7fa6cd11..6ef899b57 100755 --- a/core/model/DataObject.php +++ b/core/model/DataObject.php @@ -2320,6 +2320,38 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity } } + /** + * Process tri-state responses from permission-alterting decorators. The decorators are + * expected to return one of 3 values + * + * - false: Disallow this permission, regardless of what other decorators say + * - true: Allow this permission, as long as no other decorators return false + * - NULL: Don't affect the outcome + * + * This method itself returns a tri-state value, and is designed to be used like this: + * + * $extended = $this->extendedCan('canDoSomething', $member); + * if($extended !== null) return $extended; + * else return $normalValue; + */ + public function extendedCan($methodName, $member) { + $results = $this->extend($methodName, $member); + if($results && is_array($results)) { + // Remove NULLs + $results = array_filter($results, array($this,'isNotNull')); + // If there are any non-NULL responses, then return the lowest one of them. + // If any explicitly deny the permission, then we don't get access + if($results) return min($results); + } + return null; + } + /** + * Helper functon for extendedCan + */ + private function isNotNull($value) { + return !is_null($value); + } + /** * @param Member $member * @return boolean diff --git a/core/model/SiteTree.php b/core/model/SiteTree.php index cbf608b4d..40fb8b547 100755 --- a/core/model/SiteTree.php +++ b/core/model/SiteTree.php @@ -731,8 +731,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid if($member && Permission::checkMember($member, "ADMIN")) return true; - $results = $this->extend('canAddChildren', $member); - if($results && is_array($results)) if(!min($results)) return false; + // Standard mechanism for accepting permission changes from decorators + $extended = $this->extendedCan('canAddChildren', $member); + if($extended !== null) return $extended; return $this->canEdit($member) && $this->stat('allowed_children') != 'none'; } @@ -761,10 +762,10 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid // admin override if($member && Permission::checkMember($member, array("ADMIN", "SITETREE_VIEW_ALL"))) return true; - - // decorated access checks - $results = $this->extend('canView', $member); - if($results && is_array($results)) if(!min($results)) return false; + + // Standard mechanism for accepting permission changes from decorators + $extended = $this->extendedCan('canView', $member); + if($extended != null) return $extended; // check for empty spec if(!$this->CanViewType || $this->CanViewType == 'Anyone') return true; @@ -817,9 +818,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid return true; } - // decorated access checks - $results = $this->extend('canDelete', $memberID); - if($results && is_array($results)) if(!min($results)) return false; + // Standard mechanism for accepting permission changes from decorators + $extended = $this->extendedCan('canDelete', $memberID); + if($extended !== null) return $extended; // Check cache (the can_edit_multiple call below will also do this, but this is quicker) if(isset(self::$cache_permissions['delete'][$this->ID])) { @@ -858,9 +859,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid if($member && Permission::checkMember($member, "ADMIN")) return true; - // decorated permission checks - $results = $this->extend('canCreate', $member); - if($results && is_array($results)) if(!min($results)) return false; + // Standard mechanism for accepting permission changes from decorators + $extended = $this->extendedCan('canCreate', $member); + if($extended !== null) return $extended; return $this->stat('can_create') != false || Director::isDev(); } @@ -892,9 +893,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid if($memberID && Permission::checkMember($memberID, array("ADMIN", "SITETREE_EDIT_ALL"))) return true; - // decorated access checks - $results = $this->extend('canEdit', $memberID); - if($results && is_array($results)) if(!min($results)) return false; + // Standard mechanism for accepting permission changes from decorators + $extended = $this->extendedCan('canEdit', $memberID); + if($extended !== null) return $extended; if($this->ID) { // Check cache (the can_edit_multiple call below will also do this, but this is quicker) @@ -933,21 +934,19 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser(); if($member && Permission::checkMember($member, "ADMIN")) return true; - - // If we have a result, then that means at least one decorator specified alternateCanPublish - // Allow the permission check only if *all* voting decorators allow it. - $results = $this->extend('canPublish', $member); - if($results && is_array($results)) if(!min($results)) return false; - // Normal case + // Standard mechanism for accepting permission changes from decorators + $extended = $this->extendedCan('canPublish', $member); + if($extended !== null) return $extended; + + // Normal case - fail over to canEdit() return $this->canEdit($member); } public function canDeleteFromLive($member = null) { - // If we have a result, then that means at least one decorator specified canDeleteFromLive - // Allow the permission check only if *all* voting decorators allow it. - $results = $this->extend('canDeleteFromLive', $member); - if($results && is_array($results)) if(!min($results)) return false; + // Standard mechanism for accepting permission changes from decorators + $extended = $this->extendedCan('canDeleteFromLive', $member); + if($extended !==null) return $extended; return $this->canPublish($member); } @@ -1570,7 +1569,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid } // Redirector pages - $redirectors = DataObject::get("RedirectorPage", "\"RedirectionType\" = 'Internal' AND \"LinkToID\" = $this->ID"); + $redirectors = DataObject::get("RedirectorPage", "\"RedirectorPage\".\"RedirectionType\" = 'Internal' AND \"LinkToID\" = $this->ID"); if($redirectors) { foreach($redirectors as $item) $item->DependentLinkType = 'Redirector page'; $items->merge($redirectors);