mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
API-CHANGE: Global template variables can now be called directly using SSViewer_DataPresenter instead of needing to inherit off ViewableData
This commit is contained in:
parent
6c24589a92
commit
927dbbe717
@ -1311,7 +1311,7 @@ class LeftAndMain extends Controller {
|
|||||||
* @return String
|
* @return String
|
||||||
*/
|
*/
|
||||||
function Locale() {
|
function Locale() {
|
||||||
return DBField::create('DBLocale', $this->i18nLocale());
|
return DBField::create('DBLocale', i18n::get_locale());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
* @package sapphire
|
* @package sapphire
|
||||||
* @subpackage control
|
* @subpackage control
|
||||||
*/
|
*/
|
||||||
class Controller extends RequestHandler {
|
class Controller extends RequestHandler implements TemplateGlobalProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array $urlParams An array of arguments extracted from the URL
|
* @var array $urlParams An array of arguments extracted from the URL
|
||||||
@ -583,6 +583,12 @@ class Controller extends RequestHandler {
|
|||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getExposedVariables() {
|
||||||
|
return array(
|
||||||
|
'CurrentPage' => 'curr',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
* @subpackage control
|
* @subpackage control
|
||||||
* @see Director::direct(),Director::addRules(),Director::set_environment_type()
|
* @see Director::direct(),Director::addRules(),Director::set_environment_type()
|
||||||
*/
|
*/
|
||||||
class Director {
|
class Director implements TemplateGlobalProvider {
|
||||||
|
|
||||||
static private $urlParams;
|
static private $urlParams;
|
||||||
|
|
||||||
@ -866,4 +866,21 @@ class Director {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array Returns an array of strings of the method names of methods on the call that should be exposed
|
||||||
|
* as global variables in the templates.
|
||||||
|
*/
|
||||||
|
public static function getExposedVariables() {
|
||||||
|
return array(
|
||||||
|
'absoluteBaseURL',
|
||||||
|
'baseURL',
|
||||||
|
'is_ajax',
|
||||||
|
'isAjax' => 'is_ajax',
|
||||||
|
'BaseHref' => 'absoluteBaseURL', //@deprecated 3.0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -227,6 +227,7 @@ set_include_path($includePath);
|
|||||||
require_once 'cache/Cache.php';
|
require_once 'cache/Cache.php';
|
||||||
require_once 'core/Object.php';
|
require_once 'core/Object.php';
|
||||||
require_once 'core/ClassInfo.php';
|
require_once 'core/ClassInfo.php';
|
||||||
|
require_once 'view/TemplateGlobalProvider.php';
|
||||||
require_once 'control/Director.php';
|
require_once 'control/Director.php';
|
||||||
require_once 'dev/Debug.php';
|
require_once 'dev/Debug.php';
|
||||||
require_once 'filesystem/FileFinder.php';
|
require_once 'filesystem/FileFinder.php';
|
||||||
|
@ -57,7 +57,7 @@
|
|||||||
* @package sapphire
|
* @package sapphire
|
||||||
* @subpackage misc
|
* @subpackage misc
|
||||||
*/
|
*/
|
||||||
class i18n extends Object {
|
class i18n extends Object implements TemplateGlobalProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This static variable is used to store the current defined locale.
|
* This static variable is used to store the current defined locale.
|
||||||
@ -1956,6 +1956,13 @@ class i18n extends Object {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getExposedVariables() {
|
||||||
|
return array(
|
||||||
|
'i18nLocale' => 'get_locale',
|
||||||
|
'get_locale',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
* @package sapphire
|
* @package sapphire
|
||||||
* @subpackage security
|
* @subpackage security
|
||||||
*/
|
*/
|
||||||
class Member extends DataObject {
|
class Member extends DataObject implements TemplateGlobalProvider {
|
||||||
|
|
||||||
static $db = array(
|
static $db = array(
|
||||||
'FirstName' => 'Varchar',
|
'FirstName' => 'Varchar',
|
||||||
@ -1378,6 +1378,13 @@ class Member extends DataObject {
|
|||||||
// If can't find a suitable editor, just default to cms
|
// If can't find a suitable editor, just default to cms
|
||||||
return $currentName ? $currentName : 'cms';
|
return $currentName ? $currentName : 'cms';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getExposedVariables() {
|
||||||
|
return array(
|
||||||
|
'CurrentMember' => 'currentUser',
|
||||||
|
'currentUser'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* @package sapphire
|
* @package sapphire
|
||||||
* @subpackage security
|
* @subpackage security
|
||||||
*/
|
*/
|
||||||
class Permission extends DataObject {
|
class Permission extends DataObject implements TemplateGlobalProvider {
|
||||||
|
|
||||||
// the (1) after Type specifies the DB default value which is needed for
|
// the (1) after Type specifies the DB default value which is needed for
|
||||||
// upgrades from older SilverStripe versions
|
// upgrades from older SilverStripe versions
|
||||||
@ -621,6 +621,12 @@ class Permission extends DataObject {
|
|||||||
// Just in case we've altered someone's permissions
|
// Just in case we've altered someone's permissions
|
||||||
Permission::flush_permission_cache();
|
Permission::flush_permission_cache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getExposedVariables() {
|
||||||
|
return array(
|
||||||
|
'HasPerm' => 'check'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
*
|
*
|
||||||
* @todo Make token name form specific for additional forgery protection.
|
* @todo Make token name form specific for additional forgery protection.
|
||||||
*/
|
*/
|
||||||
class SecurityToken extends Object {
|
class SecurityToken extends Object implements TemplateGlobalProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var String
|
* @var String
|
||||||
@ -102,6 +102,15 @@ class SecurityToken extends Object {
|
|||||||
static function get_default_name() {
|
static function get_default_name() {
|
||||||
return self::$default_name;
|
return self::$default_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of an the global SecurityToken in the current session
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
static function getSecurityID() {
|
||||||
|
$token = SecurityToken::inst();
|
||||||
|
return $token->getValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return String
|
* @return String
|
||||||
@ -208,7 +217,13 @@ class SecurityToken extends Object {
|
|||||||
$generator = new RandomGenerator();
|
$generator = new RandomGenerator();
|
||||||
return $generator->generateHash('sha1');
|
return $generator->generateHash('sha1');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getExposedVariables() {
|
||||||
|
return array(
|
||||||
|
'getSecurityID',
|
||||||
|
'SecurityID' => 'getSecurityID'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -129,11 +129,13 @@ class ControllerTest extends FunctionalTest {
|
|||||||
'Without an allowed_actions, any defined methods are recognised as actions'
|
'Without an allowed_actions, any defined methods are recognised as actions'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Controller::BaseURL no longer exists, but was just a direct call to Director::BaseURL, so not sure what this code was supposed to test
|
||||||
public function testBaseURL() {
|
public function testBaseURL() {
|
||||||
Director::setBaseURL('/baseurl/');
|
Director::setBaseURL('/baseurl/');
|
||||||
$this->assertEquals(Controller::BaseURL(), Director::BaseURL());
|
$this->assertEquals(Controller::BaseURL(), Director::BaseURL());
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -145,7 +145,7 @@ class RequestHandlingTest extends FunctionalTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function testMethodsOnParentClassesOfRequestHandlerDeclined() {
|
function testMethodsOnParentClassesOfRequestHandlerDeclined() {
|
||||||
$response = Director::test('testGoodBase1/getSecurityID');
|
$response = Director::test('testGoodBase1/getIterator');
|
||||||
$this->assertEquals(403, $response->getStatusCode());
|
$this->assertEquals(403, $response->getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +84,45 @@ SS
|
|||||||
$this->assertEquals('{$Test}', $this->render('{\\$Test}'), 'Escapes can be used to avoid injection');
|
$this->assertEquals('{$Test}', $this->render('{\\$Test}'), 'Escapes can be used to avoid injection');
|
||||||
$this->assertEquals('{\\[out:Test]}', $this->render('{\\\\$Test}'), 'Escapes before injections are correctly unescaped');
|
$this->assertEquals('{\\[out:Test]}', $this->render('{\\\\$Test}'), 'Escapes before injections are correctly unescaped');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testGlobalVariableCalls() {
|
||||||
|
$this->assertEquals(Director::absoluteBaseURL(), $this->render('{$absoluteBaseURL}'), 'Director::absoluteBaseURL can be called from within template');
|
||||||
|
$this->assertEquals(Director::absoluteBaseURL(), $this->render('{$AbsoluteBaseURL}'), 'Upper-case %AbsoluteBaseURL can be called from within template');
|
||||||
|
|
||||||
|
$this->assertEquals(Director::is_ajax(), $this->render('{$isAjax}'), 'All variations of is_ajax result in the correct call');
|
||||||
|
$this->assertEquals(Director::is_ajax(), $this->render('{$IsAjax}'), 'All variations of is_ajax result in the correct call');
|
||||||
|
$this->assertEquals(Director::is_ajax(), $this->render('{$is_ajax}'), 'All variations of is_ajax result in the correct call');
|
||||||
|
$this->assertEquals(Director::is_ajax(), $this->render('{$Is_ajax}'), 'All variations of is_ajax result in the correct call');
|
||||||
|
|
||||||
|
$this->assertEquals(i18n::get_locale(), $this->render('{$i18nLocale}'), 'i18n template functions result correct result');
|
||||||
|
$this->assertEquals(i18n::get_locale(), $this->render('{$get_locale}'), 'i18n template functions result correct result');
|
||||||
|
|
||||||
|
$this->assertEquals((string)Controller::curr(), $this->render('{$CurrentPage}'), 'i18n template functions result correct result');
|
||||||
|
$this->assertEquals((string)Controller::curr(), $this->render('{$currentPage}'), 'i18n template functions result correct result');
|
||||||
|
|
||||||
|
$this->assertEquals(Member::currentUser(), $this->render('{$CurrentMember}'), 'Member template functions result correct result');
|
||||||
|
$this->assertEquals(Member::currentUser(), $this->render('{$CurrentUser}'), 'Member template functions result correct result');
|
||||||
|
$this->assertEquals(Member::currentUser(), $this->render('{$currentMember}'), 'Member template functions result correct result');
|
||||||
|
$this->assertEquals(Member::currentUser(), $this->render('{$currentUser}'), 'Member template functions result correct result');
|
||||||
|
|
||||||
|
$this->assertEquals(SecurityToken::getSecurityID(), $this->render('{$getSecurityID}'), 'SecurityToken template functions result correct result');
|
||||||
|
$this->assertEquals(SecurityToken::getSecurityID(), $this->render('{$SecurityID}'), 'SecurityToken template functions result correct result');
|
||||||
|
}
|
||||||
|
|
||||||
|
function testGlobalVariableCallsWithArguments() {
|
||||||
|
$this->assertEquals(Permission::check("ADMIN"), $this->render('{$HasPerm(\'ADMIN\')}'), 'Permissions template functions result correct result');
|
||||||
|
$this->assertEquals(Permission::check("ADMIN"), $this->render('{$hasPerm(\'ADMIN\')}'), 'Permissions template functions result correct result');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* //TODO: enable this test
|
||||||
|
function testLocalFunctionsTakePriorityOverGlobals() {
|
||||||
|
$data = new ArrayData(array(
|
||||||
|
'Page' => new SSViewerTest_Page()
|
||||||
|
));
|
||||||
|
|
||||||
|
$result = $this->render('<% control Page %>$absoluteBaseURL<% end_control %>',$data);
|
||||||
|
$this->assertEquals("testPageCalled",$result, "Local Object's function called. Did not return the actual baseURL of the current site");
|
||||||
|
}*/
|
||||||
|
|
||||||
function testObjectDotArguments() {
|
function testObjectDotArguments() {
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
@ -755,3 +794,10 @@ class SSViewerTest_ViewableData extends ViewableData implements TestOnly {
|
|||||||
class SSViewerTest_Controller extends Controller {
|
class SSViewerTest_Controller extends Controller {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SSViewerTest_Page extends SiteTree {
|
||||||
|
|
||||||
|
function absoluteBaseURL() {
|
||||||
|
return "testPageCalled";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -135,31 +135,58 @@ class SSViewer_Scope {
|
|||||||
*/
|
*/
|
||||||
class SSViewer_DataPresenter extends SSViewer_Scope {
|
class SSViewer_DataPresenter extends SSViewer_Scope {
|
||||||
|
|
||||||
private $extras;
|
private static $extras = array();
|
||||||
|
|
||||||
function __construct($item, $extras = null){
|
function __construct($item){
|
||||||
parent::__construct($item);
|
parent::__construct($item);
|
||||||
$this->extras = $extras;
|
|
||||||
|
if (count(self::$extras) == 0) { //build up extras array only once per request
|
||||||
|
//get all the exposed variables from all classes that implement the TemplateGlobalProvider interface
|
||||||
|
$implementers = ClassInfo::implementorsOf("TemplateGlobalProvider");
|
||||||
|
foreach($implementers as $implementer) {
|
||||||
|
$exposedVariables = $implementer::getExposedVariables(); //get the exposed variables
|
||||||
|
|
||||||
|
foreach($exposedVariables as $varName => $methodName) {
|
||||||
|
if (!$varName || is_numeric($varName)) $varName = $methodName; //array has just a single value, use it for both key and value
|
||||||
|
|
||||||
|
//e.g. "array(Director, absoluteBaseURL)" means call "Director::absoluteBaseURL()"
|
||||||
|
self::$extras[$varName] = array($implementer, $methodName);
|
||||||
|
$firstCharacter = substr($varName, 0, 1);
|
||||||
|
|
||||||
|
if ((strtoupper($firstCharacter) === $firstCharacter)) { //is uppercase, so save the lowercase version, too
|
||||||
|
self::$extras[lcfirst($varName)] = array($implementer, $methodName); //callable array
|
||||||
|
} else { //is lowercase, save a version so it also works uppercase
|
||||||
|
self::$extras[ucfirst($varName)] = array($implementer, $methodName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function __call($name, $arguments) {
|
function __call($name, $arguments) {
|
||||||
|
//TODO: make local functions take priority over global functions
|
||||||
|
|
||||||
$property = $arguments[0];
|
$property = $arguments[0];
|
||||||
|
if (array_key_exists($property, self::$extras)) {
|
||||||
if ($this->extras && array_key_exists($property, $this->extras)) {
|
$this->resetLocalScope(); //if we are inside a chain (e.g. $A.B.C.Up.E) break out to the beginning of it
|
||||||
|
|
||||||
$this->resetLocalScope();
|
$value = self::$extras[$property]; //get the method call
|
||||||
|
|
||||||
$value = $this->extras[$arguments[0]];
|
//only call callable functions
|
||||||
|
if (is_callable($value)) {
|
||||||
|
$value = call_user_func_array($value, array_slice($arguments, 1));
|
||||||
|
}
|
||||||
|
|
||||||
switch ($name) {
|
switch ($name) {
|
||||||
case 'hasValue':
|
case 'hasValue':
|
||||||
return (bool)$value;
|
return (bool)$value;
|
||||||
default:
|
default: //XML_val
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::__call($name, $arguments);
|
$callResult = parent::__call($name, $arguments);
|
||||||
|
return $callResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
21
view/TemplateGlobalProvider.php
Normal file
21
view/TemplateGlobalProvider.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Interface that is implemented by any classes that want to expose a method that can be called in a template.
|
||||||
|
* Director::AbsoluteBaseURL is an example of this.
|
||||||
|
* @package sapphire
|
||||||
|
* @subpackage core
|
||||||
|
*/
|
||||||
|
interface TemplateGlobalProvider {
|
||||||
|
/**
|
||||||
|
* @abstract
|
||||||
|
* @return array Returns an array of strings of the method names of methods on the call that should be exposed
|
||||||
|
* as global variables in the templates. A map (template-variable-name => method-name) can optionally be supplied
|
||||||
|
* if the template variable name is different from the name of the method to call. The case of the first character
|
||||||
|
* in the method name called from the template does not matter, although names specified in the map should
|
||||||
|
* correspond to the actual method name in the relevant class.
|
||||||
|
* Note that the template renderer must be able to call these methods statically.
|
||||||
|
*/
|
||||||
|
public static function getExposedVariables();
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
@ -689,75 +689,7 @@ class ViewableData extends Object implements IteratorAggregate {
|
|||||||
|
|
||||||
return Convert::raw2att(implode(' ', $classes));
|
return Convert::raw2att(implode(' ', $classes));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see Member::currentUser()
|
|
||||||
*/
|
|
||||||
public function CurrentMember() {
|
|
||||||
return Member::currentUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a CSRF-preventing ID to insert into a form.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getSecurityID() {
|
|
||||||
$token = SecurityToken::inst();
|
|
||||||
return $token->getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see Permission::check()
|
|
||||||
*/
|
|
||||||
public function HasPerm($code) {
|
|
||||||
return Permission::check($code);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see Director::absoluteBaseURL()
|
|
||||||
*
|
|
||||||
* @deprecated 3.0
|
|
||||||
*/
|
|
||||||
public function BaseHref() {
|
|
||||||
Deprecation::notice('3.0', 'Use AbsoluteBaseURL instead.');
|
|
||||||
|
|
||||||
return $this->AbsoluteBaseURL();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the absolute base url
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function AbsoluteBaseURL() {
|
|
||||||
return Director::absoluteBaseURL();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Access the BaseURL from template: proxy the value from the Director.
|
|
||||||
* Needed for building hardcoded links.
|
|
||||||
*
|
|
||||||
* @return string base url
|
|
||||||
*/
|
|
||||||
function BaseURL() {
|
|
||||||
return Director::baseURL();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see Director::is_ajax()
|
|
||||||
*/
|
|
||||||
public function IsAjax() {
|
|
||||||
return Director::is_ajax();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see i18n::get_locale()
|
|
||||||
*/
|
|
||||||
public function i18nLocale() {
|
|
||||||
return i18n::get_locale();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return debug information about this object that can be rendered into a template
|
* Return debug information about this object that can be rendered into a template
|
||||||
*
|
*
|
||||||
@ -767,19 +699,6 @@ class ViewableData extends Object implements IteratorAggregate {
|
|||||||
return new ViewableData_Debugger($this);
|
return new ViewableData_Debugger($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see Controller::curr()
|
|
||||||
*/
|
|
||||||
public function CurrentPage() {
|
|
||||||
return Controller::curr();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see SSViewer::topLevel()
|
|
||||||
*/
|
|
||||||
public function Top() {
|
|
||||||
return SSViewer::topLevel();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user