diff --git a/library/vendor/ipl/Compat/Translator.php b/library/vendor/ipl/Compat/Translator.php new file mode 100644 index 00000000..2fd478e1 --- /dev/null +++ b/library/vendor/ipl/Compat/Translator.php @@ -0,0 +1,22 @@ +domain = $domain; + } + + public function translate($string) + { + return WebTranslator::translate($string, $this->domain); + } +} diff --git a/library/vendor/ipl/Data/Paginatable.php b/library/vendor/ipl/Data/Paginatable.php new file mode 100644 index 00000000..bdffcafe --- /dev/null +++ b/library/vendor/ipl/Data/Paginatable.php @@ -0,0 +1,64 @@ +query = $query; + } + + public function setMaxRows($max) + { + $this->maxRows = $max; + return $this; + } + + public function getQuery() + { + if ($this->needsSubQuery()) { + return $this->buildSubQuery(); + } else { + return $this->buildSimpleQuery(); + } + } + + protected function hasOneOf($parts) + { + foreach ($parts as $part) { + if ($this->hasPart($part)) { + return true; + } + } + + return false; + } + + protected function hasPart($part) + { + $values = $this->query->getPart($part); + return ! empty($values); + } + + protected function needsSubQuery() + { + return null !== $this->maxRows || $this->hasOneOf([ + ZfSelect::GROUP, + ZfSelect::UNION + ]); + } + + protected function buildSubQuery() + { + $sub = clone($this->query); + $sub->limit(null, null); + $query = new ZfSelect($this->query->getAdapter()); + $query->from($sub, ['cnt' => 'COUNT(*)']); + if (null !== $this->maxRows) { + $sub->limit($this->maxRows + 1); + } + + return $query; + } + + protected function buildSimpleQuery() + { + $query = clone($this->query); + $query->reset(ZfSelect::COLUMNS); + $query->reset(ZfSelect::ORDER); + $query->reset(ZfSelect::LIMIT_COUNT); + $query->reset(ZfSelect::LIMIT_OFFSET); + $query->columns(['cnt' => 'COUNT(*)']); + return $query; + } +} diff --git a/library/vendor/ipl/Db/Zf1/FilterRenderer.php b/library/vendor/ipl/Db/Zf1/FilterRenderer.php new file mode 100644 index 00000000..aa46e5ed --- /dev/null +++ b/library/vendor/ipl/Db/Zf1/FilterRenderer.php @@ -0,0 +1,239 @@ +filter = $filter; + $this->db = $db; + } + + /** + * @return DbExpr + */ + public function toDbExpression() + { + return new DbExpr($this->render()); + } + + public static function applyToQuery(Filter $filter, DbSelect $query) + { + $renderer = new static($filter, $query->getAdapter()); + $query->where($renderer->toDbExpression()); + return $query; + } + + /** + * @return string + */ + public function render() + { + return $this->renderFilter($this->filter); + } + + protected function renderFilterChain(FilterChain $filter, $level = 0) + { + $prefix = ''; + + if ($filter instanceof FilterAnd) { + $op = ' AND '; + } elseif ($filter instanceof FilterOr) { + $op = ' OR '; + } elseif ($filter instanceof FilterNot) { + $op = ' AND '; + $prefix = 'NOT '; + } else { + throw new ProgrammingError( + 'Cannot render a %s filter chain for Zf Db', + get_class($filter) + ); + } + + $parts = array(); + if (! $filter->isEmpty()) { + foreach ($filter->filters() as $f) { + $part = $this->renderFilter($f, $level + 1); + if ($part !== '') { + $parts[] = $part; + } + } + if (! empty($parts)) { + if ($level > 0) { + return "$prefix (" . implode($op, $parts) . ')'; + } else { + return $prefix . implode($op, $parts); + } + } + } + } + + protected function renderFilterExpression(FilterExpression $filter) + { + $col = $filter->getColumn(); + $sign = $filter->getSign(); + $expression = $filter->getExpression(); + + if (is_array($expression)) { + return $this->renderArrayExpression($col, $sign, $expression); + } + + if ($sign === '=') { + if (strpos($expression, '*') === false) { + return $this->renderAny($col, $sign, $expression); + } else { + return $this->renderLike($col, $expression); + } + } + + if ($sign === '!=') { + if (strpos($expression, '*') === false) { + return $this->renderAny($col, $sign, $expression); + } else { + return $this->renderNotLike($col, $expression); + } + } + + return $this->renderAny($col, $sign, $expression); + } + + + protected function renderLike($col, $expression) + { + if ($expression === '*') { + return new DbExpr('TRUE'); + } + + return $col . ' LIKE ' . $this->escape($this->escapeWildcards($expression)); + } + + protected function renderNotLike($col, $expression) + { + if ($expression === '*') { + return new DbExpr('FALSE'); + } + + return sprintf( + '(%1$s NOT LIKE %2$s OR %1$s IS NULL)', + $col, + $this->escape($this->escapeWildcards($expression)) + ); + } + + protected function renderNotEqual($col, $expression) + { + return sprintf('(%1$s != %2$s OR %1$s IS NULL)', $col, $this->escape($expression)); + } + + protected function renderAny($col, $sign, $expression) + { + return sprintf('%s %s %s', $col, $sign, $this->escape($expression)); + } + + protected function renderArrayExpression($col, $sign, $expression) + { + if ($sign === '=') { + return $col . ' IN (' . $this->escape($expression) . ')'; + } elseif ($sign === '!=') { + return sprintf( + '(%1$s NOT IN (%2$s) OR %1$s IS NULL)', + $col, + $this->escape($expression) + ); + } + + throw new ProgrammingError( + 'Array expressions can only be rendered for = and !=, got %s', + $sign + ); + } + + /** + * @param Filter $filter + * @param int $level + * @return string|DbExpr + */ + protected function renderFilter(Filter $filter, $level = 0) + { + if ($filter instanceof FilterChain) { + return $this->renderFilterChain($filter, $level); + } else { + return $this->renderFilterExpression($filter); + } + } + + protected function escape($value) + { + // bindParam? bindValue? + if (is_array($value)) { + $ret = array(); + foreach ($value as $val) { + $ret[] = $this->escape($val); + } + return implode(', ', $ret); + } else { + return $this->db->quote($value); + } + } + + protected function escapeWildcards($value) + { + return preg_replace('/\*/', '%', $value); + } + + public function whereToSql($col, $sign, $expression) + { + if (is_array($expression)) { + if ($sign === '=') { + return $col . ' IN (' . $this->escape($expression) . ')'; + } elseif ($sign === '!=') { + return sprintf('(%1$s NOT IN (%2$s) OR %1$s IS NULL)', $col, $this->escape($expression)); + } + + throw new QueryException('Unable to render array expressions with operators other than equal or not equal'); + } elseif ($sign === '=' && strpos($expression, '*') !== false) { + if ($expression === '*') { + return new DbExpr('TRUE'); + } + + return $col . ' LIKE ' . $this->escape($this->escapeWildcards($expression)); + } elseif ($sign === '!=' && strpos($expression, '*') !== false) { + if ($expression === '*') { + return new DbExpr('FALSE'); + } + + return sprintf( + '(%1$s NOT LIKE %2$s OR %1$s IS NULL)', + $col, + $this->escape($this->escapeWildcards($expression)) + ); + } elseif ($sign === '!=') { + return sprintf('(%1$s %2$s %3$s OR %1$s IS NULL)', $col, $sign, $this->escape($expression)); + } else { + return sprintf('%s %s %s', $col, $sign, $this->escape($expression)); + } + } +} diff --git a/library/vendor/ipl/Db/Zf1/SelectPaginationAdapter.php b/library/vendor/ipl/Db/Zf1/SelectPaginationAdapter.php new file mode 100644 index 00000000..122bb12c --- /dev/null +++ b/library/vendor/ipl/Db/Zf1/SelectPaginationAdapter.php @@ -0,0 +1,75 @@ +query = $query; + } + + public function getCountQuery() + { + if ($this->countQuery === null) { + $this->countQuery = (new CountQuery($this->query))->getQuery(); + } + + return $this->countQuery; + } + + public function count() + { + return $this->query->getAdapter()->fetchOne( + $this->getCountQuery() + ); + } + + public function limit($count = null, $offset = null) + { + $this->query->limit($count, $offset); + } + + public function hasLimit() + { + return $this->getLimit() !== null; + } + + public function getLimit() + { + return $this->query->getPart(ZfSelect::LIMIT_COUNT); + } + + public function setLimit($limit) + { + $this->query->limit( + $limit, + $this->getOffset() + ); + } + + public function hasOffset() + { + return $this->getOffset() !== null; + } + + public function getOffset() + { + return $this->query->getPart(ZfSelect::LIMIT_OFFSET); + } + + public function setOffset($offset) + { + $this->query->limit( + $this->getLimit(), + $offset + ); + } +} diff --git a/library/vendor/ipl/Html/Attribute.php b/library/vendor/ipl/Html/Attribute.php new file mode 100644 index 00000000..f39d824a --- /dev/null +++ b/library/vendor/ipl/Html/Attribute.php @@ -0,0 +1,176 @@ +setName($name)->setValue($value); + } + + /** + * @param $name + * @param $value + * @return static + */ + public static function create($name, $value) + { + return new static($name, $value); + } + + /** + * @param $name + * @return $this + * @throws ProgrammingError + */ + public function setName($name) + { + if (! preg_match('/^[a-z][a-z-]*$/i', $name)) { + throw new ProgrammingError( + 'Attribute names with special characters are not yet allowed: %s', + $name + ); + } + $this->name = $name; + return $this; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * @param string|array $value + * @return $this + */ + public function setValue($value) + { + $this->value = $value; + return $this; + } + + /** + * @param string $value + * @return $this + */ + public function addValue($value) + { + if (! is_array($this->value)) { + $this->value = array($this->value); + } + + if (is_array($value)) { + $this->value = array_merge($this->value, $value); + } else { + $this->value[] = $value; + } + + return $this; + } + + /** + * @return string + */ + public function render() + { + return sprintf( + '%s="%s"', + $this->renderName(), + $this->renderValue() + ); + } + + /** + * @return string + */ + public function renderName() + { + return static::escapeName($this->name); + } + + /** + * @return string + */ + public function renderValue() + { + return static::escapeValue($this->value); + } + + /** + * @return bool + */ + public function isEmpty() + { + return null === $this->value || $this->value === []; + } + + /** + * @param $name + * @return static + */ + public static function createEmpty($name) + { + return new static($name, null); + } + + /** + * @param $name + * @return string + */ + public static function escapeName($name) + { + // TODO: escape + return (string) $name; + } + + /** + * @param $value + * @return string + */ + public static function escapeValue($value) + { + // TODO: escape differently + if (is_array($value)) { + return Util::escapeForHtml(implode(' ', $value)); + } else { + return Util::escapeForHtml((string) $value); + } + } +} diff --git a/library/vendor/ipl/Html/Attributes.php b/library/vendor/ipl/Html/Attributes.php new file mode 100644 index 00000000..da3fe6dd --- /dev/null +++ b/library/vendor/ipl/Html/Attributes.php @@ -0,0 +1,244 @@ + $value) { + if ($value instanceof Attribute) { + $this->addAttribute($value); + } elseif (is_string($key)) { + $this->add($key, $value); + } elseif (is_array($value) && count($value) === 2) { + $this->add(array_shift($value), array_shift($value)); + } + } + } + + /** + * @param Attribute[] $attributes + * @return static + */ + public static function create(array $attributes = null) + { + return new static($attributes); + } + + /** + * @param Attributes|array|null $attributes + * @return Attributes + * @throws IcingaException + */ + public static function wantAttributes($attributes) + { + if ($attributes instanceof Attributes) { + return $attributes; + } else { + $self = new static(); + if (is_array($attributes)) { + foreach ($attributes as $k => $v) { + $self->add($k, $v); + } + + return $self; + } elseif ($attributes !== null) { + throw new IcingaException( + 'Attributes, Array or Null expected, got %s', + Util::getPhpTypeName($attributes) + ); + } + return $self; + } + } + + /** + * @return Attribute[] + */ + public function getAttributes() + { + return $this->attributes; + } + + /** + * @param Attribute|string $attribute + * @param string|array $value + * @return $this + */ + public function add($attribute, $value = null) + { + if ($attribute instanceof static) { + foreach ($attribute->getAttributes() as $a) { + $this->add($a); + } + } elseif ($attribute instanceof Attribute) { + $this->addAttribute($attribute); + } elseif (is_array($attribute)) { + foreach ($attribute as $name => $value) { + $this->add($name, $value); + } + } else { + $this->addAttribute(Attribute::create($attribute, $value)); + } + + return $this; + } + + /** + * @param Attribute|string $attribute + * @param string|array $value + * @return $this + */ + public function set($attribute, $value = null) + { + if ($attribute instanceof static) { + foreach ($attribute as $a) { + $this->setAttribute($a); + } + + return $this; + } elseif ($attribute instanceof Attribute) { + return $this->setAttribute($attribute); + } elseif (is_array($attribute)) { + foreach ($attribute as $name => $value) { + $this->set($name, $value); + } + } else { + return $this->setAttribute(new Attribute($attribute, $value)); + } + } + + /** + * @param $name + * @return Attribute + */ + public function get($name) + { + if ($this->has($name)) { + return $this->attributes[$name]; + } else { + return Attribute::createEmpty($name); + } + } + + /** + * @param $name + * @return bool + */ + public function delete($name) + { + if ($this->has($name)) { + unset($this->attributes[$name]); + return true; + } else { + return false; + } + } + + /** + * @param $name + * @return bool + */ + public function has($name) + { + return array_key_exists($name, $this->attributes); + } + + /** + * @param Attribute $attribute + * @return $this + */ + public function addAttribute(Attribute $attribute) + { + $name = $attribute->getName(); + if (array_key_exists($name, $this->attributes)) { + $this->attributes[$name]->addValue($attribute->getValue()); + } else { + $this->attributes[$name] = $attribute; + } + + return $this; + } + + /** + * @param Attribute $attribute + * @return $this + */ + public function setAttribute(Attribute $attribute) + { + $name = $attribute->getName(); + $this->attributes[$name] = $attribute; + return $this; + } + + /** + * Callback must return an instance of Attribute + * + * @param string $name + * @param callable $callback + * @return $this + * @throws ProgrammingError + */ + public function registerCallbackFor($name, $callback) + { + if (! is_callable($callback)) { + throw new ProgrammingError('registerCallBack expects a callable callback'); + } + $this->callbacks[$name] = $callback; + return $this; + } + + /** + * @inheritdoc + */ + public function render() + { + if (empty($this->attributes) && empty($this->callbacks)) { + return ''; + } + + $parts = array(); + foreach ($this->callbacks as $name => $callback) { + $attribute = call_user_func($callback); + if ($attribute instanceof Attribute) { + $parts[] = $attribute->render(); + } elseif (is_string($attribute)) { + $parts[] = Attribute::create($name, $attribute)->render(); + } elseif (null === $attribute) { + continue; + } else { + throw new ProgrammingError( + 'A registered attribute callback must return string, null' + . ' or an Attribute' + ); + } + } + + foreach ($this->attributes as $attribute) { + if ($attribute->isEmpty()) { + continue; + } + + $parts[] = $attribute->render(); + } + return ' ' . implode(' ', $parts); + } +} diff --git a/library/vendor/ipl/Html/BaseElement.php b/library/vendor/ipl/Html/BaseElement.php new file mode 100644 index 00000000..044330e8 --- /dev/null +++ b/library/vendor/ipl/Html/BaseElement.php @@ -0,0 +1,133 @@ +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()->add($attributes); + return $this; + } + + public function getDefaultAttributes() + { + return $this->defaultAttributes; + } + + public function setTag($tag) + { + $this->tag = $tag; + return $this; + } + + public function getTag() + { + return $this->tag; + } + + /** + * Container constructor. + * + * @param string $tag + * @param Attributes|array $attributes + * + * @return Element + */ + public function addElement($tag, $attributes = null) + { + $element = Element::create($tag, $attributes); + $this->add($element); + return $element; + } + + public function renderContent() + { + return parent::render(); + } + + protected function assemble() + { + } + + /** + * @return string + */ + public function render() + { + $tag = $this->getTag(); + $this->assemble(); + $content = $this->renderContent(); + if (strlen($content) || $this->forcesClosingTag()) { + return sprintf( + '<%s%s>%s', + $tag, + $this->attributes()->render(), + $content, + $tag + ); + } else { + return sprintf( + '<%s%s />', + $tag, + $this->attributes()->render() + ); + } + } + + public function forcesClosingTag() + { + return false; + } + + /** + * 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); + } +} diff --git a/library/vendor/ipl/Html/Container.php b/library/vendor/ipl/Html/Container.php new file mode 100644 index 00000000..bd33289e --- /dev/null +++ b/library/vendor/ipl/Html/Container.php @@ -0,0 +1,40 @@ +setContent($content); + } + + if ($attributes !== null) { + $container->setAttributes($attributes); + } + if ($tag !== null) { + $container->setTag($tag); + } + + return $container; + } +} diff --git a/library/vendor/ipl/Html/DeferredText.php b/library/vendor/ipl/Html/DeferredText.php new file mode 100644 index 00000000..0de21cd9 --- /dev/null +++ b/library/vendor/ipl/Html/DeferredText.php @@ -0,0 +1,86 @@ + + * $myVar = 'Some value'; + * $text = new DeferredText(function () use ($myVar) { + * return $myVar; + * }); + * $myVar = 'Changed idea'; + * echo $text; + * + */ +class DeferredText implements ValidHtml +{ + /** @var callable will return the text that should be rendered */ + protected $callback; + + /** @var bool */ + protected $escaped = false; + + /** + * DeferredText constructor. + * @param callable $callback Must return the text that should be rendered + */ + public function __construct(callable $callback) + { + $this->callback = $callback; + } + + /** + * Static factory + * + * @param callable $callback Must return the text that should be rendered + * @return static + */ + public static function create(callable $callback) + { + return new static($callback); + } + + public function render() + { + $callback = $this->callback; + + if ($this->escaped) { + return $callback(); + } else { + return Util::escapeForHtml($callback()); + } + } + + /** + * @param bool $escaped + * @return $this + */ + public function setEscaped($escaped = true) + { + $this->escaped = $escaped; + return $this; + } + + /** + * Calls the render function, but is failsafe. In case an Exception occurs, + * an error is rendered instead of the expected HTML + * + * @return string + */ + public function __toString() + { + try { + return $this->render(); + } catch (Exception $e) { + return Util::renderError($e); + } + } +} diff --git a/library/vendor/ipl/Html/Element.php b/library/vendor/ipl/Html/Element.php new file mode 100644 index 00000000..0e5fb1e0 --- /dev/null +++ b/library/vendor/ipl/Html/Element.php @@ -0,0 +1,40 @@ +tag = $tag; + + if ($attributes !== null) { + $this->attributes = $this->attributes()->add($attributes); + } + + if ($content !== null) { + $this->setContent($content); + } + } + + /** + * Container constructor. + * + * @param string $tag + * @param Attributes|array $attributes + * @param ValidHtml|array|string $content + * + * @return static + */ + public static function create($tag, $attributes = null, $content = null) + { + return new static($tag, $attributes, $content); + } +} diff --git a/library/vendor/ipl/Html/FormattedString.php b/library/vendor/ipl/Html/FormattedString.php new file mode 100644 index 00000000..7ead4ed2 --- /dev/null +++ b/library/vendor/ipl/Html/FormattedString.php @@ -0,0 +1,37 @@ +string = Util::wantHtml($string); + + foreach ($arguments as $key => $val) { + $this->arguments[$key] = Util::wantHtml($val); + } + } + + public static function create($string) + { + $args = func_get_args(); + return new static(array_shift($args), $args); + } + + public function render() + { + return vsprintf( + $this->string->render(), + $this->arguments + ); + } +} diff --git a/library/vendor/ipl/Html/Html.php b/library/vendor/ipl/Html/Html.php new file mode 100644 index 00000000..6b201aaf --- /dev/null +++ b/library/vendor/ipl/Html/Html.php @@ -0,0 +1,226 @@ +addContent($c); + } + } else { + $this->addIndexedContent(Util::wantHtml($content)); + } + + return $this; + } + + /** + * @param $content + * @return $this + */ + public function prepend($content) + { + if (is_array($content)) { + foreach (array_reverse($content) as $c) { + $this->prepend($c); + } + } else { + $pos = 0; + $html = Util::wantHtml($content); + array_unshift($this->content, $html); + $this->incrementIndexKeys(); + $this->addObjectPosition($html, $pos); + } + + return $this; + } + + public function remove(Html $html) + { + $key = spl_object_hash($html); + if (array_key_exists($key, $this->contentIndex)) { + foreach ($this->contentIndex[$key] as $pos) { + unset($this->content[$pos]); + } + } + + $this->reIndexContent(); + } + + /** + * @param $string + * @return Html + */ + public function addPrintf($string) + { + $args = func_get_args(); + array_shift($args); + + return $this->add( + new FormattedString($string, $args) + ); + } + + /** + * @param Html|array|string $content + * @return self + */ + public function setContent($content) + { + $this->content = array(); + static::addContent($content); + + return $this; + } + + /** + * @see Html::add() + */ + public function addContent($content) + { + return $this->add($content); + } + + /** + * return ValidHtml[] + */ + public function getContent() + { + return $this->content; + } + + /** + * @return bool + */ + public function hasContent() + { + return ! empty($this->content); + } + + /** + * @param $separator + * @return self + */ + 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); + } + + /** + * @param $tag + * @param null $attributes + * @param null $content + * @return Element + */ + public static function tag($tag, $attributes = null, $content = null) + { + return Element::create($tag, $attributes, $content); + } + + 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; + } + + /** + * @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); + } + } + private function reIndexContent() + { + $this->contentIndex = []; + foreach ($this->content as $pos => $html) { + $this->addObjectPosition($html, $pos); + } + } + + private function addObjectPosition(ValidHtml $html, $pos) + { + $key = spl_object_hash($html); + if (array_key_exists($key, $this->contentIndex)) { + $this->contentIndex[$key][] = $pos; + } else { + $this->contentIndex[$key] = [$pos]; + } + } + + private function addIndexedContent(ValidHtml $html) + { + $pos = count($this->content); + $this->content[$pos] = $html; + $this->addObjectPosition($html, $pos); + } + + private function incrementIndexKeys() + { + foreach ($this->contentIndex as & $index) { + foreach ($index as & $pos) { + $pos++; + } + } + } +} diff --git a/library/vendor/ipl/Html/HtmlString.php b/library/vendor/ipl/Html/HtmlString.php new file mode 100644 index 00000000..5392af48 --- /dev/null +++ b/library/vendor/ipl/Html/HtmlString.php @@ -0,0 +1,8 @@ +setContent($content); + } + + /** + * @param $content + * @param Attributes|array $attributes + * + * @return Element + */ + public static function p($content, $attributes = null) + { + return Element::create('p', $attributes)->setContent($content); + } +} diff --git a/library/vendor/ipl/Html/Icon.php b/library/vendor/ipl/Html/Icon.php new file mode 100644 index 00000000..88dfa4bd --- /dev/null +++ b/library/vendor/ipl/Html/Icon.php @@ -0,0 +1,30 @@ +setAttributes($attributes); + $this->attributes()->add('class', array('icon', 'icon-' . $name)); + } + + /** + * @param string $name + * @param array $attributes + * + * @return static + */ + public static function create($name, array $attributes = null) + { + return new static($name, $attributes); + } + + public function forcesClosingTag() + { + return true; + } +} diff --git a/library/vendor/ipl/Html/Img.php b/library/vendor/ipl/Html/Img.php new file mode 100644 index 00000000..70b189d4 --- /dev/null +++ b/library/vendor/ipl/Html/Img.php @@ -0,0 +1,73 @@ + ''); + + protected function __construct() + { + } + + /** + * @param Url|string $url + * @param array $urlParams + * @param array $attributes + * + * @return static + */ + public static function create($url, $urlParams = null, array $attributes = null) + { + /** @var Img $img */ + $img = new static(); + $img->setAttributes($attributes); + $img->attributes()->registerCallbackFor('src', array($img, 'getSrcAttribute')); + $img->setUrl($url, $urlParams); + return $img; + } + + public function setUrl($url, $urlParams) + { + if ($url instanceof WebUrl) { // Hint: Url is also a WebUrl + if ($urlParams !== null) { + $url->addParams($urlParams); + } + + $this->url = $url; + } else { + if ($urlParams === null) { + $this->url = Url::fromPath($url); + } else { + $this->url = Url::fromPath($url, $urlParams); + } + } + + $this->url->getParams(); + } + + /** + * @return Attribute + */ + public function getSrcAttribute() + { + return new Attribute('src', $this->getUrl()->getAbsoluteUrl('&')); + } + + /** + * @return Url + */ + public function getUrl() + { + // TODO: What if null? #? + return $this->url; + } +} diff --git a/library/vendor/ipl/Html/Link.php b/library/vendor/ipl/Html/Link.php new file mode 100644 index 00000000..cd3d29be --- /dev/null +++ b/library/vendor/ipl/Html/Link.php @@ -0,0 +1,83 @@ +setContent($content); + $this->setAttributes($attributes); + $this->attributes()->registerCallbackFor('href', array($this, 'getHrefAttribute')); + $this->setUrl($url, $urlParams); + } + + /** + * @param ValidHtml|array|string $content + * @param Url|string $url + * @param array $urlParams + * @param mixed $attributes + * + * @return static + */ + public static function create($content, $url, $urlParams = null, array $attributes = null) + { + $link = new static($content, $url, $urlParams, $attributes); + return $link; + } + + /** + * @param $url + * @param $urlParams + */ + public function setUrl($url, $urlParams) + { + if ($url instanceof WebUrl) { // Hint: Url is also a WebUrl + if ($urlParams !== null) { + $url->addParams($urlParams); + } + + $this->url = $url; + } else { + if ($urlParams === null) { + $this->url = Url::fromPath($url); + } else { + $this->url = Url::fromPath($url, $urlParams); + } + } + + $this->url->getParams(); + } + + /** + * @return Attribute + */ + public function getHrefAttribute() + { + return new Attribute('href', $this->getUrl()->getAbsoluteUrl('&')); + } + + /** + * @return Url + */ + public function getUrl() + { + // TODO: What if null? #? + return $this->url; + } +} diff --git a/library/vendor/ipl/Html/Table.php b/library/vendor/ipl/Html/Table.php new file mode 100644 index 00000000..0d1f29cd --- /dev/null +++ b/library/vendor/ipl/Html/Table.php @@ -0,0 +1,223 @@ +caption = Element::create('caption')->addContent( + $content + ); + + return $this; + } + + /** + * Static helper creating a tr element + * + * @param Attributes|array $attributes + * @param Html|array|string $content + * @return Element + */ + public static function tr($content = null, $attributes = null) + { + return Element::create('tr', $attributes, $content); + } + + /** + * Static helper creating a th element + * + * @param Attributes|array $attributes + * @param Html|array|string $content + * @return Element + */ + public static function th($content = null, $attributes = null) + { + return Element::create('th', $attributes, $content); + } + + /** + * Static helper creating a td element + * + * @param Attributes|array $attributes + * @param Html|array|string $content + * @return Element + */ + public static function td($content = null, $attributes = null) + { + return Element::create('td', $attributes, $content); + } + + + public function generateHeader() + { + return Element::create('thead')->add( + $this->addHeaderColumnsTo(static::tr()) + ); + } + + public function generateFooter() + { + return Element::create('tfoot')->add( + $this->addHeaderColumnsTo(static::tr()) + ); + } + + protected function addHeaderColumnsTo(Element $parent) + { + foreach ($this->getColumnsToBeRendered() as $column) { + $parent->add( + Element::create('th')->setContent($column) + ); + } + + return $parent; + } + + /** + * @return null|array + */ + public function getColumnsToBeRendered() + { + return $this->columnsToBeRendered; + } + + public function setColumnsToBeRendered(array $columns) + { + $this->columnsToBeRendered = $columns; + return $this; + } + + public function renderRow($row) + { + $tr = $this->addRowClasses(Element::create('tr'), $row); + + $columns = $this->getColumnsToBeRendered(); + if ($columns === null) { + $this->setColumnsToBeRendered(array_keys((array) $row)); + $columns = $this->getColumnsToBeRendered(); + } + + foreach ($columns as $column) { + $td = static::td(); + if (property_exists($row, $column)) { + $td->setContent($row->$column); + } + $tr->add($td); + } + + return $tr; + } + + public function addRowClasses(Element $tr, $row) + { + $classes = $this->getRowClasses($row); + if (! empty($classes)) { + $tr->attributes()->add('class', $classes); + } + + return $tr; + } + + public function renderRows(Traversable $rows) + { + $body = $this->body(); + foreach ($rows as $row) { + $body->add($this->renderRow($row)); + } + + return $body; + } + + public function body() + { + if ($this->body === null) { + $this->body = Element::create('tbody')->setSeparator("\n"); + } + + return $this->body; + } + + public function header() + { + if ($this->header === null) { + $this->header = $this->generateHeader(); + } + + return $this->header; + } + + public function footer() + { + if ($this->footer === null) { + $this->footer = $this->generateFooter(); + } + + return $this->footer; + } + + public function renderContent() + { + if (null !== $this->caption) { + $this->add($this->caption); + } + + if (null !== $this->header) { + $this->add($this->header); + } + + if (null !== $this->body) { + $this->add($this->body()); + } + + if (null !== $this->footer) { + $this->add($this->footer); + } + + return parent::renderContent(); + } +} diff --git a/library/vendor/ipl/Html/Text.php b/library/vendor/ipl/Html/Text.php new file mode 100644 index 00000000..4f013faa --- /dev/null +++ b/library/vendor/ipl/Html/Text.php @@ -0,0 +1,87 @@ +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); + } + } + + /** + * TODO: Allow to (statically) inject an error renderer. This will allow + * us to satisfy "Show exceptions" settings and/or preferences + * + * @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); + } + } +} diff --git a/library/vendor/ipl/Html/Util.php b/library/vendor/ipl/Html/Util.php new file mode 100644 index 00000000..dd65948c --- /dev/null +++ b/library/vendor/ipl/Html/Util.php @@ -0,0 +1,153 @@ +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? + } + + $output = sprintf( + // TODO: translate? Be careful when doing so, it must be failsafe! + "
\n

" + . "Oops, an error occurred!

\n
%s
\n", + static::escapeForHtml($msg) + ); + + if (static::showTraces()) { + $output .= sprintf( + "
%s
\n", + static::escapeForHtml($error->getTraceAsString()) + ); + } + $output .= "
\n"; + return $output; + } + + public static function showTraces($show = null) + { + if ($show !== null) { + self::$showTraces = $show; + } + + return self::$showTraces; + } + + /** + * @param $any + * @return ValidHtml + * @throws IcingaException + */ + public static function wantHtml($any) + { + if ($any instanceof ValidHtml) { + 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 � (ENT_SUBSTITUTE) + * - With PHP 5.3 they are ignored (ENT_IGNORE, less secure) + * - Uses HTML5 entities for PHP >= 5.4, disallowing + * + * @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; + } +} diff --git a/library/vendor/ipl/Html/ValidHtml.php b/library/vendor/ipl/Html/ValidHtml.php new file mode 100644 index 00000000..5416445e --- /dev/null +++ b/library/vendor/ipl/Html/ValidHtml.php @@ -0,0 +1,21 @@ +getLoader()->registerNamespace( + 'ipl', + dirname(__DIR__) + ); + } +} diff --git a/library/vendor/ipl/Test/BaseTestCase.php b/library/vendor/ipl/Test/BaseTestCase.php new file mode 100644 index 00000000..2760c286 --- /dev/null +++ b/library/vendor/ipl/Test/BaseTestCase.php @@ -0,0 +1,56 @@ +setupCompatLoader(); + } + + /** + * @param $obj + * @param $name + * @return \ReflectionMethod + */ + public function getProtectedMethod($obj, $name) + { + $class = new ReflectionClass($obj); + $method = $class->getMethod($name); + $method->setAccessible(true); + return $method; + } + + /** + * @param $obj + * @param $name + * @return \ReflectionMethod + */ + public function getPrivateMethod($obj, $name) + { + return $this->getProtectedMethod($obj, $name); + } + + protected function setupCompatLoader() + { + require_once dirname(__DIR__) . '/Loader/CompatLoader.php'; + CompatLoader::delegateLoadingToIcingaWeb($this->app()); + } + + protected function app() + { + if (self::$app === null) { + self::$app = Icinga::app(); + } + + return self::$app; + } +} diff --git a/library/vendor/ipl/Test/Bootstrap.php b/library/vendor/ipl/Test/Bootstrap.php new file mode 100644 index 00000000..cd42498a --- /dev/null +++ b/library/vendor/ipl/Test/Bootstrap.php @@ -0,0 +1,31 @@ +translate($string); + } + + public static function getTranslator() + { + return StaticTranslator::get(); + } + + public static function setNoTranslator() + { + StaticTranslator::set(new NoTranslator()); + } + + /** + * @param TranslatorInterface $translator + */ + public static function setTranslator(TranslatorInterface $translator) + { + StaticTranslator::set($translator); + } +} diff --git a/library/vendor/ipl/Translation/TranslatorInterface.php b/library/vendor/ipl/Translation/TranslatorInterface.php new file mode 100644 index 00000000..e477a929 --- /dev/null +++ b/library/vendor/ipl/Translation/TranslatorInterface.php @@ -0,0 +1,8 @@ +wrapped = $wrapped; + $this->callback = $callback; + } + + public function translate($string) + { + return call_user_func_array( + $this->callback, + [$this->wrapped->translate($string)] + ); + } +} diff --git a/library/vendor/ipl/Web/CompatController.php b/library/vendor/ipl/Web/CompatController.php new file mode 100644 index 00000000..d51af096 --- /dev/null +++ b/library/vendor/ipl/Web/CompatController.php @@ -0,0 +1,569 @@ +params = UrlParams::fromQueryString(); + + $this->setRequest($request) + ->setResponse($response) + ->_setInvokeArgs($invokeArgs); + + $this->prepareViewRenderer(); + $this->_helper = new ZfHelperBroker($this); + + $this->handlerBrowserWindows(); + $moduleName = $this->getModuleName(); + $this->initializeTranslator(); + $layout = $this->layout = $this->_helper->layout(); + $layout->isIframe = $request->getUrl()->shift('isIframe'); + $layout->showFullscreen = $request->getUrl()->shift('showFullscreen'); + $layout->moduleName = $moduleName; + + $this->view->compact = $request->getParam('view') === 'compact'; + $url = $this->url(); + $this->params = $url->getParams(); + + if ($url->shift('showCompact')) { + $this->view->compact = true; + } + if ($this->rerenderLayout = $url->shift('renderLayout')) { + $this->xhrLayout = $this->innerLayout; + } + if ($url->shift('_disableLayout')) { + $this->layout->disableLayout(); + } + + // $auth->authenticate($request, $response, $this->requiresLogin()); + if ($this->requiresLogin()) { + if (! $request->isXmlHttpRequest() && $request->isApiRequest()) { + Auth::getInstance()->challengeHttp(); + } + $this->redirectToLogin(Url::fromRequest()); + } + if (($this->Auth()->isAuthenticated() || $this->requiresLogin()) + && $this->getFrontController()->getDefaultModule() !== $this->getModuleName()) { + $this->assertPermission(Manager::MODULE_PERMISSION_NS . $this->getModuleName()); + } + + Benchmark::measure('Ready to initialize the controller'); + $this->prepareInit(); + $this->init(); + } + + /** + * Prepare controller initialization + * + * As it should not be required for controllers to call the parent's init() method, + * base controllers should use prepareInit() in order to prepare the controller + * initialization. + * + * @see \Zend_Controller_Action::init() For the controller initialization method. + */ + protected function prepareInit() + { + } + + /** + * Return the current module's name + * + * @return string + */ + public function getModuleName() + { + if ($this->moduleName === null) { + $this->moduleName = $this->getRequest()->getModuleName(); + } + + return $this->moduleName; + } + + public function Config($file = null) + { + if ($this->moduleName === null) { + if ($file === null) { + return Config::app(); + } else { + return Config::app($file); + } + } else { + return $this->getModuleConfig($file); + } + } + + public function getModuleConfig($file = null) + { + if ($file === null) { + if ($this->config === null) { + $this->config = Config::module($this->getModuleName()); + } + return $this->config; + } else { + if (! array_key_exists($file, $this->configs)) { + $this->configs[$file] = Config::module($this->getModuleName(), $file); + } + return $this->configs[$file]; + } + } + + /** + * Return this controller's module + * + * @return Module + */ + public function Module() + { + if ($this->module === null) { + $this->module = Icinga::app()->getModuleManager()->getModule($this->getModuleName()); + } + + return $this->module; + } + + + public function Window() + { + if ($this->window === null) { + $this->window = new Window( + $this->_request->getHeader('X-Icinga-WindowId', Window::UNDEFINED) + ); + } + return $this->window; + } + + protected function handlerBrowserWindows() + { + if ($this->isXhr()) { + $id = $this->_request->getHeader('X-Icinga-WindowId', null); + + if ($id === Window::UNDEFINED) { + $this->window = new Window($id); + $this->_response->setHeader('X-Icinga-WindowId', Window::generateId()); + } + } + } + + protected function initializeTranslator() + { + $moduleName = $this->getModuleName(); + $domain = $moduleName !== 'default' ? $moduleName : 'icinga'; + $this->view->translationDomain = $domain; + TranslationHelper::setTranslator(new Translator($domain)); + } + + public function init() + { + // Hint: we intentionally do not call our parent's init() method + } + + /** + * Get the authentication manager + * + * @return Auth + */ + public function Auth() + { + if ($this->auth === null) { + $this->auth = Auth::getInstance(); + } + return $this->auth; + } + + /** + * Whether the current user has the given permission + * + * @param string $permission Name of the permission + * + * @return bool + */ + public function hasPermission($permission) + { + return $this->Auth()->hasPermission($permission); + } + + /** + * Assert that the current user has the given permission + * + * @param string $permission Name of the permission + * + * @throws SecurityException If the current user lacks the given permission + */ + public function assertPermission($permission) + { + if (! $this->Auth()->hasPermission($permission)) { + throw new SecurityException('No permission for %s', $permission); + } + } + + /** + * Return restriction information for an eventually authenticated user + * + * @param string $name Restriction name + * + * @return array + */ + public function getRestrictions($name) + { + return $this->Auth()->getRestrictions($name); + } + + /** + * Check whether the controller requires a login. That is when the controller requires authentication and the + * user is currently not authenticated + * + * @return bool + */ + protected function requiresLogin() + { + if (! $this->requiresAuthentication) { + return false; + } + + return ! $this->Auth()->isAuthenticated(); + } + + public function prepareViewRenderer() + { + $this->viewRenderer = new SimpleViewRenderer(); + $this->viewRenderer->replaceZendViewRenderer(); + $this->view = $this->viewRenderer->view; + } + + /** + * @return SimpleViewRenderer + */ + public function getViewRenderer() + { + return $this->viewRenderer; + } + + public function setAutorefreshInterval($interval) + { + if (! is_int($interval) || $interval < 1) { + throw new ProgrammingError( + 'Setting autorefresh interval smaller than 1 second is not allowed' + ); + } + $this->autorefreshInterval = $interval; + $this->layout->autorefreshInterval = $interval; + return $this; + } + + public function disableAutoRefresh() + { + $this->autorefreshInterval = null; + $this->layout->autorefreshInterval = null; + return $this; + } + + protected function redirectXhr($url) + { + if (! $url instanceof WebUrl) { + $url = Url::fromPath($url); + } + + if ($this->rerenderLayout) { + $this->getResponse()->setHeader('X-Icinga-Rerender-Layout', 'yes'); + } + if ($this->reloadCss) { + $this->getResponse()->setHeader('X-Icinga-Reload-Css', 'now'); + } + + $this->shutdownSession(); + + $this->getResponse() + ->setHeader('X-Icinga-Redirect', rawurlencode($url->getAbsoluteUrl())) + ->sendHeaders(); + + exit; + } + + /** + * @see Zend_Controller_Action::preDispatch() + */ + public function preDispatch() + { + $form = new AutoRefreshForm(); + $form->handleRequest(); + $this->_helper->layout()->autoRefreshForm = $form; + } + + /** + * Detect whether the current request requires changes in the layout and apply them before rendering + * + * @see Zend_Controller_Action::postDispatch() + */ + public function postDispatch() + { + Benchmark::measure('Action::postDispatch()'); + + $layout = $this->layout; + $req = $this->getRequest(); + $layout->innerLayout = $this->innerLayout; + + /** @var User $user */ + if ($user = $req->getUser()) { + if ((bool) $user->getPreferences()->getValue('icingaweb', 'show_benchmark', false)) { + if ($layout->isEnabled()) { + $layout->benchmark = $this->renderBenchmark(); + } + } + + if (! (bool) $user->getPreferences()->getValue('icingaweb', 'auto_refresh', true)) { + $this->disableAutoRefresh(); + } + } + + if ($req->getParam('format') === 'pdf') { + $this->shutdownSession(); + $this->sendAsPdf(); + return; + } + + if ($this->isXhr()) { + $this->postDispatchXhr(); + } + + $this->shutdownSession(); + } + + public function postDispatchXhr() + { + $this->layout->setLayout($this->xhrLayout); + $resp = $this->getResponse(); + + $notifications = Notification::getInstance(); + if ($notifications->hasMessages()) { + $notificationList = array(); + foreach ($notifications->popMessages() as $m) { + $notificationList[] = rawurlencode($m->type . ' ' . $m->message); + } + $resp->setHeader('X-Icinga-Notification', implode('&', $notificationList), true); + } + + if ($this->reloadCss) { + $resp->setHeader('X-Icinga-CssReload', 'now', true); + } + + if ($this->title) { + if (preg_match('~[\r\n]~', $this->title)) { + // TODO: Innocent exception and error log for hack attempts + throw new IcingaException('No newlines allowed in page title'); + } + $resp->setHeader( + 'X-Icinga-Title', + rawurlencode($this->title . ' :: Icinga Web'), + true + ); + } else { + $resp->setHeader('X-Icinga-Title', rawurlencode('Icinga Web'), true); + } + + if ($this->rerenderLayout) { + $this->getResponse()->setHeader('X-Icinga-Container', 'layout', true); + } + + if ($this->autorefreshInterval !== null) { + $resp->setHeader('X-Icinga-Refresh', $this->autorefreshInterval, true); + } + + if ($name = $this->getModuleName()) { + $this->getResponse()->setHeader('X-Icinga-Module', $name, true); + } + } + + /** + * Redirect to login + * + * XHR will always redirect to __SELF__ if an URL to redirect to after successful login is set. __SELF__ instructs + * JavaScript to redirect to the current window's URL if it's an auto-refresh request or to redirect to the URL + * which required login if it's not an auto-refreshing one. + * + * XHR will respond with HTTP status code 403 Forbidden. + * + * @param Url|string $redirect URL to redirect to after successful login + */ + protected function redirectToLogin($redirect = null) + { + $login = Url::fromPath('authentication/login'); + if ($this->isXhr()) { + if ($redirect !== null) { + $login->setParam('redirect', '__SELF__'); + } + + $this->_response->setHttpResponseCode(403); + } elseif ($redirect !== null) { + if (! $redirect instanceof Url) { + $redirect = Url::fromPath($redirect); + } + + if (($relativeUrl = $redirect->getRelativeUrl())) { + $login->setParam('redirect', $relativeUrl); + } + } + + $this->rerenderLayout()->redirectNow($login); + } + + protected function sendAsPdf() + { + $pdf = new Pdf(); + $pdf->renderControllerAction($this); + } + + /** + * Render the benchmark + * + * @return string Benchmark HTML + */ + protected function renderBenchmark() + { + $this->viewRenderer->postDispatch(); + Benchmark::measure('Response ready'); + return Benchmark::renderToHtml()/* + . '
'
+            . print_r(get_included_files(), 1)
+        . '
'*/; + } + + public function isXhr() + { + return $this->getRequest()->isXmlHttpRequest(); + } + + protected function redirectHttp($url) + { + if (! $url instanceof Url) { + $url = Url::fromPath($url); + } + $this->shutdownSession(); + $this->_helper->Redirector->gotoUrlAndExit($url->getRelativeUrl()); + } + + /** + * Redirect to a specific url, updating the browsers URL field + * + * @param Url|string $url The target to redirect to + **/ + public function redirectNow($url) + { + if ($this->isXhr()) { + $this->redirectXhr($url); + } else { + $this->redirectHttp($url); + } + } + + protected function shutdownSession() + { + $session = Session::getSession(); + if ($session->hasChanged()) { + $session->write(); + } + } + + protected function rerenderLayout() + { + $this->rerenderLayout = true; + $this->xhrLayout = 'layout'; + return $this; + } + + protected function reloadCss() + { + $this->reloadCss = true; + return $this; + } +} diff --git a/library/vendor/ipl/Web/Component/AbstractList.php b/library/vendor/ipl/Web/Component/AbstractList.php new file mode 100644 index 00000000..9468a076 --- /dev/null +++ b/library/vendor/ipl/Web/Component/AbstractList.php @@ -0,0 +1,40 @@ +addItem($item); + } + + if ($attributes !== null) { + $this->addAttributes($attributes); + } + } + + /** + * @param Html|array|string $content + * @param Attributes|array $attributes + * + * @return $this + */ + public function addItem($content, $attributes = null) + { + return $this->add(Element::create('li', $attributes, $content)); + } +} diff --git a/library/vendor/ipl/Web/Component/ActionBar.php b/library/vendor/ipl/Web/Component/ActionBar.php new file mode 100644 index 00000000..61e70f7b --- /dev/null +++ b/library/vendor/ipl/Web/Component/ActionBar.php @@ -0,0 +1,15 @@ + 'action-bar'); +} diff --git a/library/vendor/ipl/Web/Component/Content.php b/library/vendor/ipl/Web/Component/Content.php new file mode 100644 index 00000000..aa150643 --- /dev/null +++ b/library/vendor/ipl/Web/Component/Content.php @@ -0,0 +1,12 @@ + 'content'); +} diff --git a/library/vendor/ipl/Web/Component/Controls.php b/library/vendor/ipl/Web/Component/Controls.php new file mode 100644 index 00000000..d7316336 --- /dev/null +++ b/library/vendor/ipl/Web/Component/Controls.php @@ -0,0 +1,107 @@ + 'controls'); + + /** @var Tabs */ + private $tabs; + + /** @var ActionBar */ + private $actions; + + /** @var string */ + private $title; + + /** @var string */ + private $subTitle; + + /** + * @param $title + * @param null $subTitle + * @return $this + */ + public function addTitle($title, $subTitle = null) + { + $this->title = $title; + if ($subTitle !== null) { + $this->subTitle = $subTitle; + } + + return $this->add($this->renderTitleElement()); + } + + /** + * @return Tabs + */ + public function getTabs() + { + if ($this->tabs === null) { + $this->tabs = new Tabs(); + } + + return $this->tabs; + } + + /** + * @param Tabs $tabs + * @return $this + */ + public function setTabs(Tabs $tabs) + { + $this->tabs = $tabs; + return $this; + } + + /** + * @return Html + */ + public function getActionBar() + { + if ($this->actions === null) { + $this->setActionBar(new ActionBar()); + } + + return $this->actions; + } + + public function setActionBar(Html $actionBar) + { + if ($this->actions !== null) { + $this->remove($this->actions); + } + + $this->actions = $actionBar; + $this->add($actionBar); + + return $this; + } + + protected function renderTitleElement() + { + $h1 = Html::tag('h1')->setContent($this->title); + if ($this->subTitle) { + $h1->setSeparator(' ')->add( + Html::tag('small', null, $this->subTitle) + ); + } + + return $h1; + } + + public function renderContent() + { + if (null !== $this->tabs) { + $this->prepend($this->tabs); + } + + return parent::renderContent(); + } +} diff --git a/library/vendor/ipl/Web/Component/ControlsAndContent.php b/library/vendor/ipl/Web/Component/ControlsAndContent.php new file mode 100644 index 00000000..784a7c21 --- /dev/null +++ b/library/vendor/ipl/Web/Component/ControlsAndContent.php @@ -0,0 +1,59 @@ +add( + Element::create('li', $content, $attributes) + ); + } +} diff --git a/library/vendor/ipl/Web/Component/OrderedList.php b/library/vendor/ipl/Web/Component/OrderedList.php new file mode 100644 index 00000000..5a3ea1dc --- /dev/null +++ b/library/vendor/ipl/Web/Component/OrderedList.php @@ -0,0 +1,8 @@ + 'pagination-control', + 'role' => 'navigation', + ]; + + /** @var Paginatable The query the paginator widget is created for */ + protected $query; + + /** @var int */ + protected $pageCount; + + /** @var int */ + protected $currentCount; + + /** @var Url */ + protected $url; + + /** @var string */ + protected $pageParam; + + /** @var string */ + protected $perPageParam; + + /** @var int */ + protected $totalCount; + + /** @var int */ + protected $defaultItemCountPerPage = 25; + + public function __construct( + Paginatable $query, + Url $url, + $pageParameter = 'page', + $perPageParameter = 'limit' + ) { + $this->query = $query; + $this->setPageParam($pageParameter); + $this->setPerPageParam($perPageParameter); + $this->setUrl($url); + } + + public function setItemsPerPage($count) + { + // TODO: this should become setOffset once available + $query = $this->getQuery(); + $query->setLimit($count); + + return $this; + } + + protected function setPageParam($pageParam) + { + $this->pageParam = $pageParam; + return $this; + } + + protected function setPerPageParam($perPageParam) + { + $this->perPageParam = $perPageParam; + return $this; + } + + public function getPageParam() + { + return $this->pageParam; + } + + public function getPerPageParam() + { + return $this->perPageParam; + } + + public function getCurrentPage() + { + $query = $this->getQuery(); + if ($query->hasOffset()) { + return ($query->getOffset() / $this->getItemsPerPage()) + 1; + } else { + return 1; + } + } + + protected function setCurrentPage($page) + { + $page = (int) $page; + $this->currentPage = $page; + $offset = $this->firstRowOnPage($page) - 1; + if ($page > 1) { + $query = $this->getQuery(); + $query->setOffset($offset); + } + } + + public function getPageCount() + { + if ($this->pageCount === null) { + $this->pageCount = (int) ceil($this->getTotalItemCount() / $this->getItemsPerPage()); + } + + return $this->pageCount; + } + + protected function getItemsPerPage() + { + $limit = $this->getQuery()->getLimit(); + if ($limit === null) { + throw new ProgrammingError('Something went wrong, got no limit when there should be one'); + } else { + return $limit; + } + } + + public function getTotalItemCount() + { + if ($this->totalCount === null) { + $this->totalCount = count($this->getQuery()); + } + + return $this->totalCount; + } + + public function getPrevious() + { + if ($this->hasPrevious()) { + return $this->getCurrentPage() - 1; + } else { + return null; + } + } + + public function hasPrevious() + { + return $this->getCurrentPage() > 1; + } + + public function getNext() + { + if ($this->hasNext()) { + return $this->getCurrentPage() + 1; + } else { + return null; + } + } + + public function hasNext() + { + return $this->getCurrentPage() < $this->getPageCount(); + } + + public function getQuery() + { + return $this->query; + } + + /** + * Returns an array of "local" pages given the page count and current page number + * + * @return array + */ + protected function getPages() + { + $page = $this->getPageCount(); + $current = $this->getCurrentPage(); + + $range = []; + + if ($page < 10) { + // Show all pages if we have less than 10 + for ($i = 1; $i < 10; $i++) { + if ($i > $page) { + break; + } + + $range[$i] = $i; + } + } else { + // More than 10 pages: + foreach ([1, 2] as $i) { + $range[$i] = $i; + } + + if ($current < 6) { + // We are on page 1-5 from + for ($i = 1; $i <= 7; $i++) { + $range[$i] = $i; + } + } else { + // Current page > 5 + $range[] = '…'; + + if (($page - $current) < 5) { + // Less than 5 pages left + $start = 5 - ($page - $current); + } else { + $start = 1; + } + + for ($i = $current - $start; $i < ($current + (4 - $start)); $i++) { + if ($i > $page) { + break; + } + + $range[$i] = $i; + } + } + + if ($current < ($page - 2)) { + $range[] = '…'; + } + + foreach ([$page - 1, $page] as $i) { + $range[$i] = $i; + } + } + + if (empty($range)) { + $range[] = 1; + } + + return $range; + } + + public function getDefaultItemCountPerPage() + { + return $this->defaultItemCountPerPage; + } + + public function setDefaultItemCountPerPage($count) + { + $this->defaultItemCountPerPage = (int) $count; + return $this; + } + + public function setUrl(Url $url) + { + $page = (int) $url->shift($this->getPageParam()); + $perPage = (int) $url->getParam($this->getPerPageParam()); + if ($perPage > 0) { + $this->setItemsPerPage($perPage); + } else { + $this->setItemsPerPage($this->getDefaultItemCountPerPage()); + } + if ($page > 0) { + $this->setCurrentPage($page); + } + + $this->url = $url; + + return $this; + } + + public function getUrl() + { + if ($this->url === null) { + $this->setUrl(Url::fromRequest()); + } + + return $this->url; + } + + public function getPreviousLabel() + { + return $this->getLabel($this->getCurrentPage() - 1); + } + + protected function getNextLabel() + { + return $this->getLabel($this->getCurrentPage() + 1); + } + + protected function getLabel($page) + { + return sprintf( + $this->translate('Show rows %u to %u of %u'), + $this->firstRowOnPage($page), + $this->lastRowOnPage($page), + $this->getTotalItemCount() + ); + } + + protected function renderPrevious() + { + return Html::tag('li', [ + 'class' => 'nav-item' + ], Link::create( + Icon::create('angle-double-left'), + $this->makeUrl($this->getPrevious()), + null, + [ + 'title' => $this->getPreviousLabel(), + 'class' => 'previous-page' + ] + )); + } + + protected function renderNoPrevious() + { + return $this->renderDisabled(Html::tag('span', [ + 'class' => 'previous-page' + ], [ + $this->srOnly($this->translate('Previous page')), + Icon::create('angle-double-left') + ])); + } + + protected function renderNext() + { + return Html::tag('li', [ + 'class' => 'nav-item' + ], Link::create( + Icon::create('angle-double-right'), + $this->makeUrl($this->getNext()), + null, + [ + 'title' => $this->getNextLabel(), + 'class' => 'next-page' + ] + )); + } + + protected function renderNoNext() + { + return $this->renderDisabled(Html::tag('span', [ + 'class' => 'previous-page' + ], [ + $this->srOnly($this->translate('Next page')), + Icon::create('angle-double-right') + ])); + } + + protected function renderDots() + { + return $this->renderDisabled(Html::tag('span', null, '…')); + } + + protected function renderInnerPages() + { + $pages = []; + $current = $this->getCurrentPage(); + + foreach ($this->getPages() as $page) { + if ($page === '…') { + $pages[] = $this->renderDots(); + } else { + $pages[] = Html::tag( + 'li', + $page === $current ? ['class' => 'active'] : null, + $this->makeLink($page) + ); + } + } + + return $pages; + } + + protected function lastRowOnPage($page) + { + $perPage = $this->getItemsPerPage(); + $total = $this->getTotalItemCount(); + $last = $page * $perPage; + if ($last > $total) { + $last = $total; + } + + return $last; + } + + protected function firstRowOnPage($page) + { + return ($page - 1) * $this->getItemsPerPage() + 1; + } + + protected function makeLink($page) + { + return Link::create( + $page, + $this->makeUrl($page), + null, + ['title' => $this->getLabel($page)] + ); + } + + protected function makeUrl($page) + { + if ($page) { + return $this->getUrl()->with('page', $page); + } else { + return $this->getUrl(); + } + } + + protected function srOnly($content) + { + return Html::tag('span', ['class' => 'sr-only'], $content); + } + + protected function renderDisabled($content) + { + return Html::tag('li', [ + 'class' => ['nav-item', 'disabled'], + 'aria-hidden' => 'true' + ], $content); + } + + protected function renderList() + { + return Html::tag( + 'ul', + ['class' => ['nav', 'tab-nav']], + [ + $this->hasPrevious() ? $this->renderPrevious() : $this->renderNoPrevious(), + $this->renderInnerPages(), + $this->hasNext() ? $this->renderNext() : $this->renderNoNext() + ] + ); + } + + public function renderContent() + { + $this->add([ + $this->renderScreenReaderHeader(), + $this->renderList() + ]); + + return parent::renderContent(); + } + + protected function renderScreenReaderHeader() + { + return Html::tag('h2', [ + // 'id' => $this->protectId('pagination') -> why? + 'class' => 'sr-only', + 'tab-index' => '-1' + ], $this->translate('Pagination')); + } + + public function render() + { + if ($this->getPageCount() < 2) { + return ''; + } else { + return parent::render(); + } + } +} diff --git a/library/vendor/ipl/Web/Component/Tabs.php b/library/vendor/ipl/Web/Component/Tabs.php new file mode 100644 index 00000000..c5f74385 --- /dev/null +++ b/library/vendor/ipl/Web/Component/Tabs.php @@ -0,0 +1,10 @@ +controls === null) { + $this->view->controls = $this->controls = Controls::create(); + } + + return $this->controls; + } + + /** + * @return Tabs + */ + public function tabs(Tabs $tabs = null) + { + if ($tabs === null) { + return $this->controls()->getTabs(); + } else { + $this->controls()->setTabs($tabs); + return $tabs; + } + } + + /** + * @param Html|null $actionBar + * @return Html + */ + public function actions(Html $actionBar = null) + { + if ($actionBar === null) { + return $this->controls()->getActionBar(); + } else { + $this->controls()->setActionBar($actionBar); + return $actionBar; + } + } + + /** + * @return Content + */ + public function content() + { + if ($this->content === null) { + $this->view->content = $this->content = Content::create(); + } + + return $this->content; + } + + /** + * @param $title + * @return $this + */ + public function setTitle($title) + { + $this->title = $this->makeTitle(func_get_args()); + return $this; + } + + /** + * @param $title + * @return $this + */ + public function addTitle($title) + { + $title = $this->makeTitle(func_get_args()); + $this->title = $title; + $this->controls()->addTitle($title); + + return $this; + } + + private function makeTitle($args) + { + $title = array_shift($args); + + if (empty($args)) { + return $title; + } else { + return vsprintf($title, $args); + } + } + + /** + * @param $title + * @param null $url + * @param string $name + * @return $this + */ + public function addSingleTab($title, $url = null, $name = 'main') + { + if ($url === null) { + $url = $this->url(); + } + + $this->tabs()->add($name, [ + 'label' => $title, + 'url' => $url, + ])->activate($name); + + return $this; + } + + /** + * @return Url + */ + public function url() + { + if ($this->url === null) { + $this->url = $this->getOriginalUrl(); + } + + return $this->url; + } + + /** + * @return Url + */ + public function getOriginalUrl() + { + if ($this->originalUrl === null) { + $this->originalUrl = clone($this->getUrlFromRequest()); + } + + return clone($this->originalUrl); + } + + /** + * @return Url + */ + protected function getUrlFromRequest() + { + $webUrl = $this->getRequest()->getUrl(); + + return Url::fromPath( + $webUrl->getPath() + )->setParams($webUrl->getParams()); + } +} diff --git a/library/vendor/ipl/Web/FakeRequest.php b/library/vendor/ipl/Web/FakeRequest.php new file mode 100644 index 00000000..a3a48505 --- /dev/null +++ b/library/vendor/ipl/Web/FakeRequest.php @@ -0,0 +1,32 @@ +url = $url; + return $this; + } + + public function getBaseUrl($raw = false) + { + if (self::$baseUrl === null) { + throw new ProgrammingError('Cannot determine base URL on CLI if not configured'); + } else { + return self::$baseUrl; + } + } +} diff --git a/library/vendor/ipl/Web/Url.php b/library/vendor/ipl/Web/Url.php new file mode 100644 index 00000000..f0d3c3d3 --- /dev/null +++ b/library/vendor/ipl/Web/Url.php @@ -0,0 +1,117 @@ +setPath($url); + } + + $parts = parse_url($url); + + $self->setBasePath($request->getBaseUrl()); + if (isset($parts['path'])) { + $self->setPath($parts['path']); + } + + if (isset($parts['query'])) { + $params = UrlParams::fromQueryString($parts['query'])->mergeValues($params); + } + + if (isset($parts['fragment'])) { + $self->setAnchor($parts['fragment']); + } + + $self->setParams($params); + return $self; + } + + /** + * Create a new Url class representing the current request + * + * If $params are given, those will be added to the request's parameters + * and overwrite any existing parameters + * + * @param UrlParams|array $params Parameters that should additionally be considered for the url + * @param \Icinga\Web\Request $request A request to use instead of the default one + * + * @return Url + */ + public static function fromRequest($params = array(), $request = null) + { + if ($request === null) { + $request = static::getRequest(); + } + + $url = new Url(); + $url->setPath(ltrim($request->getPathInfo(), '/')); + $request->getQuery(); + + // $urlParams = UrlParams::fromQueryString($request->getQuery()); + if (isset($_SERVER['QUERY_STRING'])) { + $urlParams = UrlParams::fromQueryString($_SERVER['QUERY_STRING']); + } else { + $urlParams = UrlParams::fromQueryString(''); + foreach ($request->getQuery() as $k => $v) { + $urlParams->set($k, $v); + } + } + + foreach ($params as $k => $v) { + $urlParams->set($k, $v); + } + $url->setParams($urlParams); + $url->setBasePath($request->getBaseUrl()); + return $url; + } + + public function setBasePath($basePath) + { + if (property_exists($this, 'basePath')) { + parent::setBasePath($basePath); + } else { + return $this->setBaseUrl($basePath); + } + } + + protected static function getRequest() + { + $app = Icinga::app(); + if ($app->isCli()) { + return new FakeRequest(); + } else { + return $app->getRequest(); + } + } +} diff --git a/library/vendor/ipl/Zf1/SimpleViewRenderer.php b/library/vendor/ipl/Zf1/SimpleViewRenderer.php new file mode 100644 index 00000000..b7288e93 --- /dev/null +++ b/library/vendor/ipl/Zf1/SimpleViewRenderer.php @@ -0,0 +1,67 @@ +disabled = $disabled; + return $this; + } + + public function replaceZendViewRenderer() + { + /** @var \Zend_Controller_Action_Helper_ViewRenderer $viewRenderer */ + $viewRenderer = Icinga::app()->getViewRenderer(); + $viewRenderer->setNeverRender(); + $viewRenderer->setNeverController(); + HelperBroker::removeHelper('viewRenderer'); + HelperBroker::addHelper($this); + $this->view = $viewRenderer->view; + return $this; + } + + public function render($action = null, $name = null, $noController = null) + { + if (null === $name) { + $name = null; // $this->getResponseSegment(); + } + + $this->getResponse()->appendBody( + $this->view->controls . $this->view->content, + $name + ); + + // $this->setNoRender(); + $this->rendered = true; + } + + public function shouldRender() + { + return ! $this->disabled && ! $this->rendered; + } + + public function postDispatch() + { + if ($this->shouldRender()) { + $this->render(); + } + } + + public function getName() + { + return 'ViewRenderer'; + } +} diff --git a/run.php b/run.php index 94dab814..c57f5d2c 100644 --- a/run.php +++ b/run.php @@ -1,6 +1,7 @@ provideHook('director/Job', $prefix . 'Job\\ImportJob'); $this->provideHook('director/Job', $prefix . 'Job\\SyncJob'); $this->provideHook('cube/Actions', 'CubeLinks'); + +// ipl compat, unless it is released: + +if (class_exists('ipl\\Html\\ValidHtml')) { + return; +} + +if ($this->app->getModuleManager()->hasEnabled('ipl')) { + return; +} + +require_once __DIR__ . '/library/vendor/ipl/Loader/CompatLoader.php'; +CompatLoader::delegateLoadingToIcingaWeb($this->app);