Html: a bunch of new classes, some changes

This commit is contained in:
Thomas Gelf 2016-11-27 23:54:38 +01:00
parent 34ef3f26af
commit ea7e79248c
11 changed files with 544 additions and 21 deletions

View file

@ -1,6 +1,6 @@
<?php
namespace Icinga\Module\Businessprocess\Web\Html;
namespace Icinga\Module\Businessprocess\Html;
class Attribute
{

View file

@ -1,13 +1,13 @@
<?php
namespace Icinga\Module\Businessprocess\Web\Html;
namespace Icinga\Module\Businessprocess\Html;
use Icinga\Exception\IcingaException;
use Icinga\Exception\ProgrammingError;
class Attributes
{
/** @var Attribute */
/** @var Attribute[] */
protected $attributes = array();
/** @var callable */
@ -64,7 +64,7 @@ class Attributes
} elseif ($attributes !== null) {
throw new IcingaException(
'Attributes, Array or Null expected, got %s',
$self->getPhpTypeName($attributes)
Util::getPhpTypeName($attributes)
);
}
return $self;
@ -74,7 +74,7 @@ class Attributes
/**
* @return Attribute[]
*/
public function attributes()
public function getAttributes()
{
return $this->attributes;
}
@ -87,16 +87,20 @@ class Attributes
public function add($attribute, $value = null)
{
if ($attribute instanceof static) {
foreach ($attribute as $a) {
foreach ($attribute->getAttributes() as $a) {
$this->add($a);
}
return $this;
} elseif ($attribute instanceof Attribute) {
return $this->addAttribute($attribute);
$this->addAttribute($attribute);
} elseif (is_array($attribute)) {
foreach ($attribute as $name => $value) {
$this->add($name, $value);
}
} else {
return $this->addAttribute(Attribute::create($attribute, $value));
$this->addAttribute(Attribute::create($attribute, $value));
}
return $this;
}
/**
@ -151,6 +155,7 @@ class Attributes
*
* @param $name
* @param $callback
* @return $this
*/
public function registerCallbackFor($name, callable $callback)
{

View file

@ -0,0 +1,130 @@
<?php
namespace Icinga\Module\Businessprocess\Html;
use Exception;
abstract class BaseElement extends Html
{
/** @var array You may want to set default attributes when extending this class */
protected $defaultAttributes;
/** @var Attributes */
protected $attributes;
/** @var string */
protected $tag;
/**
* @return Attributes
*/
public function attributes()
{
if ($this->attributes === null) {
$default = $this->getDefaultAttributes();
if (empty($default)) {
$this->attributes = new Attributes();
} else {
$this->attributes = Attributes::wantAttributes($default);
}
}
return $this->attributes;
}
/**
* @param Attributes|array|null $attributes
* @return $this
*/
public function setAttributes($attributes)
{
$this->attributes = Attributes::wantAttributes($attributes);
return $this;
}
/**
* @param Attributes|array|null $attributes
* @return $this
*/
public function addAttributes($attributes)
{
$this->attributes = Attributes::wantAttributes($attributes);
return $this;
}
public function getDefaultAttributes()
{
return $this->defaultAttributes;
}
public function setTag($tag)
{
$this->tag = $tag;
return $this;
}
public function getTag()
{
return $this->tag;
}
public function renderContent()
{
return parent::render();
}
/**
* @return string
*/
public function render()
{
$tag = $this->getTag();
return sprintf(
'<%s%s>%s</%s>',
$tag,
$this->attributes()->render(),
$this->renderContent(),
$tag
);
}
protected function translate($msg)
{
// TODO: Not so nice
return mt('businessprocess', $msg);
}
/**
* Whether the given something can be rendered
*
* @param mixed $any
* @return bool
*/
protected function canBeRendered($any)
{
return is_string($any) || is_int($any) || is_null($any);
}
/**
* @param Exception|string $error
* @return string
*/
protected function renderError($error)
{
return Util::renderError($error);
}
/**
* @return string
*/
public function __toString()
{
try {
return $this->render();
} catch (Exception $e) {
return $this->renderError($e);
}
}
}

View file

@ -1,6 +1,6 @@
<?php
namespace Icinga\Module\Businessprocess\Web\Html;
namespace Icinga\Module\Businessprocess\Html;
class Container extends BaseElement
{
@ -10,6 +10,10 @@ class Container extends BaseElement
/** @var string */
protected $tag = 'div';
protected function __construct()
{
}
/**
* @param Renderable|array|string $content
* @param Attributes|array $attributes
@ -17,10 +21,26 @@ class Container extends BaseElement
*
* @return static
*/
public static function create($content = array(), $attributes = null)
public static function create($attributes = null, $content = null, $tag = null)
{
return new static($content, $attributes, $tag);
$container = new static();
if ($content !== null) {
$container->setContent($content);
}
if ($attributes !== null) {
$container->setAttributes($attributes);
}
if ($tag !== null) {
$container->setTag($tag);
}
return $container;
}
}
class Old {
/**
* @inheritdoc
*/
@ -64,4 +84,5 @@ class Container extends BaseElement
$this->tag
);
}
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace Icinga\Module\Businessprocess\Html;
class Element extends BaseElement
{
/**
* Container constructor.
*
* @param string $tag
* @param Attributes|array $attributes
*/
public function __construct($tag, $attributes = null)
{
$this->tag = $tag;
if ($attributes !== null) {
$this->attributes = $this->attributes()->add($attributes);
}
}
/**
* Container constructor.
*
* @param string $tag
* @param Attributes|array $attributes
*
* @return static
*/
public static function create($tag, $attributes = null)
{
return new static($tag, $attributes);
}
}

View file

@ -0,0 +1,120 @@
<?php
namespace Icinga\Module\Businessprocess\Html;
use Icinga\Exception\ProgrammingError;
class Html implements Renderable
{
protected $contentSeparator = '';
/**
* @var Renderable[]
*/
private $content = array();
public static function escape($any)
{
return Util::wantHtml($any);
}
/**
* @param Renderable $element
* @return $this
*/
public function add(Renderable $element)
{
$this->content[] = $element;
return $this;
}
/**
* @param Renderable|array|string $content
* @return $this
*/
public function setContent($content)
{
$this->content = array(
static::escape($content)
);
return $this;
}
/**
* @param Renderable|array|string $content
* @return $this
*/
public function addContent($content)
{
$this->content[] = static::escape($content);
return $this;
}
/**
* return Html
*/
public function getContent()
{
if ($this->content === null) {
$this->content = array(new Html());
}
return $this->content;
}
public function hasContent()
{
if ($this->content === null) {
return false;
}
// TODO: unfinished
// return $this->content->isEmpty();
return true;
}
/**
* @param $separator
* @return $this
*/
public function setSeparator($separator)
{
$this->contentSeparator = $separator;
return $this;
}
/**
* @inheritdoc
*/
public function render()
{
$html = array();
foreach ($this->content as $element) {
if (is_string($element)) {
var_dump($this->content);
}
$html[] = $element->render();
}
return implode($this->contentSeparator, $html);
}
public static function element($name, $attributes = null)
{
// TODO: This might be anything here, add a better check
if (! ctype_alnum($name)) {
throw new ProgrammingError('Invalid element requested');
}
$class = __NAMESPACE__ . '\\' . $name;
/** @var Element $element */
$element = new $class();
if ($attributes !== null) {
$element->setAttributes($attributes);
}
return $element;
}
}

View file

@ -0,0 +1,8 @@
<?php
namespace Icinga\Module\Businessprocess\Html;
class HtmlString extends Text
{
protected $escaped = true;
}

View file

@ -1,6 +1,6 @@
<?php
namespace Icinga\Module\Businessprocess\Web\Html;
namespace Icinga\Module\Businessprocess\Html;
use Icinga\Module\Businessprocess\Web\Url;
use Icinga\Web\Url as WebUrl;
@ -15,6 +15,10 @@ class Link extends BaseElement
/** @var Url */
protected $url;
protected function __construct()
{
}
/**
* @param Renderable|array|string $content
* @param Url|string $url
@ -26,9 +30,10 @@ class Link extends BaseElement
public static function create($content, $url, $urlParams = null, array $attributes = null)
{
$link = new static();
$link->content = Util::wantHtml($content);
$link->attributes()->registerCallbackFor('href', array($link, 'getHrefAttribute'));
$link->setContent($content);
$link->setAttributes($attributes);
$link->attributes()->registerCallbackFor('href', array($link, 'getHrefAttribute'));
$link->setUrl($url, $urlParams);
return $link;
}
@ -39,15 +44,16 @@ class Link extends BaseElement
$url->addParams($urlParams);
}
$link->url = $url;
$this->url = $url;
} else {
if ($urlParams === null) {
$link->url = Url::fromPath($url);
$this->url = Url::fromPath($url);
} else {
$link->url = Url::fromPath($url, $urlParams);
$this->url = Url::fromPath($url, $urlParams);
}
}
$link->url->getParams();
$this->url->getParams();
}
/**

View file

@ -0,0 +1,8 @@
<?php
namespace Icinga\Module\Businessprocess\Html;
interface Renderable
{
public function render();
}

View file

@ -0,0 +1,61 @@
<?php
namespace Icinga\Module\Businessprocess\Html;
class Text implements Renderable
{
/** @var string */
protected $string;
protected $escaped = false;
/**
* Text constructor.
*
* @param $text
*/
public function __construct($string)
{
$this->string = (string) $string;
}
/**
* @return string
*/
public function getText()
{
return $this->string;
}
/**
* @param bool $escaped
* @return $this
*/
public function setEscaped($escaped = true)
{
$this->escaped = $escaped;
return $this;
}
/**
* @param $text
*
* @return static
*/
public static function create($text)
{
return new static($text);
}
/**
* @return string
*/
public function render()
{
if ($this->escaped) {
return $this->string;
} else {
return Util::escapeForHtml($this->string);
}
}
}

View file

@ -0,0 +1,130 @@
<?php
namespace Icinga\Module\Businessprocess\Html;
use Exception;
use Icinga\Exception\IcingaException;
class Util
{
/**
* Charset to be used - we only support UTF-8
*/
const CHARSET = 'UTF-8';
/**
* The flags we use for htmlspecialchars depend on our PHP version
*/
protected static $htmlEscapeFlags;
/**
* Escape the given value top be safely used in view scripts
*
* @param string $value The output to be escaped
* @return string
*/
public static function escapeForHtml($value)
{
return htmlspecialchars(
$value,
static::htmlEscapeFlags(),
self::CHARSET,
true
);
}
/**
* @param Exception|string $error
* @return string
*/
public static function renderError($error)
{
if ($error instanceof Exception) {
$file = preg_split('/[\/\\\]/', $error->getFile(), -1, PREG_SPLIT_NO_EMPTY);
$file = array_pop($file);
$msg = sprintf(
'%s (%s:%d)',
$error->getMessage(),
$file,
$error->getLine()
);
} elseif (is_string($error)) {
$msg = $error;
} else {
$msg = 'Got an invalid error'; // TODO: translate?
}
return sprintf(
'ERROR: %s', // TODO: translate?
static::escapeForHtml($msg)
);
}
/**
* @param $any
* @return Renderable
* @throws IcingaException
*/
public static function wantHtml($any)
{
if ($any instanceof Renderable) {
return $any;
} elseif (static::canBeRenderedAsString($any)) {
return new Text($any);
} elseif (is_array($any)) {
$html = new Html();
foreach ($any as $el) {
$html->add(static::wantHtml($el));
}
return $html;
} else {
// TODO: Should we add a dedicated Exception class?
throw new IcingaException(
'String, Html Element or Array of such expected, got "%s"',
Util::getPhpTypeName($any)
);
}
}
public static function canBeRenderedAsString($any)
{
return is_string($any) || is_int($any) || is_null($any);
}
/**
* @param $any
* @return string
*/
public static function getPhpTypeName($any)
{
if (is_object($any)) {
return get_class($any);
} else {
return gettype($any);
}
}
/**
* This defines the flags used when escaping for HTML
*
* - Single quotes are not escaped (ENT_COMPAT)
* - With PHP >= 5.4, invalid characters are replaced with <EFBFBD> (ENT_SUBSTITUTE)
* - With PHP 5.3 they are ignored (ENT_IGNORE, less secure)
* - Uses HTML5 entities for PHP >= 5.4, disallowing &#013;
*
* @return int
*/
protected static function htmlEscapeFlags()
{
if (self::$htmlEscapeFlags === null) {
if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
self::$htmlEscapeFlags = ENT_COMPAT | ENT_SUBSTITUTE | ENT_HTML5;
} else {
self::$htmlEscapeFlags = ENT_COMPAT | ENT_IGNORE;
}
}
return self::$htmlEscapeFlags;
}
}