Update navigation rendering code to fit the previous adjustments

refs #5600
This commit is contained in:
Johannes Meyer 2015-09-04 09:08:20 +02:00
parent 9e558c9861
commit a6b2c23684
4 changed files with 285 additions and 222 deletions

View File

@ -18,6 +18,7 @@ use Icinga\Exception\ConfigurationError;
use Icinga\Exception\IcingaException; use Icinga\Exception\IcingaException;
use Icinga\Exception\ProgrammingError; use Icinga\Exception\ProgrammingError;
use Icinga\Util\String; use Icinga\Util\String;
use Icinga\Web\Navigation\Renderer\RecursiveNavigationRenderer;
/** /**
* Container for navigation items * Container for navigation items

View File

@ -1,11 +1,15 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Web\Navigation; namespace Icinga\Web\Navigation\Renderer;
use ArrayIterator; use ArrayIterator;
use Exception;
use RecursiveIterator; use RecursiveIterator;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Exception\IcingaException;
use Icinga\Web\Navigation\Navigation;
use Icinga\Web\Navigation\NavigationItem;
use Icinga\Web\View; use Icinga\Web\View;
/** /**
@ -14,72 +18,151 @@ use Icinga\Web\View;
class NavigationRenderer implements RecursiveIterator, NavigationRendererInterface class NavigationRenderer implements RecursiveIterator, NavigationRendererInterface
{ {
/** /**
* Content to render * The tag used for the outer element
* *
* @var array * @var string
*/ */
private $content = array(); protected $elementTag;
/** /**
* CSS class for the navigation element * The CSS class used for the outer element
* *
* @var string|null * @var string
*/ */
protected $cssClass; protected $cssClass;
/** /**
* Flags * The navigation's heading text
*
* @var int
*/
private $flags;
/**
* The heading for the navigation
* *
* @var string * @var string
*/ */
protected $heading; protected $heading;
/** /**
* Flag for checking whether to call begin/endMarkup() or not * The content rendered so far
*7 *
* @var bool * @var array
*/ */
private $inIteration = false; protected $content;
/** /**
* Iterator over navigation * Whether to skip rendering the outer element
*
* @var bool
*/
protected $skipOuterElement;
/**
* The navigation's iterator
* *
* @var ArrayIterator * @var ArrayIterator
*/ */
private $iterator; protected $iterator;
/** /**
* Current navigation * The navigation
* *
* @var Navigation * @var Navigation
*/ */
private $navigation; protected $navigation;
/** /**
* View * View
* *
* @var View|null * @var View
*/ */
protected $view; protected $view;
/** /**
* Create a new navigation renderer * Create a new NavigationRenderer
* *
* @param Navigation $navigation * @param Navigation $navigation
* @param int $flags * @param bool $skipOuterElement
*/ */
public function __construct(Navigation $navigation, $flags = 0) public function __construct(Navigation $navigation, $skipOuterElement = false)
{ {
$this->skipOuterElement = $skipOuterElement;
$this->iterator = $navigation->getIterator(); $this->iterator = $navigation->getIterator();
$this->navigation = $navigation; $this->navigation = $navigation;
$this->flags = $flags; $this->content = array();
}
/**
* {@inheritdoc}
*/
public function setElementTag($tag)
{
$this->elementTag = $tag;
return $this;
}
/**
* {@inheritdoc}
*/
public function getElementTag()
{
return $this->elementTag ?: static::OUTER_ELEMENT_TAG;
}
/**
* {@inheritdoc}
*/
public function setCssClass($class)
{
$this->cssClass = $class;
return $this;
}
/**
* {@inheritdoc}
*/
public function getCssClass()
{
return $this->cssClass;
}
/**
* {@inheritdoc}
*/
public function setHeading($heading)
{
$this->heading = $heading;
return $this;
}
/**
* {@inheritdoc}
*/
public function getHeading()
{
return $this->heading;
}
/**
* Return the view
*
* @return View
*/
public function view()
{
if ($this->view === null) {
$this->setView(Icinga::app()->getViewRenderer()->view);
}
return $this->view;
}
/**
* Set the view
*
* @param View $view
*
* @return $this
*/
public function setView(View $view)
{
$this->view = $view;
return $this;
} }
/** /**
@ -87,7 +170,7 @@ class NavigationRenderer implements RecursiveIterator, NavigationRendererInterfa
*/ */
public function getChildren() public function getChildren()
{ {
return new static($this->current()->getChildren(), $this->flags & static::NAV_DISABLE); return new static($this->current()->getChildren(), $this->skipOuterElement);
} }
/** /**
@ -100,7 +183,8 @@ class NavigationRenderer implements RecursiveIterator, NavigationRendererInterfa
/** /**
* {@inheritdoc} * {@inheritdoc}
* @return \Icinga\Web\Navigation\NavigationItem *
* @return NavigationItem
*/ */
public function current() public function current()
{ {
@ -129,9 +213,8 @@ class NavigationRenderer implements RecursiveIterator, NavigationRendererInterfa
public function rewind() public function rewind()
{ {
$this->iterator->rewind(); $this->iterator->rewind();
if (! (bool) ($this->flags & static::NAV_DISABLE) && ! $this->inIteration) { if (! $this->skipOuterElement) {
$this->content[] = $this->beginMarkup(); $this->content[] = $this->beginMarkup();
$this->inIteration = true;
} }
} }
@ -141,83 +224,69 @@ class NavigationRenderer implements RecursiveIterator, NavigationRendererInterfa
public function valid() public function valid()
{ {
$valid = $this->iterator->valid(); $valid = $this->iterator->valid();
if (! (bool) ($this->flags & static::NAV_DISABLE) && ! $valid && $this->inIteration) { if (! $this->skipOuterElement && !$valid) {
$this->content[] = $this->endMarkup(); $this->content[] = $this->endMarkup();
$this->inIteration = false;
} }
return $valid; return $valid;
} }
/** /**
* Begin navigation markup * Return the opening markup for the navigation
* *
* @return string * @return string
*/ */
public function beginMarkup() public function beginMarkup()
{ {
$content = array(); $content = array();
if ($this->flags & static::NAV_MAJOR) {
$el = 'nav';
} else {
$el = 'div';
}
if (($elCssClass = $this->getCssClass()) !== null) {
$elCss = ' class="' . $elCssClass . '"';
} else {
$elCss = '';
}
$content[] = sprintf( $content[] = sprintf(
'<%s%s role="navigation">', '<%s%s role="navigation">',
$el, $this->getElementTag(),
$elCss $this->getCssClass() !== null ? ' class="' . $this->getCssClass() . '"' : ''
); );
$content[] = sprintf( $content[] = sprintf(
'<h%1$d class="sr-only" tabindex="-1">%2$s</h%1$d>', '<h%1$d class="sr-only" tabindex="-1">%2$s</h%1$d>',
static::HEADING_RANK, static::HEADING_RANK,
$this->getView()->escape($this->getHeading()) $this->view()->escape($this->getHeading())
); );
$content[] = $this->beginChildrenMarkup(); $content[] = $this->beginChildrenMarkup();
return implode("\n", $content); return join("\n", $content);
} }
/** /**
* End navigation markup * Return the closing markup for the navigation
* *
* @return string * @return string
*/ */
public function endMarkup() public function endMarkup()
{ {
$content = array(); $content = array();
$content[] = $this->endChildrenMarkup(); $content[] = $this->endChildrenMarkup();
if ($this->flags & static::NAV_MAJOR) { $content[] = '</' . $this->getElementTag() . '>';
$content[] = '</nav>'; return join("\n", $content);
} else {
$content[] = '</div>';
}
return implode("\n", $content);
} }
/** /**
* Begin children markup * Return the opening markup for multiple navigation items
* *
* @return string * @return string
*/ */
public function beginChildrenMarkup() public function beginChildrenMarkup()
{ {
$ulCssClass = static::CSS_CLASS_NAV; $cssClass = array(static::CSS_CLASS_NAV);
if ($this->navigation->getLayout() & Navigation::LAYOUT_TABS) { if ($this->navigation->getLayout() === Navigation::LAYOUT_TABS) {
$ulCssClass .= ' ' . static::CSS_CLASS_NAV_TABS; $cssClass[] = static::CSS_CLASS_NAV_TABS;
} elseif ($this->navigation->getLayout() === Navigation::LAYOUT_DROPDOWN) {
$cssClass[] = static::CSS_CLASS_NAV_DROPDOWN;
} }
if ($this->navigation->getLayout() & Navigation::LAYOUT_DROPDOWN) {
$ulCssClass .= ' ' . static::CSS_CLASS_NAV_DROPDOWN; return '<ul class="' . join(' ', $cssClass) . '">';
}
return '<ul class="' . $ulCssClass . '">';
} }
/** /**
* End children markup * Return the closing markup for multiple navigation items
* *
* @return string * @return string
*/ */
public function endChildrenMarkup() public function endChildrenMarkup()
{ {
@ -225,7 +294,7 @@ class NavigationRenderer implements RecursiveIterator, NavigationRendererInterfa
} }
/** /**
* Begin item markup * Return the opening markup for the given navigation item
* *
* @param NavigationItem $item * @param NavigationItem $item
* *
@ -237,104 +306,58 @@ class NavigationRenderer implements RecursiveIterator, NavigationRendererInterfa
if ($item->getActive()) { if ($item->getActive()) {
$cssClass[] = static::CSS_CLASS_ACTIVE; $cssClass[] = static::CSS_CLASS_ACTIVE;
} }
if ($item->hasChildren()
&& $item->getChildren()->getLayout() === Navigation::LAYOUT_DROPDOWN if ($item->hasChildren() && $item->getChildren()->getLayout() === Navigation::LAYOUT_DROPDOWN) {
) {
$cssClass[] = static::CSS_CLASS_DROPDOWN; $cssClass[] = static::CSS_CLASS_DROPDOWN;
$item $item
->setAttribute('class', static::CSS_CLASS_DROPDOWN_TOGGLE) ->setAttribute('class', static::CSS_CLASS_DROPDOWN_TOGGLE)
->setIcon(static::DROPDOWN_TOGGLE_ICON) ->setIcon(static::DROPDOWN_TOGGLE_ICON)
->setUrl('#'); ->setUrl('#');
} }
if (! empty($cssClass)) { if (! empty($cssClass)) {
$content = sprintf('<li class="%s">', implode(' ', $cssClass)); $content = sprintf('<li class="%s">', join(' ', $cssClass));
} else { } else {
$content = '<li>'; $content = '<li>';
} }
return $content; return $content;
} }
/** /**
* End item markup * Return the closing markup for a navigation item
* *
* @return string * @return string
*/ */
public function endItemMarkup() public function endItemMarkup()
{ {
return '</li>'; return '</li>';
} }
/**
* {@inheritdoc}
*/
public function getCssClass()
{
return $this->cssClass;
}
/**
* {@inheritdoc}
*/
public function setCssClass($class)
{
$this->cssClass = trim((string) $class);
return $this;
}
/**
* {@inheritdoc}
*/
public function getHeading()
{
return $this->heading;
}
/**
* {@inheritdoc}
*/
public function setHeading($heading)
{
$this->heading = (string) $heading;
return $this;
}
/**
* Get the view
*
* @return View
*/
public function getView()
{
if ($this->view === null) {
$this->view = Icinga::app()->getViewRenderer()->view;
}
return $this->view;
}
/**
* Set the view
*
* @param View $view
*
* @return $this
*/
public function setView(View $view)
{
$this->view = $view;
return $this;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function render() public function render()
{ {
foreach ($this as $navigationItem) { foreach ($this as $item) {
/** @var \Icinga\Web\Navigation\NavigationItem $navigationItem */ /** @var NavigationItem $item */
$this->content[] = $this->beginItemMarkup($navigationItem); $this->content[] = $this->beginItemMarkup($item);
$this->content[] = $navigationItem->render(); $this->content[] = $item->render();
$this->content[] = $this->endItemMarkup(); $this->content[] = $this->endItemMarkup();
} }
return implode("\n", $this->content);
return join("\n", $this->content);
}
/**
* {@inheritdoc}
*/
public function __toString()
{
try {
return $this->render();
} catch (Exception $e) {
return IcingaException::describe($e);
}
} }
} }

View File

@ -1,12 +1,10 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Web\Navigation; namespace Icinga\Web\Navigation\Renderer;
use Icinga\Web\View;
/** /**
* Interface for navigation renderer * Interface for navigation renderers
*/ */
interface NavigationRendererInterface interface NavigationRendererInterface
{ {
@ -25,7 +23,7 @@ interface NavigationRendererInterface
const CSS_CLASS_DROPDOWN = 'dropdown'; const CSS_CLASS_DROPDOWN = 'dropdown';
/** /**
* CSS class for dropdown's trigger * CSS class for a dropdown item's trigger
*/ */
const CSS_CLASS_DROPDOWN_TOGGLE = 'dropdown-toggle'; const CSS_CLASS_DROPDOWN_TOGGLE = 'dropdown-toggle';
@ -37,58 +35,58 @@ interface NavigationRendererInterface
const CSS_CLASS_NAV = 'nav'; const CSS_CLASS_NAV = 'nav';
/** /**
* CSS class for the ul element w/ dropdown layout * CSS class for the ul element with dropdown layout
* *
* @var string * @var string
*/ */
const CSS_CLASS_NAV_DROPDOWN = 'dropdown-menu'; const CSS_CLASS_NAV_DROPDOWN = 'dropdown-menu';
/** /**
* CSS class for the ul element w/ tabs layout * CSS class for the ul element with tabs layout
* *
* @var string * @var string
*/ */
const CSS_CLASS_NAV_TABS = 'nav-tabs'; const CSS_CLASS_NAV_TABS = 'nav-tabs';
/** /**
* Icon for the dropdown's trigger * Icon for a dropdown item's trigger
* *
* @var string * @var string
*/ */
const DROPDOWN_TOGGLE_ICON = 'menu'; const DROPDOWN_TOGGLE_ICON = 'menu';
/** /**
* Heading rank * Default tag for the outer element the navigation will be wrapped with
*
* @var string
*/
const OUTER_ELEMENT_TAG = 'nav';
/**
* The heading's rank
* *
* @var int * @var int
*/ */
const HEADING_RANK = 2; const HEADING_RANK = 1;
/** /**
* Flag for major navigation * Set the tag for the outer element the navigation is wrapped with
* *
* With this flag the outer navigation element will be nav instead of div * @param string $tag
* *
* @var int * @return $this
*/ */
const NAV_MAJOR = 1; public function setElementTag($tag);
/** /**
* Flag for disabling the outer navigation element * Return the tag for the outer element the navigation is wrapped with
* *
* @var int * @return string
*/ */
const NAV_DISABLE = 2; public function getElementTag();
/** /**
* Get the CSS class for the outer element * Set the CSS class to use for the outer element
*
* @return string|null
*/
public function getCssClass();
/**
* Set the CSS class for the outer element
* *
* @param string $class * @param string $class
* *
@ -97,14 +95,14 @@ interface NavigationRendererInterface
public function setCssClass($class); public function setCssClass($class);
/** /**
* Get the heading * Get the CSS class used for the outer element
* *
* @return string * @return string
*/ */
public function getHeading(); public function getCssClass();
/** /**
* Set the heading * Set the navigation's heading text
* *
* @param string $heading * @param string $heading
* *
@ -113,10 +111,16 @@ interface NavigationRendererInterface
public function setHeading($heading); public function setHeading($heading);
/** /**
* Render navigation to HTML * Return the navigation's heading text
* *
* @return string * @return string
*/
public function getHeading();
/**
* Return the navigation rendered to HTML
*
* @return string
*/ */
public function render(); public function render();
} }

View File

@ -1,10 +1,13 @@
<?php <?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */ /* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Web\Navigation; namespace Icinga\Web\Navigation\Renderer;
use Exception;
use RecursiveIteratorIterator; use RecursiveIteratorIterator;
use Icinga\Web\View; use Icinga\Exception\IcingaException;
use Icinga\Web\Navigation\Navigation;
use Icinga\Web\Navigation\NavigationItem;
/** /**
* Renderer for multi level navigation * Renderer for multi level navigation
@ -16,22 +19,75 @@ use Icinga\Web\View;
class RecursiveNavigationRenderer extends RecursiveIteratorIterator implements NavigationRendererInterface class RecursiveNavigationRenderer extends RecursiveIteratorIterator implements NavigationRendererInterface
{ {
/** /**
* Content to render * The content rendered so far
* *
* @var array * @var array
*/ */
private $content = array(); protected $content;
/** /**
* Create a new recursive navigation renderer * Create a new RecursiveNavigationRenderer
* *
* @param Navigation $navigation * @param Navigation $navigation
* @param int $flags
*/ */
public function __construct(Navigation $navigation, $flags = 0) public function __construct(Navigation $navigation)
{ {
$navigationRenderer = new NavigationRenderer($navigation, $flags & static::NAV_DISABLE); $this->content = array();
parent::__construct($navigationRenderer, RecursiveIteratorIterator::SELF_FIRST); parent::__construct(
new NavigationRenderer($navigation, true),
RecursiveIteratorIterator::SELF_FIRST
);
}
/**
* {@inheritdoc}
*/
public function setElementTag($tag)
{
$this->getInnerIterator()->setElementTag($tag);
return $this;
}
/**
* {@inheritdoc}
*/
public function getElementTag()
{
return $this->getInnerIterator()->getElementTag();
}
/**
* {@inheritdoc}
*/
public function setCssClass($class)
{
$this->getInnerIterator()->setCssClass($class);
return $this;
}
/**
* {@inheritdoc}
*/
public function getCssClass()
{
return $this->getInnerIterator()->getCssClass();
}
/**
* {@inheritdoc}
*/
public function setHeading($heading)
{
$this->getInnerIterator()->setHeading($heading);
return $this;
}
/**
* {@inheritdoc}
*/
public function getHeading()
{
return $this->getInnerIterator()->getHeading();
} }
/** /**
@ -66,53 +122,32 @@ class RecursiveNavigationRenderer extends RecursiveIteratorIterator implements N
$this->content[] = $this->getInnerIterator()->endChildrenMarkup(); $this->content[] = $this->getInnerIterator()->endChildrenMarkup();
} }
/**
* {@inheritdoc}
*/
public function getCssClass()
{
return $this->getInnerIterator()->getCssClass();
}
/**
* {@inheritdoc}
*/
public function setCssClass($class)
{
$this->getInnerIterator()->setCssClass($class);
return $this;
}
/**
* {@inheritdoc}
*/
public function getHeading()
{
return $this->getInnerIterator()->getHeading();
}
/**
* {@inheritdoc}
*/
public function setHeading($heading)
{
$this->getInnerIterator()->setHeading($heading);
return $this;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function render() public function render()
{ {
foreach ($this as $navigationItem) { foreach ($this as $item) {
/** @var \Icinga\Web\Navigation\NavigationItem $navigationItem */ /** @var NavigationItem $item */
$this->content[] = $this->getInnerIterator()->beginItemMarkup($navigationItem); $this->content[] = $this->getInnerIterator()->beginItemMarkup($item);
$this->content[] = $navigationItem->render(); $this->content[] = $item->render();
if (! $navigationItem->hasChildren()) { if (! $item->hasChildren()) {
$this->content[] = $this->getInnerIterator()->endItemMarkup(); $this->content[] = $this->getInnerIterator()->endItemMarkup();
} }
} }
return implode("\n", $this->content);
return join("\n", $this->content);
}
/**
* {@inheritdoc}
*/
public function __toString()
{
try {
return $this->render();
} catch (Exception $e) {
return IcingaException::describe($e);
}
} }
} }