mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-06-26 06:29:24 +02:00
ENHANCEMENT: Added Object::combined_static(), which gets all values of a static property from each class in the hierarchy
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@73473 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
6b2cf2a48e
commit
a28ea0a69e
|
@ -251,6 +251,37 @@ abstract class Object {
|
|||
return ($inherited != $parent) ? $inherited : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse down a class ancestry and attempt to merge all the uninherited static values for a particular static
|
||||
* into a single variable
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $name the static name
|
||||
* @param string $ceiling an optional parent class name to begin merging statics down from, rather than traversing
|
||||
* the entire hierarchy
|
||||
* @return mixed
|
||||
*/
|
||||
public static function combined_static($class, $name, $ceiling = false) {
|
||||
$ancestry = ClassInfo::ancestry($class);
|
||||
$values = null;
|
||||
|
||||
if($ceiling) while(current($ancestry) != $ceiling && $ancestry) {
|
||||
array_shift($ancestry);
|
||||
}
|
||||
|
||||
if($ancestry) foreach($ancestry as $ancestor) {
|
||||
$merge = self::uninherited_static($ancestor, $name);
|
||||
|
||||
if(is_array($values) && is_array($merge)) {
|
||||
$values = array_merge($values, $merge);
|
||||
} elseif($merge) {
|
||||
$values = $merge;
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge in a set of additional static variables
|
||||
*
|
||||
|
|
|
@ -81,8 +81,7 @@ class RequestHandler extends ViewableData {
|
|||
$handlerClass = ($this->class) ? $this->class : get_class($this);
|
||||
// We stop after RequestHandler; in other words, at ViewableData
|
||||
while($handlerClass && $handlerClass != 'ViewableData') {
|
||||
// Todo: ajshort's stat rewriting could be useful here.
|
||||
$urlHandlers = eval("return $handlerClass::\$url_handlers;");
|
||||
$urlHandlers = Object::get_static($handlerClass, 'url_handlers');
|
||||
|
||||
if($urlHandlers) foreach($urlHandlers as $rule => $action) {
|
||||
if(isset($_REQUEST['debug_request'])) Debug::message("Testing '$rule' with '" . $request->remaining() . "' on $this->class");
|
||||
|
@ -144,65 +143,57 @@ class RequestHandler extends ViewableData {
|
|||
// If nothing matches, return this object
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check that the given action is allowed to be called from a URL.
|
||||
* It will interrogate {@link self::$allowed_actions} to determine this.
|
||||
*/
|
||||
function checkAccessAction($action) {
|
||||
// Collate self::$allowed_actions from this class and all parent classes
|
||||
$access = null;
|
||||
$className = ($this->class) ? $this->class : get_class($this);
|
||||
while($className && $className != 'RequestHandler') {
|
||||
// Merge any non-null parts onto $access.
|
||||
$accessPart = eval("return $className::\$allowed_actions;");
|
||||
if($accessPart != null) $access = array_merge((array)$access, $accessPart);
|
||||
|
||||
// Build an array of parts for checking if part[0] == part[1], which means that this class doesn't directly define it.
|
||||
$accessParts[] = $accessPart;
|
||||
|
||||
$className = get_parent_class($className);
|
||||
}
|
||||
$action = strtolower($action);
|
||||
$allowedActions = Object::combined_static($this->class, 'allowed_actions');
|
||||
$newAllowedActions = array();
|
||||
|
||||
// Add $allowed_actions from extensions
|
||||
if($this->extension_instances) {
|
||||
foreach($this->extension_instances as $inst) {
|
||||
$accessPart = $inst->stat('allowed_actions');
|
||||
if($accessPart !== null) $access = array_merge((array)$access, $accessPart);
|
||||
// merge in any $allowed_actions from extensions
|
||||
if($this->extension_instances) foreach($this->extension_instances as $extension) {
|
||||
if($extAccess = $extension->stat('allowed_actions')) {
|
||||
$allowedActions = array_merge($allowedActions, $extAccess);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if($action == 'index') return true;
|
||||
|
||||
// Make checkAccessAction case-insensitive
|
||||
$action = strtolower($action);
|
||||
if($access) {
|
||||
foreach($access as $k => $v) $newAccess[strtolower($k)] = strtolower($v);
|
||||
$access = $newAccess;
|
||||
if($allowedActions) {
|
||||
foreach($allowedActions as $key => $value) {
|
||||
$newAllowedActions[strtolower($key)] = strtolower($value);
|
||||
}
|
||||
|
||||
$allowedActions = $newAllowedActions;
|
||||
|
||||
if(isset($allowedActions[$action])) {
|
||||
$test = $allowedActions[$action];
|
||||
|
||||
if(isset($access[$action])) {
|
||||
$test = $access[$action];
|
||||
if($test === true) return true;
|
||||
if(substr($test,0,2) == '->') {
|
||||
$funcName = substr($test,2);
|
||||
return $this->$funcName();
|
||||
if($test === true) {
|
||||
return true;
|
||||
} elseif(substr($test, 0, 2) == '->') {
|
||||
return $this->{substr($test, 2)}();
|
||||
} elseif(Permission::check($test)) {
|
||||
return true;
|
||||
}
|
||||
if(Permission::check($test)) return true;
|
||||
} else if((($key = array_search($action, $access)) !== false) && is_numeric($key)) {
|
||||
} elseif((($key = array_search($action, $allowedActions)) !== false) && is_numeric($key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if($access === null || (isset($accessParts[1]) && $accessParts[0] === $accessParts[1])) {
|
||||
|
||||
if($allowedActions === null || !$this->uninherited('allowed_actions')) {
|
||||
// If no allowed_actions are provided, then we should only let through actions that aren't handled by magic methods
|
||||
// we test this by calling the unmagic method_exists and comparing it to the magic $this->hasMethod(). This will
|
||||
// still let through actions that are handled by templates.
|
||||
return method_exists($this, $action) || !$this->hasMethod($action);
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Throw an HTTP error instead of performing the normal processing
|
||||
* @todo This doesn't work properly right now. :-(
|
||||
|
|
|
@ -35,6 +35,10 @@ class ControllerTest extends SapphireTest {
|
|||
|
||||
$response = Director::test("ControllerTest_SecuredController/adminonly");
|
||||
$this->assertEquals(403, $response->getStatusCode());
|
||||
|
||||
// test that a controller without a specified allowed_actions allows actions through
|
||||
$response = Director::test('ControllerTest_UnsecuredController/stringaction');
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -105,4 +109,6 @@ class ControllerTest_SecuredController extends Controller {
|
|||
function adminonly() {
|
||||
return "You must be an admin!";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ControllerTest_UnsecuredController extends ControllerTest_SecuredController {}
|
|
@ -52,6 +52,23 @@ class ObjectStaticTest extends SapphireTest {
|
|||
$this->assertEquals(Object::uninherited_static('ObjectStaticTest_Fourth', 'third', true), null);
|
||||
}
|
||||
|
||||
public function testCombinedStatic() {
|
||||
// test basic operation
|
||||
$this->assertEquals (
|
||||
array('test_1', 'test_2', 'test_3'), Object::combined_static('ObjectStaticTest_Combined3', 'first')
|
||||
);
|
||||
|
||||
// test that null values are ignored, but values on either side are still merged
|
||||
$this->assertEquals (
|
||||
array('test_1', 'test_3'), Object::combined_static('ObjectStaticTest_Combined3', 'second')
|
||||
);
|
||||
|
||||
// test the $ceiling param
|
||||
$this->assertEquals (
|
||||
array('test_2', 'test_3'), Object::combined_static('ObjectStaticTest_Combined3', 'first', 'ObjectStaticTest_Combined2')
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**#@+
|
||||
|
@ -76,4 +93,19 @@ class ObjectStaticTest_Third extends ObjectStaticTest_Second {
|
|||
class ObjectStaticTest_Fourth extends ObjectStaticTest_Third {
|
||||
public static $fourth = array('test_4');
|
||||
}
|
||||
|
||||
class ObjectStaticTest_Combined1 extends Object {
|
||||
public static $first = array('test_1');
|
||||
public static $second = array('test_1');
|
||||
}
|
||||
|
||||
class ObjectStaticTest_Combined2 extends ObjectStaticTest_Combined1 {
|
||||
public static $first = array('test_2');
|
||||
public static $second = null;
|
||||
}
|
||||
|
||||
class ObjectStaticTest_Combined3 extends ObjectStaticTest_Combined2 {
|
||||
public static $first = array('test_3');
|
||||
public static $second = array('test_3');
|
||||
}
|
||||
/**#@-*/
|
||||
|
|
Loading…
Reference in New Issue
Block a user