Permalink
Browse files

API-CHANGE: Global template variables can now be called directly usin…

…g SSViewer_DataPresenter instead of needing to inherit off ViewableData
  • Loading branch information...
1 parent 6c24589 commit 927dbbe717be3975c3e763bd462a8b6ebbe0a035 Hamish Friedlander committed Feb 11, 2012
@@ -1311,7 +1311,7 @@ function IsPreviewExpanded() {
* @return String
*/
function Locale() {
- return DBField::create('DBLocale', $this->i18nLocale());
+ return DBField::create('DBLocale', i18n::get_locale());
}
/**
View
@@ -9,7 +9,7 @@
* @package sapphire
* @subpackage control
*/
-class Controller extends RequestHandler {
+class Controller extends RequestHandler implements TemplateGlobalProvider {
/**
* @var array $urlParams An array of arguments extracted from the URL
@@ -583,6 +583,12 @@ static function join_links() {
return $result;
}
+
+ public static function getExposedVariables() {
+ return array(
+ 'CurrentPage' => 'curr',
+ );
+ }
}
View
@@ -11,7 +11,7 @@
* @subpackage control
* @see Director::direct(),Director::addRules(),Director::set_environment_type()
*/
-class Director {
+class Director implements TemplateGlobalProvider {
static private $urlParams;
@@ -866,4 +866,21 @@ static function isTest() {
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
+ );
+ }
+
}
+
+
+
View
@@ -227,6 +227,7 @@ function array_fill_keys($keys,$value) {
require_once 'cache/Cache.php';
require_once 'core/Object.php';
require_once 'core/ClassInfo.php';
+require_once 'view/TemplateGlobalProvider.php';
require_once 'control/Director.php';
require_once 'dev/Debug.php';
require_once 'filesystem/FileFinder.php';
View
@@ -57,7 +57,7 @@
* @package sapphire
* @subpackage misc
*/
-class i18n extends Object {
+class i18n extends Object implements TemplateGlobalProvider {
/**
* This static variable is used to store the current defined locale.
@@ -1956,6 +1956,13 @@ static function merge_locale_data($locale, $extra) {
}
}
}
+
+ public static function getExposedVariables() {
+ return array(
+ 'i18nLocale' => 'get_locale',
+ 'get_locale',
+ );
+ }
}
View
@@ -5,7 +5,7 @@
* @package sapphire
* @subpackage security
*/
-class Member extends DataObject {
+class Member extends DataObject implements TemplateGlobalProvider {
static $db = array(
'FirstName' => 'Varchar',
@@ -1378,6 +1378,13 @@ function getHtmlEditorConfigForCMS() {
// If can't find a suitable editor, just default to cms
return $currentName ? $currentName : 'cms';
}
+
+ public static function getExposedVariables() {
+ return array(
+ 'CurrentMember' => 'currentUser',
+ 'currentUser'
+ );
+ }
}
/**
View
@@ -4,7 +4,7 @@
* @package sapphire
* @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
// upgrades from older SilverStripe versions
@@ -621,6 +621,12 @@ public function onBeforeWrite() {
// Just in case we've altered someone's permissions
Permission::flush_permission_cache();
}
+
+ public static function getExposedVariables() {
+ return array(
+ 'HasPerm' => 'check'
+ );
+ }
}
View
@@ -28,7 +28,7 @@
*
* @todo Make token name form specific for additional forgery protection.
*/
-class SecurityToken extends Object {
+class SecurityToken extends Object implements TemplateGlobalProvider {
/**
* @var String
@@ -102,6 +102,15 @@ static function is_enabled() {
static function get_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
@@ -208,7 +217,13 @@ protected function generate() {
$generator = new RandomGenerator();
return $generator->generateHash('sha1');
}
-
+
+ public static function getExposedVariables() {
+ return array(
+ 'getSecurityID',
+ 'SecurityID' => 'getSecurityID'
+ );
+ }
}
/**
@@ -129,11 +129,13 @@ public function testHasAction() {
'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() {
Director::setBaseURL('/baseurl/');
$this->assertEquals(Controller::BaseURL(), Director::BaseURL());
}
+ */
}
/**
@@ -145,7 +145,7 @@ public function testHTTPError() {
}
function testMethodsOnParentClassesOfRequestHandlerDeclined() {
- $response = Director::test('testGoodBase1/getSecurityID');
+ $response = Director::test('testGoodBase1/getIterator');
$this->assertEquals(403, $response->getStatusCode());
}
@@ -84,6 +84,45 @@ function testBasicInjection() {
$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');
}
+
+ 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() {
$this->assertEquals(
@@ -755,3 +794,10 @@ function methodWithTwoArguments($arg1, $arg2) {
class SSViewerTest_Controller extends Controller {
}
+
+class SSViewerTest_Page extends SiteTree {
+
+ function absoluteBaseURL() {
+ return "testPageCalled";
+ }
+}
View
@@ -135,31 +135,58 @@ function __call($name, $arguments) {
*/
class SSViewer_DataPresenter extends SSViewer_Scope {
- private $extras;
-
- function __construct($item, $extras = null){
+ private static $extras = array();
+
+ function __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) {
+ //TODO: make local functions take priority over global functions
+
$property = $arguments[0];
-
- if ($this->extras && array_key_exists($property, $this->extras)) {
-
- $this->resetLocalScope();
-
- $value = $this->extras[$arguments[0]];
-
+ if (array_key_exists($property, self::$extras)) {
+ $this->resetLocalScope(); //if we are inside a chain (e.g. $A.B.C.Up.E) break out to the beginning of it
+
+ $value = self::$extras[$property]; //get the method call
+
+ //only call callable functions
+ if (is_callable($value)) {
+ $value = call_user_func_array($value, array_slice($arguments, 1));
+ }
+
switch ($name) {
case 'hasValue':
return (bool)$value;
- default:
+ default: //XML_val
return $value;
}
}
-
- return parent::__call($name, $arguments);
+
+ $callResult = parent::__call($name, $arguments);
+ return $callResult;
}
}
@@ -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();
+}
+
+?>
Oops, something went wrong.

0 comments on commit 927dbbe

Please sign in to comment.