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
This commit is contained in:
Sam Minnee 2010-05-12 05:28:11 +00:00
parent 82a429ebb8
commit 8424f9c496
2 changed files with 57 additions and 26 deletions

View File

@ -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

View File

@ -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);