import Horde_View
authorChuck Hagenbuch <chuck@horde.org>
Fri, 13 Feb 2009 15:53:38 +0000 (10:53 -0500)
committerChuck Hagenbuch <chuck@horde.org>
Fri, 13 Feb 2009 15:53:38 +0000 (10:53 -0500)
12 files changed:
framework/View/examples/Horde/View/template.php [new file with mode: 0644]
framework/View/examples/Horde/View/view.php [new file with mode: 0644]
framework/View/lib/Horde/View.php [new file with mode: 0644]
framework/View/lib/Horde/View/Base.php [new file with mode: 0644]
framework/View/lib/Horde/View/Exception.php [new file with mode: 0644]
framework/View/lib/Horde/View/Helper.php [new file with mode: 0644]
framework/View/lib/Horde/View/Helper/Block.php [new file with mode: 0644]
framework/View/lib/Horde/View/Helper/Url.php [new file with mode: 0644]
framework/View/lib/Horde/View/Interface.php [new file with mode: 0644]
framework/View/package.xml [new file with mode: 0644]
framework/View/test/Horde/View/AllTests.php [new file with mode: 0644]
framework/View/test/Horde/View/InterfaceTest.php [new file with mode: 0644]

diff --git a/framework/View/examples/Horde/View/template.php b/framework/View/examples/Horde/View/template.php
new file mode 100644 (file)
index 0000000..77afb5f
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+/**
+ * @package Horde_View
+ */
+
+if ($this->books):
+?>
+
+<!-- A table of some books. -->
+<table>
+    <tr>
+        <th>Author</th>
+        <th>Title</th>
+    </tr>
+
+<?php foreach ($this->books as $key => $val): ?>
+    <tr>
+<td><?php echo $this->escape($val['author']) ?></td>
+<td><?php echo $this->escape($val['title']) ?></td>
+    </tr>
+<?php endforeach; ?>
+
+</table>
+
+<?php else: ?>
+    <p>There are no books to display.</p>
+<?php endif; ?>
diff --git a/framework/View/examples/Horde/View/view.php b/framework/View/examples/Horde/View/view.php
new file mode 100644 (file)
index 0000000..fc62625
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+/**
+ * @package Horde_View
+ */
+
+require 'Horde/Autoloader.php';
+
+// use a model to get the data for book authors and titles.
+$data = array(
+              array(
+                    'author' => 'Hernando de Soto',
+        'title' => 'The Mystery of Capitalism'
+                    ),
+              array(
+                    'author' => 'Henry Hazlitt',
+        'title' => 'Economics in One Lesson'
+                    ),
+              array(
+                    'author' => 'Milton Friedman',
+        'title' => 'Free to Choose'
+                    )
+              );
+
+$view = new Horde_View;
+$view->books = $data;
+
+// and render a template called "template.php"
+echo $view->render('template.php');
diff --git a/framework/View/lib/Horde/View.php b/framework/View/lib/Horde/View.php
new file mode 100644 (file)
index 0000000..d7820ed
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+/**
+ * @category Horde
+ * @package Horde_View
+ */
+
+/**
+ * Concrete class for handling views.
+ *
+ * @category Horde
+ * @package Horde_View
+ */
+class Horde_View extends Horde_View_Base
+{
+    /**
+     * Includes the template in a scope with only public variables.
+     *
+     * @param string The template to execute. Not declared in the
+     * function signature so it stays out of the view's public scope.
+     */
+    protected function _run()
+    {
+        $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
new file mode 100644 (file)
index 0000000..32ef964
--- /dev/null
@@ -0,0 +1,269 @@
+<?php
+/**
+ * @category Horde
+ * @package Horde_View
+ */
+
+/**
+ * Abstract base class for Horde_View to get private constructs out of
+ * template scope.
+ *
+ * @category Horde
+ * @package Horde_View
+ */
+abstract class Horde_View_Base
+{
+    /**
+     * Path stack for templates.
+     *
+     * @var array
+     */
+    private $_templatePath = array('./');
+
+    /**
+     * Template to execute. Stored in a private variable to keep it
+     * out of the public view scope.
+     *
+     * @var string
+     */
+    private $_file = null;
+
+    /**
+     * Cache of helper objects.
+     *
+     * @var array
+     */
+    private $_helpers = array();
+
+    /**
+     * Callback for escaping.
+     *
+     * @var string
+     */
+    private $_escape = 'htmlspecialchars';
+
+    /**
+     * Encoding to use in escaping mechanisms; defaults to UTF-8.
+     * @var string
+     */
+    private $_encoding = 'UTF-8';
+
+    /**
+     * Constructor.
+     *
+     * @param array $config Configuration key-value pairs.
+     */
+    public function __construct($config = array())
+    {
+        // user-defined escaping callback
+        if (!empty($config['escape'])) {
+            $this->setEscape($config['escape']);
+        }
+
+        // encoding
+        if (!empty($config['encoding'])) {
+            $this->setEncoding($config['encoding']);
+        }
+
+        // user-defined template path
+        if (!empty($config['templatePath'])) {
+            $this->addTemplatePath($config['templatePath']);
+        }
+    }
+
+    /**
+     * Return a view variable
+     *
+     * @param string $name Variable name to retrieve
+     */
+    public function __get($name)
+    {
+        return isset($this->name) ? $this->name : '';
+    }
+
+    /**
+     * Assign a single view variable
+     *
+     * @param string $name Variable name to set
+     * @param mixed $value The value of $name
+     */
+    public function __set($name, $value)
+    {
+        $this->$name = $value;
+    }
+
+    /**
+     * Accesses a helper object from within a template.
+     *
+     * @param string $method The helper method.
+     * @param array $args The parameters for the helper.
+     *
+     * @return string The result of the helper method.
+     */
+    public function __call($method, $args)
+    {
+        if (isset($this->_helpers[$method])) {
+            return call_user_func_array(array($this->_helpers[$method], $method), $args);
+        }
+
+        throw new Horde_View_Exception('Helper for ' . $method . ' not found.');
+    }
+
+    /**
+     * Adds to the stack of template paths in LIFO order.
+     *
+     * @param string|array The directory (-ies) to add.
+     */
+    public function addTemplatePath($path)
+    {
+        foreach ((array)$path as $dir) {
+            // Attempt to strip any possible separator and append the
+            // system directory separator.
+            $dir = rtrim($dir, '\\/' . DIRECTORY_SEPARATOR)
+                . DIRECTORY_SEPARATOR;
+
+            // Add to the top of the stack.
+            array_unshift($this->_templatePath, $dir);
+        }
+    }
+
+    /**
+     * Resets the stack of template paths.
+     *
+     * To clear all paths, use Horde_View::setTemplatePath(null).
+     *
+     * @param string|array The directory (-ies) to set as the path.
+     */
+    public function setTemplatePath($path)
+    {
+        $this->_templatePath = array();
+        $this->addTemplatePath($path);
+    }
+
+    /**
+     * Adds to the stack of helpers in LIFO order.
+     *
+     * @param Horde_View_Helper $helper The helper instance to add.
+     */
+    public function addHelper($helper)
+    {
+        foreach (get_class_methods($helper) as $method) {
+            $this->_helpers[$method] = $helper;
+        }
+    }
+
+    /**
+     * Sets the escape() callback.
+     *
+     * @param mixed $spec The callback for escape() to use.
+     */
+    public function setEscape($spec)
+    {
+        $this->_escape = $spec;
+    }
+
+    /**
+     * Assigns multiple variables to the view.
+     *
+     * The array keys are used as names, each assigned their
+     * corresponding array value.
+     *
+     * @param array $array The array of key/value pairs to assign.
+     *
+     * @see __set()
+     */
+    public function assign($array)
+    {
+        foreach ($array as $key => $val) {
+            $this->$key = $val;
+        }
+    }
+
+    /**
+     * Processes a template and returns the output.
+     *
+     * @param string $name The template to process.
+     *
+     * @return string The template output.
+     */
+    public function render($name)
+    {
+        // Find the template file name.
+        $this->_file = $this->_template($name);
+
+        // remove $name from local scope
+        unset($name);
+
+        ob_start();
+        $this->_run($this->_file);
+        return ob_get_clean();
+    }
+
+    /**
+     * Escapes a value for output in a template.
+     *
+     * If escaping mechanism is one of htmlspecialchars or htmlentities, uses
+     * {@link $_encoding} setting.
+     *
+     * @param mixed $var The output to escape.
+     *
+     * @return mixed The escaped value.
+     */
+    public function escape($var)
+    {
+        if (in_array($this->_escape, array('htmlspecialchars', 'htmlentities'))) {
+            return call_user_func($this->_escape, $var, ENT_QUOTES, $this->_encoding);
+        }
+
+        return call_user_func($this->_escape, $var);
+    }
+
+    /**
+     * Set encoding to use with htmlentities() and htmlspecialchars()
+     *
+     * @param string $encoding
+     */
+    public function setEncoding($encoding)
+    {
+        $this->_encoding = $encoding;
+    }
+
+    /**
+     * Return current escape encoding
+     *
+     * @return string
+     */
+    public function getEncoding()
+    {
+        return $this->_encoding;
+    }
+
+    /**
+     * Finds a template from the available directories.
+     *
+     * @param $name string The base name of the template.
+     */
+    protected function _template($name)
+    {
+        if (!count($this->_templatePath)) {
+            throw new Horde_View_Exception('No template directory set; unable to locate ' . $name);
+        }
+
+        foreach ($this->_templatePath as $dir) {
+            if (is_readable($dir . $name)) {
+                return $dir . $name;
+            }
+        }
+
+        throw new Horde_View_Exception("\"$name\" not found in template path (\"" . implode(':', $this->_templatePath) . '")');
+    }
+
+    /**
+     * Use to include the template in a scope that only allows public
+     * members.
+     *
+     * @return mixed
+     */
+    abstract protected function _run();
+
+}
diff --git a/framework/View/lib/Horde/View/Exception.php b/framework/View/lib/Horde/View/Exception.php
new file mode 100644 (file)
index 0000000..9f2d56a
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+/**
+ * @category Horde
+ * @package Horde_View
+ */
+
+/**
+ * @category Horde
+ * @package Horde_View
+ */
+class Horde_View_Exception extends Exception
+{
+}
diff --git a/framework/View/lib/Horde/View/Helper.php b/framework/View/lib/Horde/View/Helper.php
new file mode 100644 (file)
index 0000000..d4a9577
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+/**
+ * @category Horde
+ * @package Horde_View
+ */
+
+/**
+ * Abstract class for Horde_View_Helper objects.
+ *
+ * @category Horde
+ * @package Horde_View
+ */
+abstract class Horde_View_Helper
+{
+    /**
+     * The parent view invoking the helper
+     *
+     * @var Horde_View
+     */
+    protected $_view;
+
+    /**
+     * Create a helper for $view
+     *
+     * @param Horde_View $view The view to help.
+     */
+    public function __construct($view)
+    {
+        $this->_view = $view;
+        $view->addHelper($this);
+    }
+
+    /**
+     * Call chaining so other helpers can be called transparently.
+     *
+     * @param string $method The helper method.
+     * @param array $args The parameters for the helper.
+     *
+     * @return string The result of the helper method.
+     */
+    public function __call($method, $args)
+    {
+        return call_user_func_array(array($this->_view, $method), $args);
+    }
+
+}
diff --git a/framework/View/lib/Horde/View/Helper/Block.php b/framework/View/lib/Horde/View/Helper/Block.php
new file mode 100644 (file)
index 0000000..f087843
--- /dev/null
@@ -0,0 +1,129 @@
+<?php
+/**
+ * $Horde: framework/View/lib/Horde/View/Helper/Block.php,v 1.2 2008/10/09 02:43:53 chuck Exp $
+ *
+ * @category Horde
+ * @package Horde_View
+ * @subpackage Helpers
+ */
+
+/**
+ * View helper for displaying Horde_Block objects
+ *
+ * @category Horde
+ * @package Horde_View
+ * @subpackage Helpers
+ */
+class Horde_View_Helper_Block extends Horde_View_Helper
+{
+    /**
+     * Blocks that have already been fetched.
+     *
+     * @var array
+     */
+    protected $_blockCache = array();
+
+    /**
+     * Return the title of the specified block.
+     *
+     * @param string $app   The application the block is from.
+     * @param string $block The name of the block to get the title for.
+     * @param mixed $arg1   (optional) The first argument to the Block constructor.
+     * @param mixed $arg2   (optional) The first argument to the Block constructor.
+     * @param mixed $arg3   (optional) The first argument to the Block constructor.
+     *
+     * ...
+     *
+     * @return string The requested Block's title.
+     *
+     * @throws Horde_View_Exception, InvalidArgumentException
+     */
+    public function blockTitle()
+    {
+        $args = func_get_args();
+        list($app, $block, $params) = $this->_args($args);
+
+        return $this->_block($app, $block, $params)->getTitle();
+    }
+
+    /**
+     * Return the content of the specified block.
+     *
+     * @param string $app   The application the block is from.
+     * @param string $block The name of the block to get the content for.
+     * @param mixed $arg1   (optional) The first argument to the Block constructor.
+     * @param mixed $arg2   (optional) The first argument to the Block constructor.
+     * @param mixed $arg3   (optional) The first argument to the Block constructor.
+     *
+     * ...
+     *
+     * @return string The requested Block's content.
+     *
+     * @throws Horde_View_Exception, InvalidArgumentException
+     */
+    public function blockContent()
+    {
+        $args = func_get_args();
+        list($app, $block, $params) = $this->_args($args);
+
+        return $this->_block($app, $block, $params)->getContent();
+    }
+
+    /**
+     * Instantiate and cache Block objects
+     *
+     * @param string $app   The application the block is from.
+     * @param string $block The name of the block to fetch.
+     * @param array $params (option) Any arguments to the Block constructor.
+     *
+     * ...
+     *
+     * @return Horde_Block The requested Block object
+     *
+     * @throws Horde_View_Exception, InvalidArgumentException
+     */
+    protected function _block($app, $block, $params)
+    {
+        $hash = sha1(serialize(array($app, $block, $params)));
+        if (!isset($this->_blockCache[$hash])) {
+            $block = Horde_Block_Collection::getBlock($app, $block, $params);
+            if (!$block instanceof Horde_Block) {
+                if (is_callable(array($block, 'getMessage'))) {
+                    throw new Horde_View_Exception($block->getMessage());
+                } else {
+                    throw new Horde_View_Exception('Unknown error instantiating Block object');
+                }
+            }
+
+            $this->_blockCache[$hash] = $block;
+        }
+
+        return $this->_blockCache[$hash];
+    }
+
+    /**
+     * Parse any argument style for the Block-fetching functions
+     *
+     * @param array $args
+     */
+    protected function _args($args)
+    {
+        $argc = count($args);
+
+        if ($argc == 1) {
+            if (is_array($args[0])) {
+                $args = $args[0];
+                $argc = count($args);
+            }
+        }
+
+        if ($argc < 2) {
+            throw new InvalidArgumentException('You must provide at least an application name and a block name.');
+        }
+        $app = array_shift($args);
+        $block = array_shift($args);
+
+        return array($app, $block, $args);
+    }
+
+}
diff --git a/framework/View/lib/Horde/View/Helper/Url.php b/framework/View/lib/Horde/View/Helper/Url.php
new file mode 100644 (file)
index 0000000..27e449e
--- /dev/null
@@ -0,0 +1,179 @@
+<?php
+/**
+ * $Horde: framework/View/lib/Horde/View/Helper/Url.php,v 1.2 2008/10/09 02:43:53 chuck Exp $
+ *
+ * @category Horde
+ * @package Horde_View
+ * @subpackage Helpers
+ */
+
+/**
+ * View helper for URLs
+ *
+ * @category Horde
+ * @package Horde_View
+ * @subpackage Helpers
+ */
+class Horde_View_Helper_Url extends Horde_View_Helper
+{
+    /**
+     * Creates a link tag of the given +name+ using a URL created by the set
+     * of +options+. See the valid options in the documentation for
+     * url_for. It's also possible to pass a string instead
+     * of an options hash to get a link tag that uses the value of the string as the
+     * href for the link, or use +:back+ to link to the referrer - a JavaScript back
+     * link will be used in place of a referrer if none exists. If nil is passed as
+     * a name, the link itself will become the name.
+     *
+     * ==== Options
+     * * <tt>:confirm => 'question?'</tt> -- This will add a JavaScript confirm
+     *   prompt with the question specified. If the user accepts, the link is
+     *   processed normally, otherwise no action is taken.
+     * * <tt>:popup => true || array of window options</tt> -- This will force the
+     *   link to open in a popup window. By passing true, a default browser window
+     *   will be opened with the URL. You can also specify an array of options
+     *   that are passed-thru to JavaScripts window.open method.
+     * * <tt>:method => symbol of HTTP verb</tt> -- This modifier will dynamically
+     *   create an HTML form and immediately submit the form for processing using
+     *   the HTTP verb specified. Useful for having links perform a POST operation
+     *   in dangerous actions like deleting a record (which search bots can follow
+     *   while spidering your site). Supported verbs are :post, :delete and :put.
+     *   Note that if the user has JavaScript disabled, the request will fall back
+     *   to using GET. If you are relying on the POST behavior, you should check
+     *   for it in your controller's action by using the request object's methods
+     *   for post?, delete? or put?.
+     * * The +html_options+ will accept a hash of html attributes for the link tag.
+     *
+     * Note that if the user has JavaScript disabled, the request will fall back
+     * to using GET. If :href=>'#' is used and the user has JavaScript disabled
+     * clicking the link will have no effect. If you are relying on the POST
+     * behavior, your should check for it in your controller's action by using the
+     * request object's methods for post?, delete? or put?.
+     */
+    public function linkTo($name, $url, $htmlOptions = array())
+    {
+        if ($htmlOptions) {
+            $href = isset($htmlOptions['href']) ? $htmlOptions['href'] : null;
+            // @todo convert_otpions_to_javascript!(html_options, url)
+            $tagOptions = $this->tagOptions($htmlOptions);
+        } else {
+            $tagOptions = null;
+        }
+
+        $hrefAttr = isset($href) ? null : 'href="' . $url . '"';
+        $nameOrUrl = isset($name) ? $name : $url;
+        return '<a ' . $hrefAttr . $tagOptions . '>' . $this->escape($nameOrUrl) . '</a>';
+    }
+
+    /**
+     * Creates a link tag of the given $name using $url unless the current
+     * request URI is the same as the links, in which case only the name is
+     * returned.
+     */
+    public function linkToUnlessCurrent($name, $url, $htmlOptions = array())
+    {
+        return $this->linkToUnless($this->isCurrentPage($url),
+                                   $name, $url, $htmlOptions);
+    }
+
+    /**
+     * Creates a link tag of the given +name+ using a URL created by the set of
+     * +options+ unless +condition+ is true, in which case only the name is
+     * returned. To specialize the default behavior (i.e., show a login link rather
+     * than just the plaintext link text), you can pass a block that
+     * accepts the name or the full argument list for link_to_unless.
+     */
+    public function linkToUnless($condition, $name, $url, $htmlOptions = array())
+    {
+        return $condition ? $name : $this->linkTo($name, $url, $htmlOptions);
+    }
+
+    /**
+     * Creates a link tag of the given +name+ using a URL created by the set of
+     * +options+ if +condition+ is true, in which case only the name is
+     * returned. To specialize the default behavior, you can pass a block that
+     * accepts the name or the full argument list for link_to_unless (see the examples
+     * in link_to_unless).
+     */
+    public function linkToIf($condition, $name, $url, $htmlOptions = array())
+    {
+        return $this->linkToUnless(!$condition, $name, $url, $htmlOptions);
+    }
+
+    /**
+     * True if the current request URI is the same as the current URL.
+     *
+     * @TODO Get REQUEST_URI from somewhere other than the global environment.
+     */
+    public function isCurrentPage($url)
+    {
+        return $url == $_SERVER['REQUEST_URI'];
+    }
+
+    // @TODO Move these methods to a generic HTML/Tag helper
+
+    /**
+     * HTML attributes that get converted from boolean to the attribute name:
+     * array('disabled' => true) becomes array('disabled' => 'disabled')
+     *
+     * @var array
+     */
+    private $_booleanAttributes = array('disabled', 'readonly', 'multiple', 'selected', 'checked');
+
+    /**
+     * Converts an associative array of $options into
+     * a string of HTML attributes
+     *
+     * @param  array  $options  key/value pairs
+     * @param  string           key1="value1" key2="value2"
+     */
+    public function tagOptions($options)
+    {
+        foreach ($options as $k => &$v) {
+            if ($v === null || $v === false) {
+                unset($options[$k]);
+            } else {
+                if (in_array($k, $this->_booleanAttributes)) {
+                    $v = $k;
+                }
+            }
+        }
+
+        if (!empty($options)) {
+            foreach ($options as $k => &$v) {
+                $v = $k . '="' . $this->escapeOnce($v) . '"';
+            }
+            sort($options);
+            return ' ' . implode(' ', $options);
+        } else {
+            return '';
+        }
+    }
+
+    /**
+     * Returns the escaped $html without affecting existing escaped entities.
+     *
+     *   $this->escapeOnce("1 > 2 &amp; 3")
+     *     => "1 &lt; 2 &amp; 3"
+     *
+     * @param  string  $html    HTML to be escaped
+     *
+     * @return string           Escaped HTML without affecting existing escaped entities
+     */
+    public function escapeOnce($html)
+    {
+        return $this->_fixDoubleEscape(htmlspecialchars($html, ENT_QUOTES, $this->getEncoding()));
+    }
+
+    /**
+     * Fix double-escaped entities, such as &amp;amp;
+     *
+     * @param  string  $escaped  Double-escaped entities
+     * @return string            Entities fixed
+     */
+    private function _fixDoubleEscape($escaped)
+    {
+        return preg_replace('/&amp;([a-z]+|(#\d+));/i', '&\\1;', $escaped);
+    }
+
+}
diff --git a/framework/View/lib/Horde/View/Interface.php b/framework/View/lib/Horde/View/Interface.php
new file mode 100644 (file)
index 0000000..c5c2c58
--- /dev/null
@@ -0,0 +1,119 @@
+<?php
+/**
+ * @category Horde
+ * @package Horde_View
+ */
+
+/**
+ * Horde_View_Interface is a reference for classes to be used as Horde
+ * Views. Implementing it is optional; type hinting is not used to
+ * enforce the interface.
+ *
+ * @category Horde
+ * @package Horde_View
+ */
+interface Horde_View_Interface
+{
+    /**
+     * Return a view variable
+     *
+     * @param string $name Variable name to retrieve
+     */
+    public function __get($name);
+
+    /**
+     * Assign a single view variable
+     *
+     * @param string $name Variable name to set
+     * @param mixed $value The value of $name
+     */
+    public function __set($name, $value);
+
+    /**
+     * Accesses a helper object from within a template.
+     *
+     * @param string $name The helper name.
+     * @param array $args The parameters for the helper.
+     *
+     * @return string The result of the helper output.
+     */
+    public function __call($name, $args);
+
+    /**
+     * Adds to the stack of template paths in LIFO order.
+     *
+     * @param string|array The directory (-ies) to add.
+     */
+    public function addTemplatePath($path);
+
+    /**
+     * Resets the stack of template paths.
+     *
+     * To clear all paths, use Horde_View::setTemplatePath(null).
+     *
+     * @param string|array The directory (-ies) to set as the path.
+     */
+    public function setTemplatePath($path);
+
+    /**
+     * Adds to the stack of helpers in LIFO order.
+     *
+     * @param Horde_View_Helper The helper instance to add.
+     */
+    public function addHelper($helper);
+
+    /**
+     * Sets the escape() callback.
+     *
+     * @param mixed $spec The callback for escape() to use.
+     */
+    public function setEscape($spec);
+
+    /**
+     * Assigns multiple variables to the view.
+     *
+     * The array keys are used as names, each assigned their
+     * corresponding array value.
+     *
+     * @param array $array The array of key/value pairs to assign.
+     *
+     * @see __set()
+     */
+    public function assign($array);
+
+    /**
+     * Processes a template and returns the output.
+     *
+     * @param string $name The template name to process.
+     *
+     * @return string The template output.
+     */
+    public function render($name);
+
+    /**
+     * Escapes a value for output in a template.
+     *
+     * If escaping mechanism is one of htmlspecialchars or htmlentities, uses
+     * {@link $_encoding} setting.
+     *
+     * @param mixed $var The output to escape.
+     *
+     * @return mixed The escaped value.
+     */
+    public function escape($var);
+
+    /**
+     * Set encoding to use with htmlentities() and htmlspecialchars()
+     *
+     * @param string $encoding
+     */
+    public function setEncoding($encoding);
+
+    /**
+     * Return current escape encoding
+     *
+     * @return string
+     */
+    public function getEncoding();
+
+}
diff --git a/framework/View/package.xml b/framework/View/package.xml
new file mode 100644 (file)
index 0000000..a64f090
--- /dev/null
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package packagerversion="1.4.9" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
+http://pear.php.net/dtd/tasks-1.0.xsd
+http://pear.php.net/dtd/package-2.0
+http://pear.php.net/dtd/package-2.0.xsd">
+ <name>View</name>
+ <channel>pear.horde.org</channel>
+ <summary>Horde View API</summary>
+ <description>The Horde_View:: class provides a simple View pattern implementation.
+ </description>
+ <lead>
+  <name>Chuck Hagenbuch</name>
+  <user>chuck</user>
+  <email>chuck@horde.org</email>
+  <active>yes</active>
+ </lead>
+ <date>2008-02-12</date>
+ <time>17:00:00</time>
+ <version>
+  <release>0.2.1</release>
+  <api>0.2.0</api>
+ </version>
+ <stability>
+  <release>beta</release>
+  <api>beta</api>
+ </stability>
+ <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+ <notes>- Better Helper architecture, improved View/Helper registration
+ </notes>
+ <contents>
+  <dir name="/">
+   <dir name="lib">
+    <dir name="Horde">
+     <dir name="View">
+      <dir name="Helper">
+       <file name="Block.php" role="php" />
+       <file name="Url.php" role="php" />
+      </dir> <!-- /lib/Horde/View/Helper -->
+      <file name="Base.php" role="php" />
+      <file name="Exception.php" role="php" />
+      <file name="Helper.php" role="php" />
+      <file name="Interface.php" role="php" />
+     </dir> <!-- /lib/Horde/View -->
+     <file name="View.php" role="php" />
+    </dir> <!-- /lib/Horde -->
+   </dir> <!-- /lib -->
+  </dir> <!-- / -->
+ </contents>
+ <dependencies>
+  <required>
+   <php>
+    <min>5.2.0</min>
+   </php>
+   <pearinstaller>
+    <min>1.5.0</min>
+   </pearinstaller>
+  </required>
+ </dependencies>
+ <phprelease>
+  <filelist>
+   <install name="lib/Horde/View/Helper/Block.php" as="Horde/View/Helper/Block.php" />
+   <install name="lib/Horde/View/Helper/Url.php" as="Horde/View/Helper/Url.php" />
+   <install name="lib/Horde/View/Base.php" as="Horde/View/Base.php" />
+   <install name="lib/Horde/View/Exception.php" as="Horde/View/Exception.php" />
+   <install name="lib/Horde/View/Helper.php" as="Horde/View/Helper.php" />
+   <install name="lib/Horde/View/Interface.php" as="Horde/View/Interface.php" />
+   <install name="lib/Horde/View.php" as="Horde/View.php" />
+  </filelist>
+ </phprelease>
+</package>
diff --git a/framework/View/test/Horde/View/AllTests.php b/framework/View/test/Horde/View/AllTests.php
new file mode 100644 (file)
index 0000000..dbebda2
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+/**
+ * @package    Horde_View
+ * @subpackage UnitTests
+ */
+
+if (!defined('PHPUnit_MAIN_METHOD')) {
+    define('PHPUnit_MAIN_METHOD', 'Horde_View_AllTests::main');
+}
+
+require_once 'PHPUnit/Framework/TestSuite.php';
+require_once 'PHPUnit/TextUI/TestRunner.php';
+
+class Horde_View_AllTests {
+
+    public static function main()
+    {
+        PHPUnit_TextUI_TestRunner::run(self::suite());
+    }
+
+    public static function suite()
+    {
+        $suite = new PHPUnit_Framework_TestSuite('Horde Framework - Horde_View');
+
+        $basedir = dirname(__FILE__);
+        $baseregexp = preg_quote($basedir . DIRECTORY_SEPARATOR, '/');
+
+        foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($basedir)) as $file) {
+            if ($file->isFile() && preg_match('/Test.php$/', $file->getFilename())) {
+                $pathname = $file->getPathname();
+                require $pathname;
+
+                $class = str_replace(DIRECTORY_SEPARATOR, '_',
+                                     preg_replace("/^$baseregexp(.*)\.php/", '\\1', $pathname));
+                $suite->addTestSuite('Horde_View_' . $class);
+            }
+        }
+
+        return $suite;
+    }
+
+}
+
+if (PHPUnit_MAIN_METHOD == 'Horde_View_AllTests::main') {
+    Horde_View_AllTests::main();
+}
diff --git a/framework/View/test/Horde/View/InterfaceTest.php b/framework/View/test/Horde/View/InterfaceTest.php
new file mode 100644 (file)
index 0000000..bcbc158
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+/**
+ * @category Horde
+ * @package Horde_View
+ * @subpackage UnitTests
+ */
+
+require_once dirname(__FILE__) . '/../../../lib/Horde/View/Interface.php';
+require_once dirname(__FILE__) . '/../../../lib/Horde/View/Base.php';
+require_once dirname(__FILE__) . '/../../../lib/Horde/View.php';
+
+class Horde_View_InterfaceTest extends PHPUnit_Framework_TestCase {
+
+    public function testViewInterface()
+    {
+        eval('class Test_View extends Horde_View implements Horde_View_Interface {};');
+    }
+
+}