New approach for view helpers - register anonymous functions

* May be subject to change
* TODO: Find out how to phpDoc them
* Removed a bunch of "old-style" view helpers
* more to come
This commit is contained in:
Thomas Gelf 2014-03-17 17:14:16 +01:00
parent 5bd1d97f5e
commit ebbd4119c2
12 changed files with 223 additions and 406 deletions

View File

@ -1,45 +0,0 @@
<?php
// @codingStandardsIgnoreStart
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga Web 2.
*
* Icinga Web 2 - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*
*/
// {{{ICINGA_LICENSE_HEADER}}}
use Icinga\Authentication\Manager;
/**
* Class Zend_View_Helper_Auth
*/
class Zend_View_Helper_Auth extends Zend_View_Helper_Abstract
{
public function auth()
{
return Manager::getInstance();
}
}
// @codingStandardsIgnoreEnd

View File

@ -1,42 +0,0 @@
<?php
// @codingStandardsIgnoreStart
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga Web 2.
*
* Icinga Web 2 - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*
*/
// {{{ICINGA_LICENSE_HEADER}}}
use Icinga\Util\Format;
class Zend_View_Helper_Format extends Zend_View_Helper_Abstract
{
public function format()
{
return Format::getInstance();
}
}
// @codingStandardsIgnoreStop

View File

@ -1,49 +0,0 @@
<?php
// @codingStandardsIgnoreStart
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga Web 2.
*
* Icinga Web 2 - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*
*/
// {{{ICINGA_LICENSE_HEADER}}}
use Icinga\Web\Url;
/**
* Helper class for creating absolute links from relative strings
*
*/
class Zend_View_Helper_Href extends Zend_View_Helper_Abstract
{
/**
* Turn the relative link $link into absolute one
*
* @param $link
*/
public function href($link, array $params = array()) {
return Url::fromPath($link, $params)->getAbsoluteUrl();
}
}
// @codingStandardsIgnoreEnd

View File

@ -1,44 +0,0 @@
<?php
// @codingStandardsIgnoreStart
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
use Icinga\Web\Url;
/**
* Class Zend_View_Helper_Icon
*/
class Zend_View_Helper_Icon extends Zend_View_Helper_Abstract
{
public function icon($img, $title = null, array $properties = array())
{
$attributes = array();
$has_alt = false;
$has_class = false;
foreach ($properties as $key => $val) {
$attributes[] = sprintf(
'%s="%s"',
filter_var($key, FILTER_SANITIZE_URL),
filter_var($val, FILTER_SANITIZE_FULL_SPECIAL_CHARS)
);
}
if (! array_key_exists('alt', $properties)) {
$attributes[] = 'alt=""';
}
if (! array_key_exists('class', $properties)) {
$attributes[] = 'class="icon"';
}
if (! array_key_exists('title', $properties) && $title !== null) {
$attributes[] = 'title="' . htmlspecialchars($title) . '"';
}
return sprintf(
'<img src="%s"%s />',
Url::fromPath('img/icons/' . $img),
!empty($attributes) ? ' ' . implode(' ', $attributes) : ''
);
}
}
// @codingStandardsIgnoreStart

View File

@ -1,59 +0,0 @@
<?php
// @codingStandardsIgnoreStart
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga Web 2.
*
* Icinga Web 2 - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*
*/
// {{{ICINGA_LICENSE_HEADER}}}
/**
* Class Zend_View_Helper_Img
*/
class Zend_View_Helper_Img extends Zend_View_Helper_Abstract
{
public function img($url, array $properties = array())
{
$attributes = array();
$has_alt = false;
foreach ($properties as $key => $val) {
if ($key === 'alt') $has_alt = true;
$attributes[] = sprintf(
'%s="%s"',
filter_var($key, FILTER_SANITIZE_URL),
filter_var($val, FILTER_SANITIZE_FULL_SPECIAL_CHARS)
);
}
if (! $has_alt) $attributes[] = 'alt=""';
return sprintf(
'<img src="%s"%s />',
$this->view->baseUrl($url),
!empty($attributes) ? ' ' . implode(' ', $attributes) : ''
);
}
}
// @codingStandardsIgnoreStart

View File

@ -1,64 +0,0 @@
<?php
// TODO: Search for the best and safest quoting
// TODO: Check whether attributes are safe. Script, title in combination with
// Hover-Tips etc. Eventually create a whitelist for a few options only.
use Icinga\Web\Url;
class Zend_View_Helper_Qlink extends Zend_View_Helper_Abstract
{
public function qlink($htmlContent, $urlFormat, array $uriParams = array(),
array $properties = array())
{
$quote = true;
$attributes = array();
$baseUrl = null;
foreach ($properties as $key => $val) {
if ($key === 'quote') {
$quote = $val;
continue;
}
if ($key === 'style' && is_array($val)) {
if (empty($val)) {
continue;
}
$parts = array();
foreach ($val as $k => $v) {
$parts[] = "$k: $v";
}
$attributes[] = 'style="' . implode('; ', $parts) . '"';
continue;
}
$attributes[] = sprintf(
'%s="%s"',
//filter_var($key, FILTER_SANITIZE_URL),
rawurlencode($key),
//filter_var($val, FILTER_SANITIZE_FULL_SPECIAL_CHARS)
rawurlencode($val)
);
}
if ($urlFormat instanceof Url) {
$url = $urlFormat;
$uriParams = $url->getParams() + $uriParams;
} else {
$url = Url::fromPath($urlFormat);
}
$url->setParams($uriParams);
return sprintf(
'<a href="%s"%s>%s</a>',
$url,
!empty($attributes) ? ' ' . implode(' ', $attributes) : '',
$quote
? filter_var(
$htmlContent,
FILTER_SANITIZE_FULL_SPECIAL_CHARS,
FILTER_FLAG_NO_ENCODE_QUOTES
)
// Alternativ: htmlentities($htmlContent)
: $htmlContent
);
}
}

View File

@ -1,47 +0,0 @@
<?php
// @codingStandardsIgnoreStart
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga Web 2.
*
* Icinga Web 2 - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*
*/
// {{{ICINGA_LICENSE_HEADER}}}
use Icinga\Util\Format;
/**
* Class Zend_View_Helper_TimeSince
*/
class Zend_View_Helper_TimeSince extends Zend_View_Helper_Abstract
{
public function timeSince($timestamp)
{
return '<span class="timesince">'
. Format::timeSince($timestamp)
. '</span>';
}
}
// @codingStandardsIgnoreStop

View File

@ -1,47 +0,0 @@
<?php
// @codingStandardsIgnoreStart
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga Web 2.
*
* Icinga Web 2 - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*
*/
// {{{ICINGA_LICENSE_HEADER}}}
use Icinga\Util\Format;
/**
* Class Zend_View_Helper_TimeSince
*/
class Zend_View_Helper_TimeUnless extends Zend_View_Helper_Abstract
{
public function timeUnless($timestamp)
{
if (! $timestamp) return '';
return '<span class="timeunless">'
. Format::timeUntil($timestamp)
. '</span>';
}
}
// @codingStandardsIgnoreStop

View File

@ -29,15 +29,25 @@
namespace Icinga\Web;
use \Zend_View_Abstract;
use \Icinga\Web\Url;
use \Icinga\Util\Format;
use Icinga\Exception\ProgrammingError;
use Zend_View_Abstract;
use Closure;
/**
* Icinga view
*/
class View extends Zend_View_Abstract
{
/**
* Charset to be used - we only support UTF-8
*/
const CHARSET = 'UTF-8';
/**
* The flags we use for htmlspecialchars depend on our PHP version
*/
private $replaceFlags;
/**
* Flag to register stream wrapper
*
@ -45,6 +55,11 @@ class View extends Zend_View_Abstract
*/
private $useViewStream = false;
/**
* Registered helper functions
*/
private $helperFunctions = array();
/**
* Create a new view object
*
@ -59,6 +74,13 @@ class View extends Zend_View_Abstract
stream_wrapper_register('zend.view', '\Icinga\Web\ViewStream');
}
}
if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
$this->replaceFlags = ENT_COMPAT | ENT_SUBSTITUTE | ENT_HTML5;
} else {
$this->replaceFlags = ENT_COMPAT | ENT_IGNORE;
}
parent::__construct($config);
}
@ -72,6 +94,62 @@ class View extends Zend_View_Abstract
$this->loadGlobalHelpers();
}
/**
* Escape the given value top be safely used in view scripts
*
* @param string $value The output to be escaped
* @return string
*/
public function escape($value) {
return htmlspecialchars($value, $this->replaceFlags, self::CHARSET, true);
}
/**
* Whether a specific helper (closure) has been registered
*
* @param string $name The desired function name
* @return boolean
*/
public function hasHelperFunction($name)
{
return array_key_exists($name, $this->helperFunctions);
}
/**
* Add a new helper function
*
* @param string $name The desired function name
* @param Closure $function An anonymous function
* @return self
*/
public function addHelperFunction($name, Closure $function)
{
if ($this->hasHelperFunction($name)) {
throw new ProgrammingError(
sprintf('Cannot assign the same helper function twice: "%s"',
$name
));
}
$this->helperFunctions[$name] = $function;
return $this;
}
/**
* Call a helper function
*
* @param string $name The desired function name
* @param Array $args Function arguments
* @return mixed
*/
public function callHelperFunction($name, $args)
{
return call_user_func_array(
$this->helperFunctions[$name],
$args
);
}
/**
* Load helpers
*/
@ -117,12 +195,8 @@ class View extends Zend_View_Abstract
*/
public function __call($name, $args)
{
$namespaced = '\\Icinga\\Web\\View\\' . $name;
if (function_exists($namespaced)) {
return call_user_func_array(
$namespaced,
$args
);
if ($this->hasHelperFunction($name)) {
return $this->callHelperFunction($name, $args);
} else {
return parent::__call($name, $args);
}

View File

@ -0,0 +1,24 @@
<?php
namespace Icinga\Web\View;
use Icinga\Web\Url;
use Icinga\Util\Format;
$this->addHelperFunction('format', function () {
return Format::getInstance();
});
$this->addHelperFunction('timeSince', function ($timestamp) {
return '<span class="timesince">'
. Format::timeSince($timestamp)
. '</span>';
});
$this->addHelperFunction('timeUnless', function ($timestamp) {
if (! $timestamp) return '';
return '<span class="timeunless">'
. Format::timeUntil($timestamp)
. '</span>';
});

View File

@ -0,0 +1,10 @@
<?php
namespace Icinga\Web\View;
use Icinga\Authentication\Manager;
$this->addHelperFunction('auth', function () {
return Manager::getInstance();
});

View File

@ -0,0 +1,106 @@
<?php
namespace Icinga\Web\View;
use Icinga\Web\Url;
use Icinga\Exception\ProgrammingError;
$view = $this;
$this->addHelperFunction('href', function ($path = null, $params = null) use ($view) {
return $view->url($path, $params);
});
$this->addHelperFunction('url', function ($path = null, $params = null) {
if ($path === null) {
$url = Url::fromRequest();
} elseif ($path instanceof Url) {
$url = $path;
} else {
$url = Url::fromPath($path);
}
if ($params !== null) {
$url->setParams($params);
}
return $url;
});
$this->addHelperFunction('qlink', function ($title, $url, $params = null, $properties = array()) use ($view) {
return sprintf(
'<a href="%s"%s>%s</a>',
$view->url($url, $params),
$view->propertiesToString($properties),
$view->escape($title)
);
});
$this->addHelperFunction('img', function ($url, array $properties = array()) use ($view) {
if (! array_key_exists('alt', $properties)) {
$properties['alt'] = '';
}
return sprintf(
'<img src="%s"%s />',
$view->url($url),
$view->propertiesToString($properties)
);
});
$this->addHelperFunction('icon', function ($img, $title = null, array $properties = array()) use ($view) {
// TODO: join with classes passed in $properties?
$attributes = array(
'class' => 'icon',
);
if ($title !== null) {
$attributes['title'] = $title;
}
return $view->img(
'img/icons/' . $img,
array_merge($attributes, $properties)
);
});
$this->addHelperFunction('propertiesToString', function ($properties) use ($view) {
if (empty($properties)) {
return '';
}
$attributes = array();
foreach ($properties as $key => $val) {
if ($key === 'style' && is_array($val)) {
if (empty($val)) {
continue;
}
$parts = array();
foreach ($val as $k => $v) {
$parts[] = "$k: $v";
}
$val = implode('; ', $parts);
continue;
}
$attributes[] = $view->attributeToString($key, $val);
}
return ' ' . implode(' ', $attributes);
});
$this->addHelperFunction('attributeToString', function ($key, $value)
{
// TODO: Doublecheck this!
if (! preg_match('~^[a-zA-Z0-9-]+$~', $key)) {
throw new ProgrammingError(sprintf(
'Trying to set an invalid HTML attribute name: %s',
$key
));
}
return sprintf(
'%s="%s"',
$key,
$value
);
});