- add helper base test
authorChuck Hagenbuch <chuck@horde.org>
Mon, 23 Feb 2009 04:21:26 +0000 (23:21 -0500)
committerChuck Hagenbuch <chuck@horde.org>
Mon, 23 Feb 2009 04:21:26 +0000 (23:21 -0500)
- add getHelperPaths
- add local variables in views
- add partials
- add .html to template names with no extension

13 files changed:
framework/View/lib/Horde/View.php
framework/View/lib/Horde/View/Base.php
framework/View/test/Horde/View/BaseTest.php [new file with mode: 0644]
framework/View/test/Horde/View/fixtures/_myPartial.html [new file with mode: 0644]
framework/View/test/Horde/View/fixtures/_myPartialLocals.html [new file with mode: 0644]
framework/View/test/Horde/View/fixtures/_myPartialObject.html [new file with mode: 0644]
framework/View/test/Horde/View/fixtures/subdir/testRender.html [new file with mode: 0644]
framework/View/test/Horde/View/fixtures/testEscape.html [new file with mode: 0644]
framework/View/test/Horde/View/fixtures/testPartial.html [new file with mode: 0644]
framework/View/test/Horde/View/fixtures/testPartialCollection.html [new file with mode: 0644]
framework/View/test/Horde/View/fixtures/testPartialLocals.html [new file with mode: 0644]
framework/View/test/Horde/View/fixtures/testPartialObject.html [new file with mode: 0644]
framework/View/test/Horde/View/fixtures/testRender.html [new file with mode: 0644]

index d7820ed..5827dca 100644 (file)
@@ -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);
index 8eff39f..34a13ca 100644 (file)
@@ -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:
+     *
+     * <code>
+     *   <div>
+     *   <?= $this->renderPartial('sidebarInfo') ?>
+     *   </div>
+     * </code>
+     *
+     * @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 (file)
index 0000000..ae8d301
--- /dev/null
@@ -0,0 +1,225 @@
+<?php
+/**
+ * Copyright 2007-2008 Maintainable Software, LLC
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @license    http://opensource.org/licenses/bsd-license.php
+ * @category   Horde
+ * @package    Horde_View
+ * @subpackage UnitTests
+ */
+
+/**
+ * @group      view
+ * @author     Mike Naberezny <mike@maintainable.com>
+ * @author     Derek DeVries <derek@maintainable.com>
+ * @author     Chuck Hagenbuch <chuck@horde.org>
+ * @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 = "<div>test</div>";
+        $this->assertEquals($expected, $this->_view->render('testRender.html'));
+    }
+
+    // test rendering
+    public function testRenderNoExtension()
+    {
+        $this->_view->myVar = 'test';
+
+        $expected = "<div>test</div>";
+        $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 = "<div>test</div>";
+        $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 = "<div>subdir test</div>";
+        $this->assertEquals($expected, $this->_view->render('testRender'));
+    }
+
+
+    /*##########################################################################
+    # Partials
+    ##########################################################################*/
+
+    // test rendering partial
+    public function testRenderPartial()
+    {
+        $this->_view->myVar1 = 'main';
+        $this->_view->myVar2 = 'partial';
+
+        $expected = '<div>main<p>partial</p></div>';
+        $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 = '<div><p>hello world</p></div>';
+        $this->assertEquals($expected, $this->_view->render('testPartialObject'));
+    }
+
+    // test rendering partial with locals passed in
+    public function testRenderPartialLocals()
+    {
+        $expected = '<div><p>hello world</p></div>';
+        $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 = '<div><p>hello</p><p>world</p></div>';
+        $this->assertEquals($expected, $this->_view->render('testPartialCollection'));
+    }
+
+    // test rendering partial with empty set as collection
+    public function testRenderPartialCollectionEmpty()
+    {
+        $this->_view->myObjects = null;
+
+        $expected = '<div></div>';
+        $this->assertEquals($expected, $this->_view->render('testPartialCollection'));
+    }
+
+    // test rendering partial with empty array as collection
+    public function testRenderPartialCollectionEmptyArray()
+    {
+        $this->_view->myObjects = array();
+
+        $expected = '<div></div>';
+        $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 = '<div><p>name a</p><p>name b</p></div>';
+        $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 = "<div>test &quot;escaping&quot; quotes</div>";
+        $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 (file)
index 0000000..f374d0e
--- /dev/null
@@ -0,0 +1 @@
+<p><?= $this->myVar2 ?></p>
\ 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 (file)
index 0000000..74607eb
--- /dev/null
@@ -0,0 +1 @@
+<p><?= $a ?> <?= $b ?></p>
\ 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 (file)
index 0000000..e2db662
--- /dev/null
@@ -0,0 +1 @@
+<p><?= $myPartialObject->string_value ?></p>
\ 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 (file)
index 0000000..5ecca93
--- /dev/null
@@ -0,0 +1 @@
+<div>subdir <?= $this->myVar ?></div>
\ 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 (file)
index 0000000..f565419
--- /dev/null
@@ -0,0 +1 @@
+<div>test <?= $this->h($this->myVar) ?> quotes</div>
\ 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 (file)
index 0000000..bbf96b6
--- /dev/null
@@ -0,0 +1 @@
+<div><?= @$this->myVar1 ?><?= $this->renderPartial('myPartial') ?></div>
\ 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 (file)
index 0000000..cc3b3a4
--- /dev/null
@@ -0,0 +1 @@
+<div><?= $this->renderPartial('myPartialObject', array('collection' => $this->myObjects)) ?></div>
\ 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 (file)
index 0000000..e6cc644
--- /dev/null
@@ -0,0 +1 @@
+<div><?= $this->renderPartial('myPartialLocals', array('locals' => array('a' => 'hello', 'b' => 'world'))) ?></div>
\ 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 (file)
index 0000000..d61d7ec
--- /dev/null
@@ -0,0 +1 @@
+<div><?= $this->renderPartial('myPartialObject', array('object' => $this->myObject)) ?></div>
\ 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 (file)
index 0000000..b764885
--- /dev/null
@@ -0,0 +1 @@
+<div><?= $this->myVar ?></div>
\ No newline at end of file