From 29bba74db207a59ad3dc50da865551be953b26cc Mon Sep 17 00:00:00 2001 From: Chuck Hagenbuch Date: Sun, 22 Feb 2009 23:21:26 -0500 Subject: [PATCH] - add helper base test - add getHelperPaths - add local variables in views - add partials - add .html to template names with no extension --- framework/View/lib/Horde/View.php | 9 + framework/View/lib/Horde/View/Base.php | 90 ++++++++- framework/View/test/Horde/View/BaseTest.php | 225 +++++++++++++++++++++ .../View/test/Horde/View/fixtures/_myPartial.html | 1 + .../test/Horde/View/fixtures/_myPartialLocals.html | 1 + .../test/Horde/View/fixtures/_myPartialObject.html | 1 + .../Horde/View/fixtures/subdir/testRender.html | 1 + .../View/test/Horde/View/fixtures/testEscape.html | 1 + .../View/test/Horde/View/fixtures/testPartial.html | 1 + .../Horde/View/fixtures/testPartialCollection.html | 1 + .../Horde/View/fixtures/testPartialLocals.html | 1 + .../Horde/View/fixtures/testPartialObject.html | 1 + .../View/test/Horde/View/fixtures/testRender.html | 1 + 13 files changed, 331 insertions(+), 3 deletions(-) create mode 100644 framework/View/test/Horde/View/BaseTest.php create mode 100644 framework/View/test/Horde/View/fixtures/_myPartial.html create mode 100644 framework/View/test/Horde/View/fixtures/_myPartialLocals.html create mode 100644 framework/View/test/Horde/View/fixtures/_myPartialObject.html create mode 100644 framework/View/test/Horde/View/fixtures/subdir/testRender.html create mode 100644 framework/View/test/Horde/View/fixtures/testEscape.html create mode 100644 framework/View/test/Horde/View/fixtures/testPartial.html create mode 100644 framework/View/test/Horde/View/fixtures/testPartialCollection.html create mode 100644 framework/View/test/Horde/View/fixtures/testPartialLocals.html create mode 100644 framework/View/test/Horde/View/fixtures/testPartialObject.html create mode 100644 framework/View/test/Horde/View/fixtures/testRender.html diff --git a/framework/View/lib/Horde/View.php b/framework/View/lib/Horde/View.php index d7820edc7..5827dca14 100644 --- a/framework/View/lib/Horde/View.php +++ b/framework/View/lib/Horde/View.php @@ -17,9 +17,18 @@ class Horde_View extends Horde_View_Base * * @param string The template to execute. Not declared in the * function signature so it stays out of the view's public scope. + * + * @param array Any local variables to declare. */ protected function _run() { + // set local variables + if (is_array(func_get_arg(1))) { + foreach (func_get_arg(1) as $key => $value) { + ${$key} = $value; + } + } + $oldShortOpenTag = ini_set('short_open_tag', 1); include func_get_arg(0); ini_set('short_open_tag', $oldShortOpenTag); diff --git a/framework/View/lib/Horde/View/Base.php b/framework/View/lib/Horde/View/Base.php index 8eff39fc5..34a13caf0 100644 --- a/framework/View/lib/Horde/View/Base.php +++ b/framework/View/lib/Horde/View/Base.php @@ -152,6 +152,16 @@ abstract class Horde_View_Base } /** + * Return the template paths + * + * @return array + */ + public function getTemplatePaths() + { + return $this->_templatePath; + } + + /** * Adds to the stack of helpers in LIFO order. * * @param Horde_View_Helper $helper The helper instance to add. If this is a @@ -222,8 +232,14 @@ abstract class Horde_View_Base * * @return string The template output. */ - public function render($name) + public function render($name, $locals = array()) { + // render partial + if (is_array($name) && $partial = $name['partial']) { + unset($name['partial']); + return $this->renderPartial($partial, $name); + } + // Find the template file name. $this->_file = $this->_template($name); @@ -231,11 +247,73 @@ abstract class Horde_View_Base unset($name); ob_start(); - $this->_run($this->_file); + $this->_run($this->_file, $locals); return ob_get_clean(); } /** + * Render a partial template. Partial template filenames are named with a + * leading underscore, although this underscore is not used when specifying + * the name of the partial. + * + * we would reference the file /views/shared/_sidebarInfo.html in our + * template using: + * + * + *
+ * renderPartial('sidebarInfo') ?> + *
+ *
+ * + * @param string $name + * @param array $options + * + * @return string The template output + */ + public function renderPartial($name, $options = array()) + { + // pop name off of the path + $parts = strstr($name, '/') ? explode('/', $name) : array($name); + $name = array_pop($parts); + $path = implode('/', $parts)."/"; + + // check if they passed in a collection before validating keys + $useCollection = array_key_exists('collection', $options); + + $valid = array('object' => null, 'locals' => array(), 'collection' => array()); + $options = array_merge($valid, $options); + $locals = array($name => null); + + // set the object variable + if ($options['object']) { + $locals[$name] = $options['object']; + } + + // set local variables to be used in the partial + foreach ($options['locals'] as $key => $val) { + $locals[$key] = $val; + } + + // collection + if ($useCollection) { + $rendered = ''; + if (is_array($options['collection'])) { + $sz = count($options['collection']); + for ($i = 0; $i < $sz; $i++) { + $locals["{$name}Counter"] = $i; + $locals[$name] = $options['collection'][$i]; + $rendered .= $this->render("{$path}_{$name}", $locals); + } + } + + // single render + } else { + $rendered = $this->render("{$path}_{$name}", $locals); + } + return $rendered; + } + + /** * Escapes a value for output in a template. * * If escaping mechanism is one of htmlspecialchars or htmlentities, uses @@ -291,6 +369,9 @@ abstract class Horde_View_Base */ protected function _template($name) { + // append missing html + if (!strstr($name, '.')) { $name .= '.html'; } + if (!count($this->_templatePath)) { throw new Horde_View_Exception('No template directory set; unable to locate ' . $name); } @@ -308,7 +389,10 @@ abstract class Horde_View_Base * Use to include the template in a scope that only allows public * members. * - * @return mixed + * @param string The template to execute. Not declared in the + * function signature so it stays out of the view's public scope. + * + * @param array Any local variables to declare. */ abstract protected function _run(); diff --git a/framework/View/test/Horde/View/BaseTest.php b/framework/View/test/Horde/View/BaseTest.php new file mode 100644 index 000000000..ae8d30199 --- /dev/null +++ b/framework/View/test/Horde/View/BaseTest.php @@ -0,0 +1,225 @@ + + * @author Derek DeVries + * @author Chuck Hagenbuch + * @license http://opensource.org/licenses/bsd-license.php + * @category Horde + * @package Horde_View + * @subpackage UnitTests + */ + +/** + * @group view + * @author Mike Naberezny + * @author Derek DeVries + * @author Chuck Hagenbuch + * @license http://opensource.org/licenses/bsd-license.php + * @category Horde + * @package Horde_View + * @subpackage UnitTests + */ +class Horde_View_BaseTest extends Horde_Test_Case +{ + protected $_view = null; + + public function setUp() + { + $this->_view = new Horde_View(); + $this->_view->addTemplatePath(dirname(__FILE__) . '/fixtures/'); + } + + /*########################################################################## + # Assignment + ##########################################################################*/ + + // test setting/getting dynamic properties + public function testSet() + { + $this->_view->publicVar = 'test'; + $this->assertEquals('test', $this->_view->publicVar); + } + + // test accessing variable + public function testAccessVar() + { + $this->_view->testVar = 'test'; + $this->assertTrue(!empty($this->_view->testVar)); + + $this->_view->testVar2 = ''; + $this->assertTrue(empty($this->_view->testVar2)); + + $this->assertTrue(isset($this->_view->testVar2)); + $this->assertTrue(!isset($this->_view->testVar3)); + } + + // test adding a template path + public function testAddTemplatePath() + { + $this->_view->addTemplatePath('app/views/shared/'); + + $expected = array('app/views/shared/', + dirname(__FILE__) . '/fixtures/', + './'); + $this->assertEquals($expected, $this->_view->getTemplatePaths()); + } + + // test adding a template path + public function testAddTemplatePathAddSlash() + { + $this->_view->addTemplatePath('app/views/shared'); + $expected = array('app/views/shared/', + dirname(__FILE__) . '/fixtures/', + './'); + $this->assertEquals($expected, $this->_view->getTemplatePaths()); + } + + + /*########################################################################## + # Rendering + ##########################################################################*/ + + // test rendering + public function testRender() + { + $this->_view->myVar = 'test'; + + $expected = "
test
"; + $this->assertEquals($expected, $this->_view->render('testRender.html')); + } + + // test rendering + public function testRenderNoExtension() + { + $this->_view->myVar = 'test'; + + $expected = "
test
"; + $this->assertEquals($expected, $this->_view->render('testRender')); + } + + // test that the + public function testRenderPathOrder() + { + $this->_view->myVar = 'test'; + + // we should be rendering the testRender.html in fixtures/ + $expected = "
test
"; + $this->assertEquals($expected, $this->_view->render('testRender')); + + // after we specify the 'subdir' path, it should read from subdir path first + $this->_view->addTemplatePath(dirname(__FILE__) . '/fixtures/subdir/'); + $expected = "
subdir test
"; + $this->assertEquals($expected, $this->_view->render('testRender')); + } + + + /*########################################################################## + # Partials + ##########################################################################*/ + + // test rendering partial + public function testRenderPartial() + { + $this->_view->myVar1 = 'main'; + $this->_view->myVar2 = 'partial'; + + $expected = '
main

partial

'; + $this->assertEquals($expected, $this->_view->render('testPartial')); + } + + // test rendering partial with object passed in + public function testRenderPartialObject() + { + $this->_view->myObject = (object)array('string_value' => 'hello world'); + $expected = '

hello world

'; + $this->assertEquals($expected, $this->_view->render('testPartialObject')); + } + + // test rendering partial with locals passed in + public function testRenderPartialLocals() + { + $expected = '

hello world

'; + $this->assertEquals($expected, $this->_view->render('testPartialLocals')); + } + + // test rendering partial with collection passed in + public function testRenderPartialCollection() + { + $this->_view->myObjects = array((object)array('string_value' => 'hello'), + (object)array('string_value' => 'world')); + $expected = '

hello

world

'; + $this->assertEquals($expected, $this->_view->render('testPartialCollection')); + } + + // test rendering partial with empty set as collection + public function testRenderPartialCollectionEmpty() + { + $this->_view->myObjects = null; + + $expected = '
'; + $this->assertEquals($expected, $this->_view->render('testPartialCollection')); + } + + // test rendering partial with empty array as collection + public function testRenderPartialCollectionEmptyArray() + { + $this->_view->myObjects = array(); + + $expected = '
'; + $this->assertEquals($expected, $this->_view->render('testPartialCollection')); + } + + // partial collection is a model collection + public function testRenderPartialModelCollection() + { + $this->_view->myObjects = array((object)array('string_value' => 'name a'), (object)array('string_value' => 'name b')); + + $expected = '

name a

name b

'; + $this->assertEquals($expected, $this->_view->render('testPartialCollection')); + } + + + /*########################################################################## + # Escape output + ##########################################################################*/ + + public function testEscapeTemplate() + { + $this->_view->myVar = '"escaping"'; + $this->_view->addHelper(new Horde_View_Helper_Text($this->_view)); + + $expected = "
test "escaping" quotes
"; + $this->assertEquals($expected, $this->_view->render('testEscape')); + } + + // test adding a helper + public function testAddHorde_View_Helper_Text() + { + $str = 'The quick brown fox jumps over the lazy dog tomorrow morning.'; + + // helper doesn't exist + try { + $this->_view->truncateMiddle($str, 40); + } catch (Exception $e) {} + $this->assertTrue($e instanceof Horde_View_Exception); + + // add text helper + $this->_view->addHelper(new Horde_View_Helper_Text($this->_view)); + $expected = 'The quick brown fox... tomorrow morning.'; + $this->assertEquals($expected, $this->_view->truncateMiddle($str, 40)); + } + + // test adding a helper where methods conflict + public function testAddHorde_View_Helper_TextMethodOverwrite() + { + // add text helper + $this->_view->addHelper(new Horde_View_Helper_Text($this->_view)); + + // sucessfull when trying to add it again + $this->_view->addHelper(new Horde_View_Helper_Text($this->_view)); + } + +} diff --git a/framework/View/test/Horde/View/fixtures/_myPartial.html b/framework/View/test/Horde/View/fixtures/_myPartial.html new file mode 100644 index 000000000..f374d0e0e --- /dev/null +++ b/framework/View/test/Horde/View/fixtures/_myPartial.html @@ -0,0 +1 @@ +

myVar2 ?>

\ No newline at end of file diff --git a/framework/View/test/Horde/View/fixtures/_myPartialLocals.html b/framework/View/test/Horde/View/fixtures/_myPartialLocals.html new file mode 100644 index 000000000..74607ebb8 --- /dev/null +++ b/framework/View/test/Horde/View/fixtures/_myPartialLocals.html @@ -0,0 +1 @@ +

\ No newline at end of file diff --git a/framework/View/test/Horde/View/fixtures/_myPartialObject.html b/framework/View/test/Horde/View/fixtures/_myPartialObject.html new file mode 100644 index 000000000..e2db662fb --- /dev/null +++ b/framework/View/test/Horde/View/fixtures/_myPartialObject.html @@ -0,0 +1 @@ +

string_value ?>

\ No newline at end of file diff --git a/framework/View/test/Horde/View/fixtures/subdir/testRender.html b/framework/View/test/Horde/View/fixtures/subdir/testRender.html new file mode 100644 index 000000000..5ecca9358 --- /dev/null +++ b/framework/View/test/Horde/View/fixtures/subdir/testRender.html @@ -0,0 +1 @@ +
subdir myVar ?>
\ No newline at end of file diff --git a/framework/View/test/Horde/View/fixtures/testEscape.html b/framework/View/test/Horde/View/fixtures/testEscape.html new file mode 100644 index 000000000..f56541960 --- /dev/null +++ b/framework/View/test/Horde/View/fixtures/testEscape.html @@ -0,0 +1 @@ +
test h($this->myVar) ?> quotes
\ No newline at end of file diff --git a/framework/View/test/Horde/View/fixtures/testPartial.html b/framework/View/test/Horde/View/fixtures/testPartial.html new file mode 100644 index 000000000..bbf96b6dd --- /dev/null +++ b/framework/View/test/Horde/View/fixtures/testPartial.html @@ -0,0 +1 @@ +
myVar1 ?>renderPartial('myPartial') ?>
\ No newline at end of file diff --git a/framework/View/test/Horde/View/fixtures/testPartialCollection.html b/framework/View/test/Horde/View/fixtures/testPartialCollection.html new file mode 100644 index 000000000..cc3b3a417 --- /dev/null +++ b/framework/View/test/Horde/View/fixtures/testPartialCollection.html @@ -0,0 +1 @@ +
renderPartial('myPartialObject', array('collection' => $this->myObjects)) ?>
\ No newline at end of file diff --git a/framework/View/test/Horde/View/fixtures/testPartialLocals.html b/framework/View/test/Horde/View/fixtures/testPartialLocals.html new file mode 100644 index 000000000..e6cc6446f --- /dev/null +++ b/framework/View/test/Horde/View/fixtures/testPartialLocals.html @@ -0,0 +1 @@ +
renderPartial('myPartialLocals', array('locals' => array('a' => 'hello', 'b' => 'world'))) ?>
\ No newline at end of file diff --git a/framework/View/test/Horde/View/fixtures/testPartialObject.html b/framework/View/test/Horde/View/fixtures/testPartialObject.html new file mode 100644 index 000000000..d61d7ec42 --- /dev/null +++ b/framework/View/test/Horde/View/fixtures/testPartialObject.html @@ -0,0 +1 @@ +
renderPartial('myPartialObject', array('object' => $this->myObject)) ?>
\ No newline at end of file diff --git a/framework/View/test/Horde/View/fixtures/testRender.html b/framework/View/test/Horde/View/fixtures/testRender.html new file mode 100644 index 000000000..b764885ee --- /dev/null +++ b/framework/View/test/Horde/View/fixtures/testRender.html @@ -0,0 +1 @@ +
myVar ?>
\ No newline at end of file -- 2.11.0