summaryrefslogtreecommitdiffstats
path: root/application/views
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 12:39:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 12:39:39 +0000
commit8ca6cc32b2c789a3149861159ad258f2cb9491e3 (patch)
tree2492de6f1528dd44eaa169a5c1555026d9cb75ec /application/views
parentInitial commit. (diff)
downloadicingaweb2-upstream/2.11.4.tar.xz
icingaweb2-upstream/2.11.4.zip
Adding upstream version 2.11.4.upstream/2.11.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'application/views')
-rw-r--r--application/views/helpers/CreateTicketLinks.php23
-rw-r--r--application/views/helpers/FormDate.php46
-rw-r--r--application/views/helpers/FormDateTime.php63
-rw-r--r--application/views/helpers/FormNumber.php77
-rw-r--r--application/views/helpers/FormTime.php46
-rw-r--r--application/views/helpers/ProtectId.php13
-rw-r--r--application/views/helpers/Util.php68
-rw-r--r--application/views/scripts/about/index.phtml171
-rw-r--r--application/views/scripts/account/index.phtml11
-rw-r--r--application/views/scripts/announcements/index.phtml71
-rw-r--r--application/views/scripts/authentication/login.phtml74
-rw-r--r--application/views/scripts/authentication/logout.phtml79
-rw-r--r--application/views/scripts/config/devtools.phtml6
-rw-r--r--application/views/scripts/config/general.phtml6
-rw-r--r--application/views/scripts/config/module-configuration-error.phtml28
-rw-r--r--application/views/scripts/config/module.phtml136
-rw-r--r--application/views/scripts/config/modules.phtml42
-rw-r--r--application/views/scripts/config/resource.phtml73
-rw-r--r--application/views/scripts/config/resource/create.phtml6
-rw-r--r--application/views/scripts/config/resource/modify.phtml6
-rw-r--r--application/views/scripts/config/resource/remove.phtml6
-rw-r--r--application/views/scripts/config/userbackend/reorder.phtml75
-rw-r--r--application/views/scripts/dashboard/error.phtml13
-rw-r--r--application/views/scripts/dashboard/index.phtml26
-rw-r--r--application/views/scripts/dashboard/new-dashlet.phtml6
-rw-r--r--application/views/scripts/dashboard/remove-dashlet.phtml6
-rw-r--r--application/views/scripts/dashboard/remove-pane.phtml6
-rw-r--r--application/views/scripts/dashboard/rename-pane.phtml6
-rw-r--r--application/views/scripts/dashboard/settings.phtml91
-rw-r--r--application/views/scripts/dashboard/update-dashlet.phtml6
-rw-r--r--application/views/scripts/error/error.phtml106
-rw-r--r--application/views/scripts/filter/index.phtml11
-rw-r--r--application/views/scripts/form/reorder-authbackend.phtml83
-rw-r--r--application/views/scripts/group/form.phtml6
-rw-r--r--application/views/scripts/group/list.phtml96
-rw-r--r--application/views/scripts/group/show.phtml108
-rw-r--r--application/views/scripts/iframe/index.phtml8
-rw-r--r--application/views/scripts/index/welcome.phtml2
-rw-r--r--application/views/scripts/inline.phtml2
-rw-r--r--application/views/scripts/joystickPagination.phtml162
-rw-r--r--application/views/scripts/layout/announcements.phtml1
-rw-r--r--application/views/scripts/layout/menu.phtml20
-rw-r--r--application/views/scripts/list/applicationlog.phtml29
-rw-r--r--application/views/scripts/mixedPagination.phtml79
-rw-r--r--application/views/scripts/navigation/dashboard.phtml27
-rw-r--r--application/views/scripts/navigation/index.phtml78
-rw-r--r--application/views/scripts/navigation/shared.phtml68
-rw-r--r--application/views/scripts/pivottablePagination.phtml48
-rw-r--r--application/views/scripts/role/list.phtml65
-rw-r--r--application/views/scripts/search/hint.phtml8
-rw-r--r--application/views/scripts/search/index.phtml7
-rw-r--r--application/views/scripts/showConfiguration.phtml27
-rw-r--r--application/views/scripts/simple-form.phtml6
-rw-r--r--application/views/scripts/user/form.phtml6
-rw-r--r--application/views/scripts/user/list.phtml90
-rw-r--r--application/views/scripts/user/show.phtml138
56 files changed, 2567 insertions, 0 deletions
diff --git a/application/views/helpers/CreateTicketLinks.php b/application/views/helpers/CreateTicketLinks.php
new file mode 100644
index 0000000..4f8a272
--- /dev/null
+++ b/application/views/helpers/CreateTicketLinks.php
@@ -0,0 +1,23 @@
+<?php
+/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */
+
+/**
+ * Helper for creating ticket links from ticket hooks
+ */
+class Zend_View_Helper_CreateTicketLinks extends Zend_View_Helper_Abstract
+{
+ /**
+ * Create ticket links form ticket hooks
+ *
+ * @param string $text
+ *
+ * @return string
+ * @see \Icinga\Application\Hook\TicketHook::createLinks()
+ */
+ public function createTicketLinks($text)
+ {
+ $tickets = $this->view->tickets;
+ /** @var \Icinga\Application\Hook\TicketHook $tickets */
+ return isset($tickets) ? $tickets->createLinks($text) : $text;
+ }
+}
diff --git a/application/views/helpers/FormDate.php b/application/views/helpers/FormDate.php
new file mode 100644
index 0000000..39e6d94
--- /dev/null
+++ b/application/views/helpers/FormDate.php
@@ -0,0 +1,46 @@
+<?php
+/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */
+
+/**
+ * Render date input controls
+ */
+class Zend_View_Helper_FormDate extends Zend_View_Helper_FormElement
+{
+ /**
+ * Render the date input control
+ *
+ * @param string $name
+ * @param int $value
+ * @param array $attribs
+ *
+ * @return string The rendered date input control
+ */
+ public function formDate($name, $value = null, $attribs = null)
+ {
+ $info = $this->_getInfo($name, $value, $attribs);
+
+ extract($info); // name, id, value, attribs, options, listsep, disable
+ /** @var string $id */
+ /** @var bool $disable */
+
+ $disabled = '';
+ if ($disable) {
+ $disabled = ' disabled="disabled"';
+ }
+
+ /** @var \Icinga\Web\View $view */
+ $view = $this->view;
+
+ $html5 = sprintf(
+ '<input type="date" name="%s" id="%s" value="%s"%s%s%s',
+ $view->escape($name),
+ $view->escape($id),
+ $view->escape($value),
+ $disabled,
+ $this->_htmlAttribs($attribs),
+ $this->getClosingBracket()
+ );
+
+ return $html5;
+ }
+}
diff --git a/application/views/helpers/FormDateTime.php b/application/views/helpers/FormDateTime.php
new file mode 100644
index 0000000..de5eb4b
--- /dev/null
+++ b/application/views/helpers/FormDateTime.php
@@ -0,0 +1,63 @@
+<?php
+/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
+
+/**
+ * Render date-and-time input controls
+ */
+class Zend_View_Helper_FormDateTime extends Zend_View_Helper_FormElement
+{
+ /**
+ * Format date and time
+ *
+ * @param DateTime $dateTime
+ * @param bool $local
+ *
+ * @return string
+ */
+ public function formatDate(DateTime $dateTime, $local)
+ {
+ $format = (bool) $local === true ? 'Y-m-d\TH:i:s' : DateTime::RFC3339;
+ return $dateTime->format($format);
+ }
+
+ /**
+ * Render the date-and-time input control
+ *
+ * @param string $name The element name
+ * @param DateTime $value The default timestamp
+ * @param array $attribs Attributes for the element tag
+ *
+ * @return string The element XHTML
+ */
+ public function formDateTime($name, $value = null, $attribs = null)
+ {
+ $info = $this->_getInfo($name, $value, $attribs);
+ extract($info); // name, id, value, attribs, options, listsep, disable
+ /** @var string $id */
+ /** @var bool $disable */
+ $disabled = '';
+ if ($disable) {
+ $disabled = ' disabled="disabled"';
+ }
+ if ($value instanceof DateTime) {
+ // If value was valid, it's a DateTime object
+ $value = $this->formatDate($value, $attribs['local']);
+ }
+ if (isset($attribs['placeholder']) && $attribs['placeholder'] instanceof DateTime) {
+ $attribs['placeholder'] = $this->formatDate($attribs['placeholder'], $attribs['local']);
+ }
+ $type = $attribs['local'] === true ? 'datetime-local' : 'datetime';
+ unset($attribs['local']); // Unset local to not render it again in $this->_htmlAttribs($attribs)
+ $html5 = sprintf(
+ '<input type="%s" data-use-datetime-picker name="%s" id="%s" step="1" value="%s"%s%s%s',
+ $type,
+ $this->view->escape($name),
+ $this->view->escape($id),
+ $this->view->escape($value),
+ $disabled,
+ $this->_htmlAttribs($attribs),
+ $this->getClosingBracket()
+ );
+ return $html5;
+ }
+}
diff --git a/application/views/helpers/FormNumber.php b/application/views/helpers/FormNumber.php
new file mode 100644
index 0000000..f447180
--- /dev/null
+++ b/application/views/helpers/FormNumber.php
@@ -0,0 +1,77 @@
+<?php
+/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
+
+/**
+ * Render number input controls
+ */
+class Zend_View_Helper_FormNumber extends Zend_View_Helper_FormElement
+{
+ /**
+ * Format a number
+ *
+ * @param $number
+ *
+ * @return string
+ */
+ public function formatNumber($number)
+ {
+ if (empty($number)) {
+ return $number;
+ }
+ return $this->view->escape(
+ sprintf(
+ ctype_digit((string) $number) ? '%d' : '%F',
+ $number
+ )
+ );
+ }
+
+ /**
+ * Render the number input control
+ *
+ * @param string $name
+ * @param int $value
+ * @param array $attribs
+ *
+ * @return string The rendered number input control
+ */
+ public function formNumber($name, $value = null, $attribs = null)
+ {
+ $info = $this->_getInfo($name, $value, $attribs);
+ extract($info); // name, id, value, attribs, options, listsep, disable
+ /** @var string $id */
+ /** @var bool $disable */
+ $disabled = '';
+ if ($disable) {
+ $disabled = ' disabled="disabled"';
+ }
+ $min = '';
+ if (isset($attribs['min'])) {
+ $min = sprintf(' min="%s"', $this->formatNumber($attribs['min']));
+ }
+ unset($attribs['min']); // Unset min to not render it again in $this->_htmlAttribs($attribs)
+ $max = '';
+ if (isset($attribs['max'])) {
+ $max = sprintf(' max="%s"', $this->formatNumber($attribs['max']));
+ }
+ unset($attribs['max']); // Unset max to not render it again in $this->_htmlAttribs($attribs)
+ $step = '';
+ if (isset($attribs['step'])) {
+ $step = sprintf(' step="%s"', $attribs['step'] === 'any' ? 'any' : $this->formatNumber($attribs['step']));
+ }
+ unset($attribs['step']); // Unset step to not render it again in $this->_htmlAttribs($attribs)
+ $html5 = sprintf(
+ '<input type="number" name="%s" id="%s" value="%s"%s%s%s%s%s%s',
+ $this->view->escape($name),
+ $this->view->escape($id),
+ $this->view->escape($this->formatNumber($value)),
+ $min,
+ $max,
+ $step,
+ $disabled,
+ $this->_htmlAttribs($attribs),
+ $this->getClosingBracket()
+ );
+ return $html5;
+ }
+}
diff --git a/application/views/helpers/FormTime.php b/application/views/helpers/FormTime.php
new file mode 100644
index 0000000..39d1b83
--- /dev/null
+++ b/application/views/helpers/FormTime.php
@@ -0,0 +1,46 @@
+<?php
+/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */
+
+/**
+ * Render time input controls
+ */
+class Zend_View_Helper_FormTime extends Zend_View_Helper_FormElement
+{
+ /**
+ * Render the time input control
+ *
+ * @param string $name
+ * @param int $value
+ * @param array $attribs
+ *
+ * @return string The rendered time input control
+ */
+ public function formTime($name, $value = null, $attribs = null)
+ {
+ $info = $this->_getInfo($name, $value, $attribs);
+
+ extract($info); // name, id, value, attribs, options, listsep, disable
+ /** @var string $id */
+ /** @var bool $disable */
+
+ $disabled = '';
+ if ($disable) {
+ $disabled = ' disabled="disabled"';
+ }
+
+ /** @var \Icinga\Web\View $view */
+ $view = $this->view;
+
+ $html5 = sprintf(
+ '<input type="time" name="%s" id="%s" value="%s"%s%s%s',
+ $view->escape($name),
+ $view->escape($id),
+ $view->escape($value),
+ $disabled,
+ $this->_htmlAttribs($attribs),
+ $this->getClosingBracket()
+ );
+
+ return $html5;
+ }
+}
diff --git a/application/views/helpers/ProtectId.php b/application/views/helpers/ProtectId.php
new file mode 100644
index 0000000..f6dc226
--- /dev/null
+++ b/application/views/helpers/ProtectId.php
@@ -0,0 +1,13 @@
+<?php
+/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */
+
+/**
+ * Class Zend_View_Helper_Util
+ */
+class Zend_View_Helper_ProtectId extends Zend_View_Helper_Abstract
+{
+ public function protectId($id)
+ {
+ return Zend_Controller_Front::getInstance()->getRequest()->protectId($id);
+ }
+}
diff --git a/application/views/helpers/Util.php b/application/views/helpers/Util.php
new file mode 100644
index 0000000..7a3e410
--- /dev/null
+++ b/application/views/helpers/Util.php
@@ -0,0 +1,68 @@
+<?php
+/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
+
+/**
+ * Class Zend_View_Helper_Util
+ */
+class Zend_View_Helper_Util extends Zend_View_Helper_Abstract
+{
+ public function util()
+ {
+ return $this;
+ }
+
+ public static function showTimeSince($timestamp)
+ {
+ if (! $timestamp) {
+ return 'unknown';
+ }
+ $duration = time() - $timestamp;
+ if ($duration > 3600 * 24 * 3) {
+ if (date('Y') === date('Y', $timestamp)) {
+ return date('d.m.', $timestamp);
+ }
+ return date('m.Y', $timestamp);
+ }
+ return self::showHourMin($duration);
+ }
+
+ public static function showHourMin($sec)
+ {
+ $min = floor($sec / 60);
+ if ($min < 60) {
+ return $min . 'm ' . ($sec % 60) . 's';
+ }
+ $hour = floor($min / 60);
+ if ($hour < 24) {
+ return date('H:i', time() - $sec);
+ }
+ return floor($hour / 24) . 'd ' . ($hour % 24) . 'h';
+ }
+
+ public static function showSeconds($sec)
+ {
+ // Todo: localization
+ if ($sec < 1) {
+ return round($sec * 1000) . 'ms';
+ }
+ if ($sec < 60) {
+ return $sec . 's';
+ }
+ return floor($sec / 60) . 'm ' . ($sec % 60) . 's';
+ }
+
+ public static function showTime($timestamp)
+ {
+ // Todo: localization
+ if ($timestamp < 86400) {
+ return 'undef';
+ }
+ if (date('Ymd') === date('Ymd', $timestamp)) {
+ return date('H:i:s', $timestamp);
+ }
+ if (date('Y') === date('Y', $timestamp)) {
+ return date('H:i d.m.', $timestamp);
+ }
+ return date('H:i d.m.Y', $timestamp);
+ }
+}
diff --git a/application/views/scripts/about/index.phtml b/application/views/scripts/about/index.phtml
new file mode 100644
index 0000000..e80cd89
--- /dev/null
+++ b/application/views/scripts/about/index.phtml
@@ -0,0 +1,171 @@
+<?php
+
+use ipl\Html\HtmlElement;
+use ipl\Web\Widget\Icon;
+
+?>
+<div class="controls">
+ <?= $tabs ?>
+</div>
+<div id="about" class="content">
+
+ <?= $this->img('img/icinga-logo-big.svg', null, array('class' => 'icinga-logo', 'width' => 194)) ?>
+
+ <section>
+ <table class="name-value-table">
+ <?php if (isset($version['appVersion'])): ?>
+ <tr>
+ <th><?= $this->translate('Icinga Web 2 Version') ?></th>
+ <td><?= $this->escape($version['appVersion']) ?></td>
+ </tr>
+ <?php endif ?>
+ <?php if (isset($version['gitCommitID'])): ?>
+ <tr>
+ <th><?= $this->translate('Git commit') ?></th>
+ <td><?= $this->escape($version['gitCommitID']) ?></td>
+ </tr>
+ <?php endif ?>
+ <tr>
+ <th><?= $this->translate('PHP Version') ?></th>
+ <td><?= $this->escape(PHP_VERSION) ?></td>
+ </tr>
+ <?php if (isset($version['gitCommitDate'])): ?>
+ <tr>
+ <th><?= $this->translate('Git commit date') ?></th>
+ <td><?= $this->escape($version['gitCommitDate']) ?></td>
+ </tr>
+ <?php endif ?>
+ </table>
+
+ <div class="external-links">
+ <div class="col">
+ <?=
+ HtmlElement::create('a', [
+ 'href' => 'https://icinga.com/support/',
+ 'target' => '_blank',
+ 'title' => $this->translate('Get Icinga Support')
+ ], [
+ new Icon('life-ring'),
+ $this->translate('Get Icinga Support'),
+ ]
+ );
+ ?>
+ </div>
+ <div class="col">
+ <?=
+ HtmlElement::create('a', [
+ 'href' => 'https://icinga.com/community/',
+ 'target' => '_blank',
+ 'title' => $this->translate('Icinga Community')
+ ], [
+ new Icon('globe-europe'),
+ $this->translate('Icinga Community'),
+ ]
+ );
+ ?>
+ </div>
+ <div class="col">
+ <?=
+ HtmlElement::create('a', [
+ 'href' => 'https://github.com/icinga/icingaweb2/issues',
+ 'target' => '_blank',
+ 'title' => $this->translate('Icinga Community')
+ ], [
+ new Icon('bullhorn'),
+ $this->translate('Report a bug'),
+ ]
+ );
+ ?>
+ </div>
+ <div class="col">
+ <?=
+ HtmlElement::create('a', [
+ 'href' => 'https://icinga.com/docs/icinga-web-2/'
+ . (isset($version['docVersion']) ? $version['docVersion'] : 'latest'),
+ 'target' => '_blank',
+ 'title' => $this->translate('Icinga Documentation')
+ ], [
+ new Icon('book'),
+ $this->translate('Icinga Documentation'),
+ ]
+ );
+ ?>
+ </div>
+ </div>
+
+ <h2><?= $this->translate('Loaded Libraries') ?></h2>
+ <table class="name-value-table" data-base-target="_next">
+ <?php foreach ($libraries as $library): ?>
+ <tr>
+ <th>
+ <?= $this->escape($library->getName()) ?>
+ </th>
+ <td>
+ <?= $this->escape($library->getVersion()) ?: '-' ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+ </table>
+
+ <h2><?= $this->translate('Loaded Modules') ?></h2>
+ <table class="name-value-table" data-base-target="_next">
+ <?php foreach ($modules as $module): ?>
+ <tr>
+ <th>
+ <?= $this->escape($module->getName()) ?>
+ </th>
+ <td>
+ <td>
+ <?= $this->escape($module->getVersion()) ?>
+ </td>
+ <td>
+ <?php if ($this->hasPermission('config/modules')): ?>
+ <?= $this->qlink(
+ $this->translate('Configure'),
+ 'config/module/',
+ array('name' => $module->getName()),
+ array('title' => sprintf($this->translate('Show the overview of the %s module'), $module->getName()))
+ ) ?>
+ <?php endif ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+ </table>
+ </section>
+
+ <footer>
+ <div class="about-copyright">
+ <?= $this->translate('Copyright') ?>
+ <span>&copy; 2013-<?= date('Y') ?></span>
+ <?= $this->qlink(
+ 'Icinga GmbH',
+ 'https://icinga.com',
+ null,
+ array(
+ 'target' => '_blank'
+ )
+ ) ?>
+ </div>
+ <div class="about-social">
+ <?= $this->qlink(
+ null,
+ 'https://www.twitter.com/icinga',
+ null,
+ array(
+ 'target' => '_blank',
+ 'icon' => 'twitter',
+ 'title' => $this->translate('Icinga on Twitter')
+ )
+ ) ?> <?= $this->qlink(
+ null,
+ 'https://www.facebook.com/icinga',
+ null,
+ array(
+ 'target' => '_blank',
+ 'icon' => 'facebook-squared',
+ 'title' => $this->translate('Icinga on Facebook')
+ )
+ ) ?>
+ </div>
+ </footer>
+</div>
diff --git a/application/views/scripts/account/index.phtml b/application/views/scripts/account/index.phtml
new file mode 100644
index 0000000..efc2bcb
--- /dev/null
+++ b/application/views/scripts/account/index.phtml
@@ -0,0 +1,11 @@
+<div class="controls">
+ <?= $tabs ?>
+</div>
+<div class="content">
+<?php if (isset($changePasswordForm)): ?>
+ <h1><?= $this->translate('Account') ?></h1>
+ <?= $changePasswordForm ?>
+<?php endif ?>
+ <h1><?= $this->translate('Preferences') ?></h1>
+ <?= $form ?>
+</div>
diff --git a/application/views/scripts/announcements/index.phtml b/application/views/scripts/announcements/index.phtml
new file mode 100644
index 0000000..ff87c66
--- /dev/null
+++ b/application/views/scripts/announcements/index.phtml
@@ -0,0 +1,71 @@
+<?php if (! $this->compact): ?>
+<div class="controls">
+ <?= $this->tabs ?>
+ <?= $this->paginator ?>
+ <div class="sort-controls-container">
+ <?= $this->limiter ?>
+ <?= $this->sortBox ?>
+ </div>
+ <?= $this->filterEditor ?>
+</div>
+<?php endif ?>
+<div class="content">
+<?php if ($this->hasPermission('application/announcements')) {
+ echo $this->qlink(
+ $this->translate('Create a New Announcement') ,
+ 'announcements/new',
+ null,
+ array(
+ 'class' => 'button-link',
+ 'data-base-target' => '_next',
+ 'icon' => 'plus',
+ 'title' => $this->translate('Create a new announcement')
+ )
+ );
+} ?>
+<?php if (empty($this->announcements)): ?>
+ <p><?= $this->translate('No announcements found.') ?></p>
+</div>
+<?php return; endif ?>
+ <table data-base-target="_next" class="table-row-selectable common-table">
+ <thead>
+ <tr>
+ <th><?= $this->translate('Author') ?></th>
+ <th><?= $this->translate('Message') ?></th>
+ <th><?= $this->translate('Start') ?></th>
+ <th><?= $this->translate('End') ?></th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ <?php foreach ($this->announcements as $announcement): /** @var object $announcement */ ?>
+ <tr>
+ <td><?= $this->escape($announcement->author) ?></td>
+ <?php if ($this->hasPermission('application/announcements')): ?>
+ <td>
+ <a href="<?= $this->href('announcements/update', array('id' => $announcement->id)) ?>">
+ <?= $this->ellipsis($this->escape($announcement->message), 100) ?>
+ </a>
+ </td>
+ <?php else: ?>
+ <td><?= $this->ellipsis($this->escape($announcement->message), 100) ?></td>
+ <?php endif ?>
+ <td><?= $this->formatDateTime($announcement->start) ?></td>
+ <td><?= $this->formatDateTime($announcement->end) ?></td>
+ <?php if ($this->hasPermission('application/announcements')): ?>
+ <td class="icon-col"><?= $this->qlink(
+ null,
+ 'announcements/remove',
+ array('id' => $announcement->id),
+ array(
+ 'class' => 'action-link',
+ 'icon' => 'cancel',
+ 'title' => $this->translate('Remove this announcement')
+ )
+ ) ?></td>
+ <?php endif ?>
+ </tr>
+ <?php endforeach ?>
+ </tbody>
+ </table>
+</div>
diff --git a/application/views/scripts/authentication/login.phtml b/application/views/scripts/authentication/login.phtml
new file mode 100644
index 0000000..167a468
--- /dev/null
+++ b/application/views/scripts/authentication/login.phtml
@@ -0,0 +1,74 @@
+<div id="login">
+ <div class="login-form" data-base-target="layout">
+ <div role="status" class="sr-only">
+ <?= $this->translate(
+ 'Welcome to Icinga Web 2. For users of the screen reader Jaws full and expectant compliant'
+ . ' accessibility is possible only with use of the Firefox browser. VoiceOver on Mac OS X is tested on'
+ . ' Chrome, Safari and Firefox.'
+ ) ?>
+ </div>
+ <div class="logo-wrapper"><div id="icinga-logo" aria-hidden="true"></div></div>
+ <?php if ($requiresSetup): ?>
+ <p class="config-note"><?= sprintf(
+ $this->translate(
+ 'It appears that you did not configure Icinga Web 2 yet so it\'s not possible to log in without any defined '
+ . 'authentication method. Please define a authentication method by following the instructions in the'
+ . ' %1$sdocumentation%3$s or by using our %2$sweb-based setup-wizard%3$s.'
+ ),
+ '<a href="https://icinga.com/docs/icinga-web-2/latest/doc/05-Authentication/#authentication" title="'
+ . $this->translate('Icinga Web 2 Documentation') . '">',
+ '<a href="' . $this->href('setup') . '" title="' . $this->translate('Icinga Web 2 Setup-Wizard') . '">',
+ '</a>'
+ ) ?></p>
+ <?php endif ?>
+ <?= $this->form ?>
+ <div id="login-footer">
+ <p>Icinga Web 2 &copy; 2013-<?= date('Y') ?></p>
+ <?= $this->qlink($this->translate('icinga.com'), 'https://icinga.com') ?>
+ </div>
+ </div>
+ <ul id="social">
+ <li>
+ <?= $this->qlink(
+ null,
+ 'https://twitter.com/icinga',
+ null,
+ array(
+ 'target' => '_blank',
+ 'icon' => 'twitter',
+ 'title' => $this->translate('Icinga on Twitter')
+ )
+ ) ?>
+ </li>
+ <li>
+ <?= $this->qlink(
+ null,
+ 'https://www.facebook.com/icinga',
+ null,
+ array(
+ 'target' => '_blank',
+ 'icon' => 'facebook-squared',
+ 'title' => $this->translate('Icinga on Facebook')
+ )
+ ) ?>
+ </li>
+ <li><?= $this->qlink(
+ null,
+ 'https://github.com/Icinga',
+ null,
+ array(
+ 'target' => '_blank',
+ 'icon' => 'github-circled',
+ 'title' => $this->translate('Icinga on GitHub')
+ )
+ ) ?>
+ </li>
+ </ul>
+</div>
+<div id="orb-analytics" class="orb" ><?= $this->img('img/orb-analytics.png'); ?></div>
+<div id="orb-automation" class="orb"><?= $this->img('img/orb-automation.png'); ?></div>
+<div id="orb-cloud" class="orb"><?= $this->img('img/orb-cloud.png'); ?></div>
+<div id="orb-icinga" class="orb"><?= $this->img('img/orb-icinga.png'); ?></div>
+<div id="orb-infrastructure" class="orb"><?= $this->img('img/orb-infrastructure.png'); ?></div>
+<div id="orb-metrics" class="orb" ><?= $this->img('img/orb-metrics.png'); ?></div>
+<div id="orb-notifactions" class="orb"><?= $this->img('img/orb-notifications.png'); ?></div>
diff --git a/application/views/scripts/authentication/logout.phtml b/application/views/scripts/authentication/logout.phtml
new file mode 100644
index 0000000..d4bd78e
--- /dev/null
+++ b/application/views/scripts/authentication/logout.phtml
@@ -0,0 +1,79 @@
+<!--
+ This view provides a workaround to logout from an external authentication provider, in case external
+ authentication was configured (the default is to handle authentications internally in Icingaweb2).
+
+ The <a href="http://tools.ietf.org/html/rfc2617">Http Basic and Digest Authentication</a> is not
+ designed to handle logout. When the user has provided valid credentials, the client is adviced to include these
+ in every further request until the browser was closed. To allow logout and to allow the user to change the
+ logged-in user this JavaScript provides a workaround to force a new authentication prompt in most browsers.
+-->
+<div class="content">
+ <div id="icinga-logo" aria-hidden="true"></div>
+ <div class="alert alert-warning" id="logout-status">
+ <b><?= $this->translate('Logging out...'); ?></b>
+ <br>
+ <?= $this->translate(
+ 'If this message does not disappear, it might be necessary to quit the'
+ . ' current session manually by clearing the cache, or by closing the current'
+ . ' browser session.'
+ ); ?>
+ </div>
+
+ <div class="container">
+ <a href="<?= $this->href('dashboard'); ?>"><?= $this->translate('Login'); ?></a>
+ </div>
+</div>
+<script type="text/javascript">
+ /*
+ * When JavaScript is available, trigger an XmlHTTPRequest with the non-existing user 'logout' and abort it
+ * before it is able to finish. This will cause the browser to show a new authentication prompt in the next
+ * request.
+ */
+ document.addEventListener('DOMContentLoaded', function () {
+ var msg = document.getElementById('logout-status');
+ try {
+ if (navigator.userAgent.toLowerCase().indexOf('msie') !== -1) {
+ document.execCommand('ClearAuthenticationCache');
+ } else {
+ var xhttp = new XMLHttpRequest();
+ xhttp.open('GET', 'arbitrary url', true, 'logout', 'logout');
+ xhttp.send('');
+ xhttp.abort();
+ }
+ } catch (e) {
+ }
+ msg.innerHTML = '<?= $this->translate('Logout successful!'); ?>';
+ msg.className = 'alert alert-success';
+ });
+</script>
+<style type="text/css">
+ body {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+ background-color: #0095bf;
+ color: white;
+ }
+ .content {
+ text-align: center;
+ }
+
+ #icinga-logo {
+ background-image: url('../img/icinga-logo-big.svg');
+ background-position: center bottom;
+ background-repeat: no-repeat;
+ background-size: contain;
+ height: 177px;
+ margin-top: 10em;
+ width: 100%;
+ }
+
+ #logout-status {
+ margin: 2em 0 1em;
+ font-size: 2em;
+ font-weight: bold;
+ }
+
+ .container a {
+ color: white;
+ font-size: 1.5em;
+ }
+</style>
diff --git a/application/views/scripts/config/devtools.phtml b/application/views/scripts/config/devtools.phtml
new file mode 100644
index 0000000..245a71a
--- /dev/null
+++ b/application/views/scripts/config/devtools.phtml
@@ -0,0 +1,6 @@
+<div class="controls">
+<?= $this->tabs ?>
+</div>
+<table class="avp">
+<tr><th><?= $this->translate('UI Debug') ?></th><td><a href="javascript:void(0);" onclick="icinga.ui.toggleDebug();"><?= $this->translate('toggle') ?></td></tr>
+</table>
diff --git a/application/views/scripts/config/general.phtml b/application/views/scripts/config/general.phtml
new file mode 100644
index 0000000..13a8ed9
--- /dev/null
+++ b/application/views/scripts/config/general.phtml
@@ -0,0 +1,6 @@
+<div class="controls">
+ <?= $tabs ?>
+</div>
+<div class="content">
+ <?= $form ?>
+</div>
diff --git a/application/views/scripts/config/module-configuration-error.phtml b/application/views/scripts/config/module-configuration-error.phtml
new file mode 100644
index 0000000..85fb128
--- /dev/null
+++ b/application/views/scripts/config/module-configuration-error.phtml
@@ -0,0 +1,28 @@
+<?php
+ $action = (isset($this->action)) ? $this->action : 'do something with';
+ $moduleName = $this->moduleName;
+ $exceptionMessage = $this->exceptionMessage;
+?>
+<?= $this->tabs->render($this); ?>
+<br/>
+<div>
+ <h1>Could not <?= $action; ?> module "<?= $moduleName; ?>"</h1>
+ <p>
+ While operation the following error occurred:
+ <br />
+ <?= $exceptionMessage; ?>
+ </p>
+</div>
+
+<p>
+ This could have one or more of the following reasons:
+<ul>
+ <li>No file permissions to write into module directory</li>
+ <li>Errors on filesystems: Mount points, operational errors </li>
+ <li>General application error</li>
+</ul>
+</p>
+
+<p>
+ Details can be seen in your application log (if you don't have access to this file, call your administrator in this case).
+</p> \ No newline at end of file
diff --git a/application/views/scripts/config/module.phtml b/application/views/scripts/config/module.phtml
new file mode 100644
index 0000000..6d41ab2
--- /dev/null
+++ b/application/views/scripts/config/module.phtml
@@ -0,0 +1,136 @@
+<div class="controls">
+ <?= $this->tabs ?>
+</div>
+<div class="content">
+ <?php if (! $module): ?>
+ <?= $this->translate('There is no such module installed.') ?>
+ <?php return; endif ?>
+ <?php
+ $requiredMods = $module->getRequiredModules();
+ $requiredLibs = $module->getRequiredLibraries();
+ $restrictions = $module->getProvidedRestrictions();
+ $permissions = $module->getProvidedPermissions();
+ $unmetDependencies = $moduleManager->hasUnmetDependencies($module->getName());
+ $isIcingadbSupported = isset($requiredMods['icingadb']);
+ $state = $moduleData->enabled ? ($moduleData->loaded ? 'enabled' : 'failed') : 'disabled';
+ ?>
+ <table class="name-value-table">
+ <tr>
+ <th><?= $this->escape($this->translate('Name')) ?></th>
+ <td><?= $this->escape($module->getName()) ?></td>
+ </tr>
+ <tr>
+ <th><?= $this->translate('State') ?></th>
+ <td>
+ <?= $state ?>
+ <?php if (isset($this->toggleForm)): ?>
+ <?php if ($moduleData->enabled || ! $unmetDependencies): ?>
+ <?= $this->toggleForm ?>
+ <?php else: ?>
+ <?= $this->icon('attention-alt', $this->translate('Module can\'t be enabled due to unmet dependencies')) ?>
+ <?php endif ?>
+ <?php endif ?>
+ </td>
+ <tr>
+ <th><?= $this->escape($this->translate('Version')) ?></th>
+ <td><?= $this->escape($module->getVersion()) ?></td>
+ </tr>
+ <?php if (isset($moduleGitCommitId) && $moduleGitCommitId !== false): ?>
+ <tr>
+ <th><?= $this->escape($this->translate('Git commit')) ?></th>
+ <td><?= $this->escape($moduleGitCommitId) ?></td>
+ </tr>
+ <?php endif ?>
+ <tr>
+ <th><?= $this->escape($this->translate('Description')) ?></th>
+ <td>
+ <strong><?= $this->escape($module->getTitle()) ?></strong><br>
+ <?= nl2br($this->escape($module->getDescription())) ?>
+ </td>
+ </tr>
+ <tr>
+ <th><?= $this->escape($this->translate('Dependencies')) ?></th>
+ <td class="module-dependencies">
+ <?php if (empty($requiredLibs) && empty($requiredMods)): ?>
+ <?= $this->translate('This module has no dependencies') ?>
+ <?php else: ?>
+ <?php if ($unmetDependencies): ?>
+ <strong class="unmet-dependencies">
+ <?= $this->translate('Unmet dependencies found! Module can\'t be enabled unless all dependencies are met.') ?>
+ </strong>
+ <?php endif ?>
+ <?php if (! empty($requiredLibs)): ?>
+ <table class="name-value-table">
+ <caption><?= $this->translate('Libraries') ?></caption>
+ <?php foreach ($requiredLibs as $libraryName => $versionString): ?>
+ <tr>
+ <th><?= $this->escape($libraryName) ?></th>
+ <td>
+ <?php if ($libraries->has($libraryName, $versionString === true ? null : $versionString)): ?>
+ <?= $versionString === true ? '*' : $this->escape($versionString) ?>
+ <?php else: ?>
+ <span class="missing"><?= $versionString === true ? '*' : $this->escape($versionString) ?></span>
+ <?php if (($library = $libraries->get($libraryName)) !== null): ?>
+ (<?= $library->getVersion() ?>)
+ <?php endif ?>
+ <?php endif ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+ </table>
+ <?php endif ?>
+ <?php if (! empty($requiredMods)): ?>
+ <table class="name-value-table">
+ <caption><?= $this->translate('Modules') ?></caption>
+ <?php foreach ($requiredMods as $moduleName => $versionString): ?>
+ <?php if ($moduleName === 'monitoring' && $isIcingadbSupported && $moduleManager->has('icingadb', $requiredMods['icingadb'])) : ?>
+ <?php continue; ?>
+ <?php endif ?>
+ <tr>
+ <th><?= $this->escape($moduleName) ?></th>
+ <td>
+ <?php if ($moduleManager->has($moduleName, $versionString === true ? null : $versionString)): ?>
+ <?= $versionString === true ? '*' : $this->escape($versionString) ?>
+ <?php else: ?>
+ <span <?= ($moduleName === 'icingadb' && isset($requiredMods['monitoring']) && $moduleManager->has('monitoring', $requiredMods['monitoring'])) ? 'class="optional"' : 'class="missing"' ?>>
+ <?= $versionString === true ? '*' : $this->escape($versionString) ?>
+ </span>
+ <?php if (! $moduleManager->hasInstalled($moduleName)): ?>
+ (<?= $this->translate('not installed') ?>)
+ <?php else: ?>
+ (<?= $moduleManager->getModule($moduleName, false)->getVersion() ?><?= $moduleManager->hasEnabled($moduleName) ? '' : ', ' . $this->translate('disabled') ?>)
+ <?php endif ?>
+ <?php endif ?>
+ </td>
+ <?php if ($moduleName === 'monitoring' && $isIcingadbSupported) : ?>
+ <td class="or-separator"><?= $this->translate('or') ?></td>
+ <?php endif ?>
+ </tr>
+ <?php endforeach ?>
+ </table>
+ <?php endif ?>
+ <?php endif ?>
+ </td>
+ </tr>
+ <?php if (! empty($permissions)): ?>
+ <tr>
+ <th><?= $this->escape($this->translate('Permissions')) ?></th>
+ <td>
+ <?php foreach ($permissions as $permission): ?>
+ <strong><?= $this->escape($permission->name) ?></strong>: <?= $this->escape($permission->description) ?><br />
+ <?php endforeach ?>
+ </td>
+ </tr>
+ <?php endif ?>
+ <?php if (! empty($restrictions)): ?>
+ <tr>
+ <th><?= $this->escape($this->translate('Restrictions')) ?></th>
+ <td>
+ <?php foreach ($restrictions as $restriction): ?>
+ <strong><?= $this->escape($restriction->name) ?></strong>: <?= $this->escape($restriction->description) ?><br />
+ <?php endforeach ?>
+ </td>
+ </tr>
+ <?php endif ?>
+ </table>
+</div>
diff --git a/application/views/scripts/config/modules.phtml b/application/views/scripts/config/modules.phtml
new file mode 100644
index 0000000..b13b378
--- /dev/null
+++ b/application/views/scripts/config/modules.phtml
@@ -0,0 +1,42 @@
+<?php if (! $this->compact): ?>
+<div class="controls">
+ <?= $this->tabs ?>
+ <?= $this->paginator ?>
+</div>
+<?php endif ?>
+<div class="content">
+ <table class="table-row-selectable common-table" data-base-target="_next">
+ <thead>
+ <tr>
+ <th><?= $this->translate('Module') ?></th>
+ </tr>
+ </thead>
+ <tbody>
+ <?php foreach ($modules as $module): ?>
+ <tr>
+ <td>
+ <?php if (! $module->installed) {
+ $this->icon('flash', sprintf($this->translate('Module %s is dangling'), $module->name));
+ } elseif ($module->enabled && $module->loaded) {
+ echo $this->icon('thumbs-up', sprintf($this->translate('Module %s is enabled'), $module->name));
+ } elseif (! $module->enabled) {
+ echo $this->icon('block', sprintf($this->translate('Module %s is disabled'), $module->name));
+ } else { // ! $module->loaded
+ echo $this->icon('block', sprintf($this->translate('Module %s has failed to load'), $module->name));
+ }
+
+ echo $this->qlink(
+ $module->name,
+ 'config/module',
+ array('name' => $module->name),
+ array(
+ 'class' => 'rowaction',
+ 'title' => sprintf($this->translate('Show the overview of the %s module'), $module->name)
+ )
+ ); ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+ </tbody>
+ </table>
+</div>
diff --git a/application/views/scripts/config/resource.phtml b/application/views/scripts/config/resource.phtml
new file mode 100644
index 0000000..317c115
--- /dev/null
+++ b/application/views/scripts/config/resource.phtml
@@ -0,0 +1,73 @@
+<div class="controls">
+ <?= $tabs ?>
+</div>
+<div class="content">
+ <?= $this->qlink(
+ $this->translate('Create a New Resource') ,
+ 'config/createresource',
+ null,
+ array(
+ 'class' => 'button-link',
+ 'data-base-target' => '_next',
+ 'icon' => 'plus',
+ 'title' => $this->translate('Create a new resource')
+ )
+ ) ?>
+ <table class="table-row-selectable common-table" data-base-target="_next">
+ <thead>
+ <tr>
+ <th><?= $this->translate('Resource') ?></th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+<?php foreach ($this->resources as $name => $value): ?>
+ <tr>
+ <td>
+ <?php
+ switch ($value->type) {
+ case 'db':
+ $icon = 'database';
+ break;
+ case 'ldap':
+ $icon = 'sitemap';
+ break;
+ case 'ssh':
+ $icon = 'user';
+ break;
+ case 'file':
+ case 'ini':
+ $icon = 'doc-text';
+ break;
+ default:
+ $icon = 'edit';
+ break;
+ }
+ ?>
+ <?= $this->qlink(
+ $name,
+ 'config/editresource',
+ array('resource' => $name),
+ array(
+ 'icon' => $icon,
+ 'title' => sprintf($this->translate('Edit resource %s'), $name)
+ )
+ ) ?>
+ </td>
+ <td class="icon-col text-right">
+ <?= $this->qlink(
+ '',
+ 'config/removeresource',
+ array('resource' => $name),
+ array(
+ 'class' => 'action-link',
+ 'icon' => 'cancel',
+ 'title' => sprintf($this->translate('Remove resource %s'), $name)
+ )
+ ) ?>
+ </td>
+ </tr>
+<?php endforeach ?>
+ </tbody>
+ </table>
+</div>
diff --git a/application/views/scripts/config/resource/create.phtml b/application/views/scripts/config/resource/create.phtml
new file mode 100644
index 0000000..13a8ed9
--- /dev/null
+++ b/application/views/scripts/config/resource/create.phtml
@@ -0,0 +1,6 @@
+<div class="controls">
+ <?= $tabs ?>
+</div>
+<div class="content">
+ <?= $form ?>
+</div>
diff --git a/application/views/scripts/config/resource/modify.phtml b/application/views/scripts/config/resource/modify.phtml
new file mode 100644
index 0000000..13a8ed9
--- /dev/null
+++ b/application/views/scripts/config/resource/modify.phtml
@@ -0,0 +1,6 @@
+<div class="controls">
+ <?= $tabs ?>
+</div>
+<div class="content">
+ <?= $form ?>
+</div>
diff --git a/application/views/scripts/config/resource/remove.phtml b/application/views/scripts/config/resource/remove.phtml
new file mode 100644
index 0000000..13a8ed9
--- /dev/null
+++ b/application/views/scripts/config/resource/remove.phtml
@@ -0,0 +1,6 @@
+<div class="controls">
+ <?= $tabs ?>
+</div>
+<div class="content">
+ <?= $form ?>
+</div>
diff --git a/application/views/scripts/config/userbackend/reorder.phtml b/application/views/scripts/config/userbackend/reorder.phtml
new file mode 100644
index 0000000..c77fd2e
--- /dev/null
+++ b/application/views/scripts/config/userbackend/reorder.phtml
@@ -0,0 +1,75 @@
+<div class="controls">
+ <?= $tabs ?>
+</div>
+<div class="content">
+ <?php if ($this->auth()->hasPermission('config/access-control/users')): ?>
+ <h1><?= $this->translate('User Backends') ?></h1>
+ <?= $this->qlink(
+ $this->translate('Create a New User Backend') ,
+ 'config/createuserbackend',
+ null,
+ array(
+ 'class' => 'button-link',
+ 'data-base-target' => '_next',
+ 'icon' => 'plus',
+ 'title' => $this->translate('Create a new user backend')
+ )
+ ) ?>
+ <?= $form ?>
+ <?php endif ?>
+
+ <?php if ($this->auth()->hasPermission('config/access-control/groups')): ?>
+ <h1><?= $this->translate('User Group Backends') ?></h1>
+ <?= $this->qlink(
+ $this->translate('Create a New User Group Backend') ,
+ 'usergroupbackend/create',
+ null,
+ array(
+ 'class' => 'button-link',
+ 'data-base-target' => '_next',
+ 'icon' => 'plus',
+ 'title' => $this->translate('Create a new user group backend')
+ )
+ ) ?>
+<?php if (! count($backendNames)) { return; } ?>
+ <table class="table-row-selectable common-table" data-base-target="_next">
+ <thead>
+ <tr>
+ <th><?= $this->translate('Backend') ?></th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+<?php foreach ($backendNames as $backendName => $config):
+ $type = $config->get('backend');
+?>
+ <tr>
+ <td>
+ <?= $this->qlink(
+ $backendName,
+ 'usergroupbackend/edit',
+ array('backend' => $backendName),
+ array(
+ 'icon' => $type === 'external' ? 'magic' : ($type === 'ldap' || $type === 'msldap' ? 'sitemap' : 'database'),
+ 'title' => sprintf($this->translate('Edit user group backend %s'), $backendName)
+ )
+ ); ?>
+ </td>
+ <td class="icon-col text-right">
+ <?= $this->qlink(
+ null,
+ 'usergroupbackend/remove',
+ array('backend' => $backendName),
+ array(
+ 'class' => 'action-link',
+ 'icon' => 'cancel',
+ 'title' => sprintf($this->translate('Remove user group backend %s'), $backendName)
+ )
+ ) ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+ </tbody>
+ </table>
+ <?php endif ?>
+</div>
diff --git a/application/views/scripts/dashboard/error.phtml b/application/views/scripts/dashboard/error.phtml
new file mode 100644
index 0000000..9396b49
--- /dev/null
+++ b/application/views/scripts/dashboard/error.phtml
@@ -0,0 +1,13 @@
+<div class="content">
+ <h1><?= $this->translate('Could not save dashboard'); ?></h1>
+ <p>
+ <?= $this->translate('Please copy the following dashboard snippet to '); ?>
+ <strong><?= $this->config->getConfigFile(); ?>;</strong>.
+ <br>
+ <?= $this->translate('Make sure that the webserver can write to this file.'); ?>
+ </p>
+ <pre><?= $this->config; ?></pre>
+ <hr>
+ <h2><?= $this->translate('Error details'); ?></h2>
+ <p><?= $this->error->getMessage(); ?></p>
+</div> \ No newline at end of file
diff --git a/application/views/scripts/dashboard/index.phtml b/application/views/scripts/dashboard/index.phtml
new file mode 100644
index 0000000..1d56114
--- /dev/null
+++ b/application/views/scripts/dashboard/index.phtml
@@ -0,0 +1,26 @@
+<div class="controls">
+<?php if (! $this->compact): ?>
+<?= $this->tabs ?>
+<?php endif ?>
+</div>
+<?php if ($this->dashboard): ?>
+ <div class="dashboard content">
+ <?= $this->dashboard ?>
+ </div>
+<?php else: ?>
+ <div class="content">
+ <h1><?= $this->escape($this->translate('Welcome to Icinga Web!')) ?></h1>
+ <p>
+ <?php if (! $this->hasPermission('config/modules')) {
+ echo $this->escape($this->translate(
+ 'Currently there is no dashlet available. Please contact the administrator.'
+ ));
+ } else {
+ printf(
+ $this->escape($this->translate('Currently there is no dashlet available. This might change once you enabled some of the available %s.')),
+ $this->qlink($this->translate('modules'), 'config/modules')
+ );
+ } ?>
+ </p>
+ </div>
+<?php endif ?>
diff --git a/application/views/scripts/dashboard/new-dashlet.phtml b/application/views/scripts/dashboard/new-dashlet.phtml
new file mode 100644
index 0000000..b265a25
--- /dev/null
+++ b/application/views/scripts/dashboard/new-dashlet.phtml
@@ -0,0 +1,6 @@
+<div class="controls">
+ <?= $this->tabs ?>
+</div>
+<div class="content">
+ <?= $this->form; ?>
+</div> \ No newline at end of file
diff --git a/application/views/scripts/dashboard/remove-dashlet.phtml b/application/views/scripts/dashboard/remove-dashlet.phtml
new file mode 100644
index 0000000..b265a25
--- /dev/null
+++ b/application/views/scripts/dashboard/remove-dashlet.phtml
@@ -0,0 +1,6 @@
+<div class="controls">
+ <?= $this->tabs ?>
+</div>
+<div class="content">
+ <?= $this->form; ?>
+</div> \ No newline at end of file
diff --git a/application/views/scripts/dashboard/remove-pane.phtml b/application/views/scripts/dashboard/remove-pane.phtml
new file mode 100644
index 0000000..b265a25
--- /dev/null
+++ b/application/views/scripts/dashboard/remove-pane.phtml
@@ -0,0 +1,6 @@
+<div class="controls">
+ <?= $this->tabs ?>
+</div>
+<div class="content">
+ <?= $this->form; ?>
+</div> \ No newline at end of file
diff --git a/application/views/scripts/dashboard/rename-pane.phtml b/application/views/scripts/dashboard/rename-pane.phtml
new file mode 100644
index 0000000..b265a25
--- /dev/null
+++ b/application/views/scripts/dashboard/rename-pane.phtml
@@ -0,0 +1,6 @@
+<div class="controls">
+ <?= $this->tabs ?>
+</div>
+<div class="content">
+ <?= $this->form; ?>
+</div> \ No newline at end of file
diff --git a/application/views/scripts/dashboard/settings.phtml b/application/views/scripts/dashboard/settings.phtml
new file mode 100644
index 0000000..a6cfe83
--- /dev/null
+++ b/application/views/scripts/dashboard/settings.phtml
@@ -0,0 +1,91 @@
+<div class="controls">
+ <?= $this->tabs ?>
+</div>
+<div class="content">
+ <h1><?= t('Dashboard Settings'); ?></h1>
+
+ <table class="avp action" data-base-target="_next">
+ <thead>
+ <tr>
+ <th style="width: 18em;">
+ <strong><?= t('Dashlet Name') ?></strong>
+ </th>
+ <th>
+ <strong><?= t('Url') ?></strong>
+ </th>
+ <th style="width: 1.4em;">&nbsp;</th>
+ </tr>
+ </thead>
+ <tbody>
+ <?php foreach ($this->dashboard->getPanes() as $pane): ?>
+ <?php if ($pane->getDisabled()) continue; ?>
+ <tr>
+ <th colspan="2" style="text-align: left; padding: 0.5em;">
+ <?php if ($pane->isUserWidget()): ?>
+ <?= $this->qlink(
+ $pane->getName(),
+ 'dashboard/rename-pane',
+ array('pane' => $pane->getName()),
+ array('title' => sprintf($this->translate('Edit pane %s'), $pane->getName()))
+ ) ?>
+ <?php else: ?>
+ <?= $this->escape($pane->getName()) ?>
+ <?php endif ?>
+ </th>
+ <th>
+ <?= $this->qlink(
+ '',
+ 'dashboard/remove-pane',
+ array('pane' => $pane->getName()),
+ array(
+ 'icon' => 'trash',
+ 'title' => sprintf($this->translate('Remove pane %s'), $pane->getName())
+ )
+ ); ?>
+ </th>
+ </tr>
+ <?php $dashlets = $pane->getDashlets(); ?>
+ <?php if(empty($dashlets)): ?>
+ <tr>
+ <td colspan="3">
+ <?= $this->translate('No dashlets added to dashboard') ?>.
+ </td>
+ </tr>
+ <?php else: ?>
+ <?php foreach ($dashlets as $dashlet): ?>
+ <?php if ($dashlet->getDisabled()) continue; ?>
+ <tr>
+ <td>
+ <?= $this->qlink(
+ $dashlet->getTitle(),
+ 'dashboard/update-dashlet',
+ array('pane' => $pane->getName(), 'dashlet' => $dashlet->getName()),
+ array('title' => sprintf($this->translate('Edit dashlet %s'), $dashlet->getTitle()))
+ ); ?>
+ </td>
+ <td style="table-layout: fixed; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
+ <?= $this->qlink(
+ $dashlet->getUrl()->getRelativeUrl(),
+ $dashlet->getUrl()->getRelativeUrl(),
+ null,
+ array('title' => sprintf($this->translate('Show dashlet %s'), $dashlet->getTitle()))
+ ); ?>
+ </td>
+ <td>
+ <?= $this->qlink(
+ '',
+ 'dashboard/remove-dashlet',
+ array('pane' => $pane->getName(), 'dashlet' => $dashlet->getName()),
+ array(
+ 'icon' => 'trash',
+ 'title' => sprintf($this->translate('Remove dashlet %s from pane %s'), $dashlet->getTitle(), $pane->getTitle())
+ )
+ ); ?>
+ </td>
+ </tr>
+ <?php endforeach; ?>
+ <?php endif; ?>
+ <?php endforeach; ?>
+ </tbody>
+ </table>
+</div>
diff --git a/application/views/scripts/dashboard/update-dashlet.phtml b/application/views/scripts/dashboard/update-dashlet.phtml
new file mode 100644
index 0000000..b265a25
--- /dev/null
+++ b/application/views/scripts/dashboard/update-dashlet.phtml
@@ -0,0 +1,6 @@
+<div class="controls">
+ <?= $this->tabs ?>
+</div>
+<div class="content">
+ <?= $this->form; ?>
+</div> \ No newline at end of file
diff --git a/application/views/scripts/error/error.phtml b/application/views/scripts/error/error.phtml
new file mode 100644
index 0000000..3c7462f
--- /dev/null
+++ b/application/views/scripts/error/error.phtml
@@ -0,0 +1,106 @@
+<?php if (! $this->compact && ! $hideControls): ?>
+<div class="controls">
+ <?= $tabs ?>
+</div>
+<?php endif ?>
+<div class="content">
+<?php
+if (isset($stackTraces)) {
+ foreach ($messages as $i => $message) {
+ echo '<p tabindex="-1" class="autofocus error-message">' . nl2br($this->escape($message)) . '</p>'
+ . '<hr>'
+ . '<pre>' . $this->escape($stackTraces[$i]) . '</pre>';
+ }
+} else {
+ foreach ($messages as $message) {
+ echo '<p tabindex="-1" class="autofocus error-message">' . nl2br($this->escape($message)) . '</p>';
+ }
+}
+
+$libraries = \Icinga\Application\Icinga::app()->getLibraries();
+$coreReason = [];
+$modReason = [];
+
+if (isset($requiredVendor, $requiredProject) && $requiredVendor && $requiredProject) {
+ // TODO: I don't like this, can we define requirements somewhere else?
+ $coreDeps = ['icinga-php-library' => '>= 0.9', 'icinga-php-thirdparty' => '>= 0.11'];
+
+ foreach ($coreDeps as $libraryName => $requiredVersion) {
+ if (! $libraries->has($libraryName)) {
+ $coreReason[] = sprintf($this->translate(
+ 'Library "%s" is required and missing. Please install a version of it matching the required one: %s'
+ ), $libraryName, $requiredVersion);
+ } elseif (! $libraries->has($libraryName, $requiredVersion) && $libraries->get($libraryName)->isRequired($requiredVendor, $requiredProject)) {
+ $coreReason[] = sprintf($this->translate(
+ 'Library "%s" is required and installed, but its version (%s) does not satisfy the required one: %s'
+ ), $libraryName, $libraries->get($libraryName)->getVersion() ?: '-', $requiredVersion);
+ }
+ }
+
+ if (! empty($coreReason)) {
+ array_unshift($coreReason, $this->translate('You have unmet dependencies. Please check Icinga Web 2\'s installation instructions.'));
+ }
+}
+
+if (isset($module)) {
+ $manager = \Icinga\Application\Icinga::app()->getModuleManager();
+ if ($manager->hasUnmetDependencies($module->getName())) {
+ if (isset($requiredModule) && $requiredModule && isset($module->getRequiredModules()[$requiredModule])) {
+ if (! $manager->hasInstalled($requiredModule)) {
+ $modReason[] = sprintf($this->translate(
+ 'Module "%s" is required and missing. Please install a version of it matching the required one: %s'
+ ), $requiredModule, $module->getRequiredModules()[$requiredModule]);
+ } elseif (! $manager->hasEnabled($requiredModule)) {
+ $modReason[] = sprintf($this->translate(
+ 'Module "%s" is required and installed, but not enabled. Please enable module "%1$s".'
+ ), $requiredModule);
+ } elseif (! $manager->has($requiredModule, $module->getRequiredModules()[$requiredModule])) {
+ $modReason[] = sprintf($this->translate(
+ 'Module "%s" is required and installed, but its version (%s) does not satisfy the required one: %s'
+ ), $requiredModule, $manager->getModule($requiredModule, false)->getVersion(), $module->getRequiredModules()[$requiredModule]);
+ }
+ } elseif (isset($requiredVendor, $requiredProject) && $requiredVendor && $requiredProject) {
+ foreach ($module->getRequiredLibraries() as $libraryName => $requiredVersion) {
+ if (! $libraries->has($libraryName)) {
+ $modReason[] = sprintf($this->translate(
+ 'Library "%s" is required and missing. Please install a version of it matching the required one: %s'
+ ), $libraryName, $requiredVersion);
+ } elseif (! $libraries->has($libraryName, $requiredVersion) && $libraries->get($libraryName)->isRequired($requiredVendor, $requiredProject)) {
+ $modReason[] = sprintf($this->translate(
+ 'Library "%s" is required and installed, but its version (%s) does not satisfy the required one: %s'
+ ), $libraryName, $libraries->get($libraryName)->getVersion() ?: '-', $requiredVersion);
+ }
+ }
+ }
+
+ if (! empty($modReason)) {
+ array_unshift($modReason, sprintf($this->translate(
+ 'This error might have occurred because module "%s" has unmet dependencies.'
+ . ' Please check it\'s installation instructions and install missing dependencies.'
+ ), $module->getName()));
+ }
+ }
+}
+
+// The following doesn't use ipl\Html because that's what the error possibly is about
+?>
+<?php if (! empty($coreReason)): ?>
+<div class="error-reason">
+<?php endif ?>
+<?php foreach ($coreReason as $msg): ?>
+ <p><?= $msg ?></p>
+<?php endforeach ?>
+<?php if (! empty($coreReason)): ?>
+</div>
+<?php endif ?>
+
+<?php if (! empty($modReason)): ?>
+<div class="error-reason">
+<?php endif ?>
+<?php foreach ($modReason as $msg): ?>
+ <p><?= $msg ?></p>
+<?php endforeach ?>
+<?php if (! empty($modReason)): ?>
+</div>
+<?php endif ?>
+</div>
diff --git a/application/views/scripts/filter/index.phtml b/application/views/scripts/filter/index.phtml
new file mode 100644
index 0000000..5e6a63d
--- /dev/null
+++ b/application/views/scripts/filter/index.phtml
@@ -0,0 +1,11 @@
+<?php
+
+echo $this->form;
+
+if ($this->tree) {
+ echo $this->tree->render($this);
+ echo '<br/><pre><code>';
+ echo $this->sqlString;
+ echo '</pre></code>';
+ print_r($this->params);
+} \ No newline at end of file
diff --git a/application/views/scripts/form/reorder-authbackend.phtml b/application/views/scripts/form/reorder-authbackend.phtml
new file mode 100644
index 0000000..34b10b3
--- /dev/null
+++ b/application/views/scripts/form/reorder-authbackend.phtml
@@ -0,0 +1,83 @@
+<form id="<?=
+$this->escape($form->getId())
+?>" name="<?=
+$this->escape($form->getName())
+?>" enctype="<?=
+$this->escape($form->getEncType())
+?>" method="<?=
+$this->escape($form->getMethod())
+?>" action="<?=
+$this->escape($form->getAction())
+?>">
+ <table class="table-row-selectable common-table" data-base-target="_next">
+ <thead>
+ <th><?= $this->translate('Backend') ?></th>
+ <th></th>
+ <th></th>
+ </thead>
+ <tbody>
+<?php
+ $backendNames = $form->getBackendOrder();
+ $backendConfigs = $form->getConfig();
+ for ($i = 0; $i < count($backendNames); $i++):
+ $type = $backendConfigs->getSection($backendNames[$i])->get('backend');
+?>
+ <tr>
+ <td class="action">
+ <?= $this->qlink(
+ $backendNames[$i],
+ 'config/edituserbackend',
+ array('backend' => $backendNames[$i]),
+ array(
+ 'icon' => $type === 'external' ?
+ 'magic' : ($type === 'ldap' || $type === 'msldap' ? 'sitemap' : 'database'),
+ 'class' => 'rowaction',
+ 'title' => sprintf($this->translate('Edit user backend %s'), $backendNames[$i])
+ )
+ ) ?>
+ </td>
+ <td class="icon-col text-right">
+ <?= $this->qlink(
+ '',
+ 'config/removeuserbackend',
+ array('backend' => $backendNames[$i]),
+ array(
+ 'class' => 'action-link',
+ 'icon' => 'cancel',
+ 'title' => sprintf($this->translate('Remove user backend %s'), $backendNames[$i])
+ )
+ ) ?>
+ </td>
+ <td class="icon-col text-right" data-base-target="_self">
+<?php if ($i > 0): ?>
+ <button type="submit" name="backend_newpos" class="link-button icon-only animated move-up" value="<?= $this->escape(
+ $backendNames[$i] . '|' . ($i - 1)
+ ) ?>" title="<?= $this->translate(
+ 'Move up in authentication order'
+ ) ?>" aria-label="<?= $this->escape(sprintf(
+ $this->translate('Move user backend %s upwards'),
+ $backendNames[$i]
+ )) ?>">
+ <?= $this->icon('up-small') ?>
+ </button>
+<?php endif ?>
+<?php if ($i + 1 < count($backendNames)): ?>
+ <button type="submit" name="backend_newpos" class="link-button icon-only animated move-down" value="<?= $this->escape(
+ $backendNames[$i] . '|' . ($i + 1)
+ ) ?>" title="<?= $this->translate(
+ 'Move down in authentication order'
+ ) ?>" aria-label="<?= $this->escape(sprintf(
+ $this->translate('Move user backend %s downwards'),
+ $backendNames[$i]
+ )) ?>">
+ <?= $this->icon('down-small') ?>
+ </button>
+<?php endif ?>
+ </td>
+ </tr>
+<?php endfor ?>
+ </tbody>
+ </table>
+ <?= $form->getElement($form->getTokenElementName()) ?>
+ <?= $form->getElement($form->getUidElementName()) ?>
+</form>
diff --git a/application/views/scripts/group/form.phtml b/application/views/scripts/group/form.phtml
new file mode 100644
index 0000000..cbf0659
--- /dev/null
+++ b/application/views/scripts/group/form.phtml
@@ -0,0 +1,6 @@
+<div class="controls">
+ <?= $tabs->showOnlyCloseButton(); ?>
+</div>
+<div class="content">
+ <?= $form; ?>
+</div> \ No newline at end of file
diff --git a/application/views/scripts/group/list.phtml b/application/views/scripts/group/list.phtml
new file mode 100644
index 0000000..d362db4
--- /dev/null
+++ b/application/views/scripts/group/list.phtml
@@ -0,0 +1,96 @@
+<?php
+
+use Icinga\Data\Extensible;
+use Icinga\Data\Reducible;
+
+if (! $this->compact): ?>
+<div class="controls separated">
+ <?= $this->tabs ?>
+ <?= $this->paginator ?>
+ <div class="sort-controls-container">
+ <?= $this->limiter ?>
+ <?= $this->sortBox ?>
+ </div>
+ <?= $this->backendSelection ?>
+ <?= $this->filterEditor ?>
+</div>
+<?php endif ?>
+<div class="content">
+<?php
+
+if (! isset($backend)) {
+ echo $this->translate('No backend found which is able to list user groups') . '</div>';
+ return;
+} else {
+ $extensible = $this->hasPermission('config/access-control/groups') && $backend instanceof Extensible;
+ $reducible = $this->hasPermission('config/access-control/groups') && $backend instanceof Reducible;
+}
+?>
+
+<?php if ($extensible): ?>
+ <?= $this->qlink(
+ $this->translate('Add a New User Group'),
+ 'group/add',
+ array('backend' => $backend->getName()),
+ array(
+ 'class' => 'button-link',
+ 'data-base-target' => '_next',
+ 'icon' => 'plus'
+ )
+ ) ?>
+<?php endif ?>
+
+<?php if (! $groups->hasResult()): ?>
+ <p><?= $this->translate('No user groups found matching the filter'); ?></p>
+</div>
+<?php return; endif ?>
+ <table data-base-target="_next" class="table-row-selectable common-table">
+ <thead>
+ <tr>
+ <th><?= $this->translate('User Group'); ?></th>
+ <?php if ($reducible): ?>
+ <th><?= $this->translate('Remove'); ?></th>
+ <?php endif ?>
+ </tr>
+ </thead>
+ <tbody>
+ <?php foreach ($groups as $group): ?>
+ <tr>
+ <td>
+ <?= $this->qlink(
+ $group->group_name,
+ 'group/show',
+ array(
+ 'backend' => $backend->getName(),
+ 'group' => $group->group_name
+ ),
+ array(
+ 'title' => sprintf(
+ $this->translate('Show detailed information for user group %s'),
+ $group->group_name
+ )
+ )
+ ); ?>
+ </td>
+ <?php if ($reducible): ?>
+ <td class="icon-col">
+ <?= $this->qlink(
+ null,
+ 'group/remove',
+ array(
+ 'backend' => $backend->getName(),
+ 'group' => $group->group_name
+ ),
+ array(
+ 'class' => 'action-link',
+ 'title' => sprintf($this->translate('Remove user group %s'), $group->group_name),
+ 'icon' => 'cancel'
+ )
+ ); ?>
+ </td>
+ <?php endif ?>
+ </tr>
+ <?php endforeach ?>
+ </tbody>
+ </table>
+</div>
diff --git a/application/views/scripts/group/show.phtml b/application/views/scripts/group/show.phtml
new file mode 100644
index 0000000..75f0b75
--- /dev/null
+++ b/application/views/scripts/group/show.phtml
@@ -0,0 +1,108 @@
+<?php
+
+use Icinga\Data\Extensible;
+use Icinga\Data\Updatable;
+
+$extensible = $this->hasPermission('config/access-control/groups') && $backend instanceof Extensible;
+
+$editLink = null;
+if ($this->hasPermission('config/access-control/groups') && $backend instanceof Updatable) {
+ $editLink = $this->qlink(
+ null,
+ 'group/edit',
+ array(
+ 'backend' => $backend->getName(),
+ 'group' => $group->group_name
+ ),
+ array(
+ 'title' => sprintf($this->translate('Edit group %s'), $group->group_name),
+ 'class' => 'group-edit',
+ 'icon' => 'edit'
+ )
+ );
+}
+
+?>
+<div class="controls separated">
+<?php if (! $this->compact): ?>
+ <?= $tabs; ?>
+<?php endif ?>
+ <h2 class="clearfix"><?= $this->escape($group->group_name) ?><span class="pull-right"><?= $editLink ?></span></h2>
+ <table class="name-value-table">
+ <tr>
+ <th><?= $this->translate('Created at'); ?></th>
+ <td><?= $group->created_at === null ? '-' : $this->formatDateTime($group->created_at); ?></td>
+ </tr>
+ <tr>
+ <th><?= $this->translate('Last modified'); ?></th>
+ <td><?= $group->last_modified === null ? '-' : $this->formatDateTime($group->last_modified); ?></td>
+ </tr>
+ </table>
+<?php if (! $this->compact): ?>
+ <h2><?= $this->translate('Members'); ?></h2>
+ <div class="sort-controls-container">
+ <?= $this->limiter; ?>
+ <?= $this->paginator; ?>
+ <?= $this->sortBox; ?>
+ </div>
+ <?= $this->filterEditor; ?>
+<?php endif ?>
+</div>
+<div class="content">
+<?php if ($extensible): ?>
+ <?= $this->qlink(
+ $this->translate('Add New Member'),
+ 'group/addmember',
+ array(
+ 'backend' => $backend->getName(),
+ 'group' => $group->group_name
+ ),
+ array(
+ 'icon' => 'plus',
+ 'class' => 'button-link'
+ )
+ ) ?>
+<?php endif ?>
+
+<?php if (! $members->hasResult()): ?>
+ <p><?= $this->translate('No group member found matching the filter'); ?></p>
+</div>
+<?php return; endif ?>
+
+ <table data-base-target="_next" class="table-row-selectable common-table">
+ <thead>
+ <tr>
+ <th><?= $this->translate('Username'); ?></th>
+ <?php if (isset($removeForm)): ?>
+ <th><?= $this->translate('Remove'); ?></th>
+ <?php endif ?>
+ </tr>
+ </thead>
+ <tbody>
+ <?php foreach ($members as $member): ?>
+ <tr>
+ <td>
+ <?php if (
+ $this->hasPermission('config/access-control/users')
+ && ($userBackend = $backend->getUserBackendName($member->user_name)) !== null
+ ): ?>
+ <?= $this->qlink($member->user_name, 'user/show', array(
+ 'backend' => $userBackend,
+ 'user' => $member->user_name
+ ), array(
+ 'title' => sprintf($this->translate('Show detailed information about %s'), $member->user_name)
+ )); ?>
+ <?php else: ?>
+ <?= $this->escape($member->user_name); ?>
+ <?php endif ?>
+ </td>
+ <?php if (isset($removeForm)): ?>
+ <td class="icon-col" data-base-target="_self">
+ <?php $removeForm->getElement('user_name')->setValue($member->user_name); echo $removeForm; ?>
+ </td>
+ <?php endif ?>
+ </tr>
+ <?php endforeach ?>
+ </tbody>
+ </table>
+</div>
diff --git a/application/views/scripts/iframe/index.phtml b/application/views/scripts/iframe/index.phtml
new file mode 100644
index 0000000..96e9de7
--- /dev/null
+++ b/application/views/scripts/iframe/index.phtml
@@ -0,0 +1,8 @@
+<?php if (! $compact): ?>
+<div class="controls">
+ <?= $tabs ?>
+</div>
+<?php endif ?>
+<div class="iframe-container">
+ <iframe src="<?= $this->escape($url) ?>" frameborder="no"></iframe>
+</div>
diff --git a/application/views/scripts/index/welcome.phtml b/application/views/scripts/index/welcome.phtml
new file mode 100644
index 0000000..496dec9
--- /dev/null
+++ b/application/views/scripts/index/welcome.phtml
@@ -0,0 +1,2 @@
+<h1>Welcome to Icinga!</h1>
+You should install/configure some <a href="<?= $this->href('config/modules');?>">modules</a> now!
diff --git a/application/views/scripts/inline.phtml b/application/views/scripts/inline.phtml
new file mode 100644
index 0000000..2534d44
--- /dev/null
+++ b/application/views/scripts/inline.phtml
@@ -0,0 +1,2 @@
+<?= $this->layout()->content ?>
+
diff --git a/application/views/scripts/joystickPagination.phtml b/application/views/scripts/joystickPagination.phtml
new file mode 100644
index 0000000..a8c24c9
--- /dev/null
+++ b/application/views/scripts/joystickPagination.phtml
@@ -0,0 +1,162 @@
+<?php
+
+use Icinga\Web\Url;
+
+$showText = $this->translate('%s: Show %s %u to %u out of %u', 'pagination.joystick');
+$xAxisPages = $xAxisPaginator->getPages('all');
+$yAxisPages = $yAxisPaginator->getPages('all');
+
+$flipUrl = Url::fromRequest();
+if ($flipUrl->getParam('flipped')) {
+ $flipUrl->remove('flipped');
+} else {
+ $flipUrl->setParam('flipped');
+}
+if ($flipUrl->hasParam('page')) {
+ $flipUrl->setParam('page', implode(',', array_reverse(explode(',', $flipUrl->getParam('page')))));
+}
+if ($flipUrl->hasParam('limit')) {
+ $flipUrl->setParam('limit', implode(',', array_reverse(explode(',', $flipUrl->getParam('limit')))));
+}
+
+$totalYAxisPages = $yAxisPaginator->count();
+$currentYAxisPage = $yAxisPaginator->getCurrentPageNumber();
+$prevYAxisPage = $currentYAxisPage > 1 ? $currentYAxisPage - 1 : null;
+$nextYAxisPage = $currentYAxisPage < $totalYAxisPages ? $currentYAxisPage + 1 : null;
+
+$totalXAxisPages = $xAxisPaginator->count();
+$currentXAxisPage = $xAxisPaginator->getCurrentPageNumber();
+$prevXAxisPage = $currentXAxisPage > 1 ? $currentXAxisPage - 1 : null;
+$nextXAxisPage = $currentXAxisPage < $totalXAxisPages ? $currentXAxisPage + 1 : null;
+
+?>
+
+<table class="joystick-pagination">
+ <tbody>
+ <tr>
+ <td>&nbsp;</td>
+ <td>
+ <?php if ($prevYAxisPage): ?>
+ <?= $this->qlink(
+ '',
+ Url::fromRequest(),
+ array(
+ 'page' => $currentXAxisPage . ',' . $prevYAxisPage
+ ),
+ array(
+ 'icon' => 'up-open',
+ 'data-base-target' => '_self',
+ 'title' => sprintf(
+ $showText,
+ $this->translate('Y-Axis', 'pagination.joystick'),
+ $this->translate('hosts', 'pagination.joystick'),
+ ($prevYAxisPage - 1) * $yAxisPages->itemCountPerPage + 1,
+ $prevYAxisPage * $yAxisPages->itemCountPerPage,
+ $yAxisPages->totalItemCount
+ )
+ )
+ ); ?>
+ <?php else: ?>
+ <?= $this->icon('up-open'); ?>
+ <?php endif ?>
+ </td>
+ <td>&nbsp;</td>
+ </tr>
+ <tr>
+ <td>
+ <?php if ($prevXAxisPage): ?>
+ <?= $this->qlink(
+ '',
+ Url::fromRequest(),
+ array(
+ 'page' => $prevXAxisPage . ',' . $currentYAxisPage
+ ),
+ array(
+ 'icon' => 'left-open',
+ 'data-base-target' => '_self',
+ 'title' => sprintf(
+ $showText,
+ $this->translate('X-Axis', 'pagination.joystick'),
+ $this->translate('services', 'pagination.joystick'),
+ ($prevXAxisPage - 1) * $xAxisPages->itemCountPerPage + 1,
+ $prevXAxisPage * $xAxisPages->itemCountPerPage,
+ $xAxisPages->totalItemCount
+ )
+ )
+ ); ?>
+ <?php else: ?>
+ <?= $this->icon('left-open'); ?>
+ <?php endif ?>
+ </td>
+ <?php if ($this->flippable): ?>
+ <td><?= $this->qlink(
+ '',
+ $flipUrl,
+ null,
+ array(
+ 'icon' => 'arrows-cw',
+ 'data-base-target' => '_self',
+ 'title' => $this->translate('Flip grid axes')
+ )
+ ) ?></td>
+ <?php else: ?>
+ <td>&nbsp;</td>
+ <?php endif ?>
+ <td>
+ <?php if ($nextXAxisPage): ?>
+ <?= $this->qlink(
+ '',
+ Url::fromRequest(),
+ array(
+ 'page' => $nextXAxisPage . ',' . $currentYAxisPage
+ ),
+ array(
+ 'icon' => 'right-open',
+ 'data-base-target' => '_self',
+ 'title' => sprintf(
+ $showText,
+ $this->translate('X-Axis', 'pagination.joystick'),
+ $this->translate('services', 'pagination.joystick'),
+ $currentXAxisPage * $xAxisPages->itemCountPerPage + 1,
+ $nextXAxisPage === $xAxisPages->last ? $xAxisPages->totalItemCount : $nextXAxisPage * $xAxisPages->itemCountPerPage,
+ $xAxisPages->totalItemCount
+ )
+ ),
+ false
+ ); ?>
+ <?php else: ?>
+ <?= $this->icon('right-open'); ?>
+ <?php endif ?>
+ </td>
+ </tr>
+ <tr>
+ <td>&nbsp;</td>
+ <td>
+ <?php if ($nextYAxisPage): ?>
+ <?= $this->qlink(
+ '',
+ Url::fromRequest(),
+ array(
+ 'page' => $currentXAxisPage . ',' . $nextYAxisPage
+ ),
+ array(
+ 'icon' => 'down-open',
+ 'data-base-target' => '_self',
+ 'title' => sprintf(
+ $showText,
+ $this->translate('Y-Axis', 'pagination.joystick'),
+ $this->translate('hosts', 'pagination.joystick'),
+ $currentYAxisPage * $yAxisPages->itemCountPerPage + 1,
+ $nextYAxisPage === $yAxisPages->last ? $yAxisPages->totalItemCount : $nextYAxisPage * $yAxisPages->itemCountPerPage,
+ $yAxisPages->totalItemCount
+ )
+ )
+ ); ?>
+ <?php else: ?>
+ <?= $this->icon('down-open'); ?>
+ <?php endif ?>
+ </td>
+ <td>&nbsp;</td>
+ </tr>
+ </tbody>
+</table>
diff --git a/application/views/scripts/layout/announcements.phtml b/application/views/scripts/layout/announcements.phtml
new file mode 100644
index 0000000..3be6b83
--- /dev/null
+++ b/application/views/scripts/layout/announcements.phtml
@@ -0,0 +1 @@
+<?= $this->widget('announcements') ?>
diff --git a/application/views/scripts/layout/menu.phtml b/application/views/scripts/layout/menu.phtml
new file mode 100644
index 0000000..dfb544d
--- /dev/null
+++ b/application/views/scripts/layout/menu.phtml
@@ -0,0 +1,20 @@
+<?php
+
+use Icinga\Web\Navigation\ConfigMenu;
+use Icinga\Web\Widget\SearchDashboard;
+
+$searchDashboard = new SearchDashboard();
+$searchDashboard->setUser($this->Auth()->getUser());
+
+if ($searchDashboard->search('dummy')->getPane('search')->hasDashlets()): ?>
+ <form action="<?= $this->href('search') ?>" method="get" role="search" class="search-control">
+ <input type="text" name="q" id="search" class="search search-input" required
+ placeholder="<?= $this->translate('Search') ?> &hellip;"
+ autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">
+ <button class="search-reset icon-cancel" type="reset"></button>
+ </form>
+<?php endif; ?>
+<?= $menuRenderer->setCssClass('primary-nav')->setElementTag('nav')->setHeading(t('Navigation')); ?>
+<nav class="config-menu">
+ <?= new ConfigMenu() ?>
+</nav>
diff --git a/application/views/scripts/list/applicationlog.phtml b/application/views/scripts/list/applicationlog.phtml
new file mode 100644
index 0000000..aa1a90a
--- /dev/null
+++ b/application/views/scripts/list/applicationlog.phtml
@@ -0,0 +1,29 @@
+<?php if (! $this->compact): ?>
+<div class="controls separated">
+ <?= $this->tabs ?>
+ <?= $this->paginator ?>
+ <div class="sort-controls-container">
+ <?= $this->limiter ?>
+ </div>
+</div>
+<?php endif ?>
+<div class="content">
+<?php if ($this->logData !== null): ?>
+ <table class="action">
+ <tbody>
+ <?php foreach ($this->logData as $value): ?>
+ <?php $datetime = new Datetime($value->datetime) ?>
+ <tr class="state">
+ <td style="width: 6em; text-align: center">
+ <?= $this->escape($datetime->format('d.m. H:i')) ?><br />
+ <?= $this->escape($value->loglevel) ?>
+ </td>
+ <td>
+ <?= nl2br($this->escape($value->message), false) ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+ </tbody>
+ </table>
+<?php endif ?>
+</div>
diff --git a/application/views/scripts/mixedPagination.phtml b/application/views/scripts/mixedPagination.phtml
new file mode 100644
index 0000000..e92a9c9
--- /dev/null
+++ b/application/views/scripts/mixedPagination.phtml
@@ -0,0 +1,79 @@
+<?php if ($this->pageCount <= 1) return; ?>
+<div class="pagination-control" role="navigation">
+ <h2 id="<?= $this->protectId('pagination') ?>" class="sr-only" tabindex="-1"><?= $this->translate('Pagination') ?></h2>
+ <ul class="nav tab-nav">
+ <?php if (isset($this->previous)): ?>
+ <?php $label = sprintf(
+ $this->translate('Show rows %u to %u of %u'),
+ ($this->current - 2) * $this->itemCountPerPage + 1,
+ ($this->current - 1) * $this->itemCountPerPage,
+ $this->totalItemCount
+ ) ?>
+ <li class="nav-item">
+ <a href="<?= $this->escape($this->url()->overwriteParams(array('page' => $this->previous))->getAbsoluteUrl()) ?>"
+ title="<?= $label ?>"
+ aria-label="<?= $label ?>"
+ class="previous-page">
+ <?= $this->icon('angle-double-left') ?>
+ </a>
+ </li>
+ <?php else: ?>
+ <li class="nav-item disabled" aria-hidden="true">
+ <span class="previous-page">
+ <span class="sr-only"><?= $this->translate('Previous page') ?></span>
+ <?= $this->icon('angle-double-left') ?>
+ </span>
+ </li>
+ <?php endif ?>
+ <?php foreach ($this->pagesInRange as $page): ?>
+ <?php if ($page === '...'): ?>
+ <li class="nav-item disabled">
+ <span>...</span>
+ </li>
+ <?php else: ?>
+ <?php
+ $end = $page * $this->itemCountPerPage;
+ if ($end > $this->totalItemCount) {
+ $end = $this->totalItemCount;
+ }
+ $label = sprintf(
+ $this->translate('Show rows %u to %u of %u'),
+ ($page - 1) * $this->itemCountPerPage + 1,
+ $end,
+ $this->totalItemCount
+ );
+ ?>
+ <li<?= $page === $this->current ? ' class="active nav-item"' : ' class="nav-item"' ?>>
+ <a href="<?= $this->escape($this->url()->overwriteParams(array('page' => $page))->getAbsoluteUrl()) ?>"
+ title="<?= $label ?>"
+ aria-label="<?= $label ?>">
+ <?= $page ?>
+ </a>
+ </li>
+ <?php endif ?>
+ <?php endforeach ?>
+ <?php if (isset($this->next)): ?>
+ <?php $label = sprintf(
+ $this->translate('Show rows %u to %u of %u'),
+ $this->current * $this->itemCountPerPage + 1,
+ ($this->current + 1) * $this->itemCountPerPage,
+ $this->totalItemCount
+ ) ?>
+ <li class="nav-item">
+ <a href="<?= $this->escape($this->url()->overwriteParams(array('page' => $this->next))->getAbsoluteUrl()) ?>"
+ title="<?= $label ?>"
+ aria-label="<?= $label ?>"
+ class="next-page">
+ <?= $this->icon('angle-double-right') ?>
+ </a>
+ </li>
+ <?php else: ?>
+ <li class="disabled nav-item" aria-hidden="true">
+ <span class="next-page">
+ <span class="sr-only"><?= $this->translate('Next page') ?></span>
+ <?= $this->icon('angle-double-right') ?>
+ </span>
+ </li>
+ <?php endif ?>
+ </ul>
+</div>
diff --git a/application/views/scripts/navigation/dashboard.phtml b/application/views/scripts/navigation/dashboard.phtml
new file mode 100644
index 0000000..f069882
--- /dev/null
+++ b/application/views/scripts/navigation/dashboard.phtml
@@ -0,0 +1,27 @@
+<?php
+
+use ipl\Web\Widget\Icon;
+
+?>
+<div class="controls">
+ <?= $tabs ?>
+</div>
+<div class="content">
+ <?php foreach ($navigation as $item): /** @var \Icinga\Web\Navigation\NavigationItem $item */?>
+ <a class="dashboard-link" href="<?= $this->url($item->getUrl(), $item->getUrlParameters()) ?>"<?= $this->propertiesToString($item->getAttributes()) ?>>
+ <div class="link-icon">
+ <?php
+ if (substr($item->getUrl()->getPath(), 0, 9) === 'icingadb/') {
+ echo new Icon($item->getIcon(), [ 'aria-hidden' => 1]);
+ } else {
+ echo $this->icon($item->getIcon() ?: 'forward', null, array('aria-hidden' => true));
+ }
+ ?>
+ </div>
+ <div class="link-meta">
+ <div class="link-label"><?= $this->escape($item->getLabel()) ?></div>
+ <div class="link-description"><?= $this->escape($item->getDescription() ?: sprintf('Open %s', strtolower($item->getLabel()))) ?></div>
+ </div>
+ </a>
+ <?php endforeach ?>
+</div>
diff --git a/application/views/scripts/navigation/index.phtml b/application/views/scripts/navigation/index.phtml
new file mode 100644
index 0000000..bf08562
--- /dev/null
+++ b/application/views/scripts/navigation/index.phtml
@@ -0,0 +1,78 @@
+<?php if (! $this->compact): ?>
+<div class="controls separated">
+ <?= $this->tabs ?>
+ <div class="grid">
+ <?= $this->sortBox ?>
+ </div>
+ <?= $this->filterEditor ?>
+</div>
+<?php endif ?>
+<div class="content">
+ <?= $this->qlink(
+ $this->translate('Create a New Navigation Item') ,
+ 'navigation/add',
+ null,
+ array(
+ 'class' => 'button-link',
+ 'data-base-target' => '_next',
+ 'icon' => 'plus',
+ 'title' => $this->translate('Create a new navigation item')
+ )
+ ) ?>
+<?php if (count($items) === 0): ?>
+ <p><?= $this->translate('You did not create any navigation item yet.') ?></p>
+</div>
+<?php return; endif ?>
+ <table class="table-row-selectable common-table" data-base-target="_next">
+ <thead>
+ <tr>
+ <th><?= $this->translate('Navigation') ?></th>
+ <th><?= $this->translate('Type') ?></th>
+ <th><?= $this->translate('Shared') ?></th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+<?php foreach ($items as $item): ?>
+ <tr>
+ <td>
+ <?= $this->qlink(
+ $item->name,
+ 'navigation/edit',
+ array(
+ 'name' => $item->name,
+ 'type' => $item->type
+ ),
+ array(
+ 'title' => sprintf($this->translate('Edit navigation item %s'), $item->name)
+ )
+ ) ?>
+ </td>
+ <td>
+ <?= $item->type && isset($types[$item->type])
+ ? $this->escape($types[$item->type])
+ : $this->escape($this->translate('Unknown')) ?>
+ </td>
+ <td class="icon-col">
+ <?= $item->owner ? $this->translate('Yes') : $this->translate('No') ?>
+ </td>
+ <td class="icon-col text-right">
+ <?= $this->qlink(
+ '',
+ 'navigation/remove',
+ array(
+ 'name' => $item->name,
+ 'type' => $item->type
+ ),
+ array(
+ 'class' => 'action-link',
+ 'icon' => 'cancel',
+ 'title' => sprintf($this->translate('Remove navigation item %s'), $item->name)
+ )
+ ) ?>
+ </td>
+ </tr>
+<?php endforeach ?>
+ </tbody>
+ </table>
+</div>
diff --git a/application/views/scripts/navigation/shared.phtml b/application/views/scripts/navigation/shared.phtml
new file mode 100644
index 0000000..b6739fb
--- /dev/null
+++ b/application/views/scripts/navigation/shared.phtml
@@ -0,0 +1,68 @@
+<?php
+
+use Icinga\Web\Url;
+
+if (! $this->compact): ?>
+<div class="controls">
+ <?= $this->tabs; ?>
+ <div class="grid">
+ <?= $this->sortBox ?>
+ </div>
+</div>
+<?php endif ?>
+<div class="content" data-base-target="_next">
+<?php if (count($items) === 0): ?>
+ <p><?= $this->translate('There are currently no navigation items being shared'); ?></p>
+<?php else: ?>
+ <table class="table-row-selectable common-table">
+ <thead>
+ <th><?= $this->translate('Shared Navigation'); ?></th>
+ <th style="width: 10em"><?= $this->translate('Type'); ?></th>
+ <th style="width: 10em"><?= $this->translate('Owner'); ?></th>
+ <th style="width: 5em"><?= $this->translate('Unshare'); ?></th>
+ </thead>
+ <tbody>
+ <?php foreach ($items as $item): ?>
+ <tr>
+ <td><?= $this->qlink(
+ $item->name,
+ 'navigation/edit',
+ array(
+ 'name' => $item->name,
+ 'type' => $item->type,
+ 'owner' => $item->owner,
+ 'referrer' => 'shared'
+ ),
+ array(
+ 'title' => sprintf($this->translate('Edit shared navigation item %s'), $item->name)
+ )
+ ); ?></td>
+ <td><?= $item->type && isset($types[$item->type])
+ ? $this->escape($types[$item->type])
+ : $this->escape($this->translate('Unknown')); ?></td>
+ <td><?= $this->escape($item->owner); ?></td>
+ <?php if ($item->parent): ?>
+ <td><?= $this->icon(
+ 'block',
+ sprintf(
+ $this->translate(
+ 'This is a child of the navigation item %1$s. You can'
+ . ' only unshare this item by unsharing %1$s'
+ ),
+ $item->parent
+ )
+ ); ?></td>
+ <?php else: ?>
+ <td data-base-target="_self"><?= $removeForm
+ ->setDefault('name', $item->name)
+ ->setAction(Url::fromPath(
+ 'navigation/unshare',
+ array('type' => $item->type, 'owner' => $item->owner)
+ )); ?></td>
+ <?php endif ?>
+ </tr>
+ <?php endforeach ?>
+ </tbody>
+ </table>
+<?php endif ?>
+</div>
diff --git a/application/views/scripts/pivottablePagination.phtml b/application/views/scripts/pivottablePagination.phtml
new file mode 100644
index 0000000..ce18014
--- /dev/null
+++ b/application/views/scripts/pivottablePagination.phtml
@@ -0,0 +1,48 @@
+<?php
+
+use Icinga\Web\Url;
+
+if ($xAxisPaginator->count() <= 1 && $yAxisPaginator->count() <= 1) {
+ return; // Display this pagination only if there are multiple pages
+}
+
+$fromTo = t('%s: %d to %d of %d (on the %s-axis)');
+$xAxisPages = $xAxisPaginator->getPages('all');
+$yAxisPages = $yAxisPaginator->getPages('all');
+
+?>
+
+<div class="pivot-pagination">
+ <span><?= t('Navigation'); ?></span>
+ <table>
+ <tbody>
+<?php foreach ($yAxisPages->pagesInRange as $yAxisPage): ?>
+ <tr>
+<?php foreach ($xAxisPages->pagesInRange as $xAxisPage): ?>
+ <td<?= $xAxisPage === $xAxisPages->current && $yAxisPage === $yAxisPages->current ? ' class="active"' : ''; ?>>
+<?php if ($xAxisPage !== $xAxisPages->current || $yAxisPage !== $yAxisPages->current): ?>
+ <a href="<?= Url::fromRequest()->overwriteParams(
+ array('page' => $xAxisPage . ',' . $yAxisPage)
+ )->getAbsoluteUrl(); ?>" title="<?= sprintf(
+ $fromTo,
+ t('Hosts'),
+ ($yAxisPage - 1) * $yAxisPages->itemCountPerPage + 1,
+ $yAxisPage === $yAxisPages->last ? $yAxisPages->totalItemCount : $yAxisPage * $yAxisPages->itemCountPerPage,
+ $yAxisPages->totalItemCount,
+ 'y'
+ ) . '; ' . sprintf(
+ $fromTo,
+ t('Services'),
+ ($xAxisPage - 1) * $xAxisPages->itemCountPerPage + 1,
+ $xAxisPage === $xAxisPages->last ? $xAxisPages->totalItemCount : $xAxisPage * $xAxisPages->itemCountPerPage,
+ $xAxisPages->totalItemCount,
+ 'x'
+ ); ?>"></a>
+<?php endif ?>
+ </td>
+<?php endforeach ?>
+ </tr>
+<?php endforeach ?>
+ </tbody>
+ </table>
+</div>
diff --git a/application/views/scripts/role/list.phtml b/application/views/scripts/role/list.phtml
new file mode 100644
index 0000000..352e3e2
--- /dev/null
+++ b/application/views/scripts/role/list.phtml
@@ -0,0 +1,65 @@
+<div class="controls separated">
+ <?= $tabs ?>
+ <?= $this->paginator ?>
+ <div class="sort-controls-container">
+ <?= $this->limiter ?>
+ <?= $this->sortBox ?>
+ </div>
+ <?= $this->filterEditor ?>
+</div>
+<div class="content">
+ <?= $this->qlink(
+ $this->translate('Create a New Role') ,
+ 'role/add',
+ null,
+ array(
+ 'class' => 'button-link',
+ 'data-base-target' => '_next',
+ 'icon' => 'plus',
+ 'title' => $this->translate('Create a new role')
+ )
+ ) ?>
+<?php /** @var \Icinga\Application\Config $roles */ if (! $roles->hasResult()): ?>
+ <p><?= $this->translate('No roles found.') ?></p>
+<?php return; endif ?>
+ <table class="table-row-selectable common-table" data-base-target="_next">
+ <thead>
+ <tr>
+ <th><?= $this->translate('Name') ?></th>
+ <th><?= $this->translate('Users') ?></th>
+ <th><?= $this->translate('Groups') ?></th>
+ <th><?= $this->translate('Inherits From') ?></th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+<?php foreach ($roles as $name => $role): /** @var object $role */ ?>
+ <tr>
+ <td>
+ <?= $this->qlink(
+ $name,
+ 'role/edit',
+ array('role' => $name),
+ array('title' => sprintf($this->translate('Edit role %s'), $name))
+ ) ?>
+ </td>
+ <td><?= $this->escape($role->users) ?></td>
+ <td><?= $this->escape($role->groups) ?></td>
+ <td><?= $this->escape($role->parent) ?></td>
+ <td class="icon-col text-right">
+ <?= $this->qlink(
+ '',
+ 'role/remove',
+ array('role' => $name),
+ array(
+ 'class' => 'action-link',
+ 'icon' => 'cancel',
+ 'title' => sprintf($this->translate('Remove role %s'), $name)
+ )
+ ) ?>
+ </td>
+ </tr>
+<?php endforeach ?>
+ </tbody>
+ </table>
+</div>
diff --git a/application/views/scripts/search/hint.phtml b/application/views/scripts/search/hint.phtml
new file mode 100644
index 0000000..d54c0b2
--- /dev/null
+++ b/application/views/scripts/search/hint.phtml
@@ -0,0 +1,8 @@
+<div class="content">
+<h1><?= $this->translate("I'm ready to search, waiting for your input") ?></h1>
+<p><strong><?= $this->translate('Hint') ?>: </strong><?= $this->translate(
+ 'Please use the asterisk (*) as a placeholder for wildcard searches.'
+ . " For convenience I'll always add a wildcard in front and after your"
+ . ' search string.'
+) ?></p>
+</div>
diff --git a/application/views/scripts/search/index.phtml b/application/views/scripts/search/index.phtml
new file mode 100644
index 0000000..321597e
--- /dev/null
+++ b/application/views/scripts/search/index.phtml
@@ -0,0 +1,7 @@
+<div class="controls">
+<?= $this->dashboard->getTabs() ?>
+</div>
+
+<div class="content dashboard">
+<?= $this->dashboard ?>
+</div>
diff --git a/application/views/scripts/showConfiguration.phtml b/application/views/scripts/showConfiguration.phtml
new file mode 100644
index 0000000..682b349
--- /dev/null
+++ b/application/views/scripts/showConfiguration.phtml
@@ -0,0 +1,27 @@
+<div>
+ <h4><?= $this->translate('Saving Configuration Failed'); ?></h4>
+ <p>
+ <?= sprintf(
+ $this->translate('The file %s couldn\'t be stored. (Error: "%s")'),
+ $this->escape($filePath),
+ $this->escape($errorMessage)
+ ); ?>
+ <br>
+ <?= $this->translate('This could have one or more of the following reasons:'); ?>
+ </p>
+ <ul>
+ <li><?= $this->translate('You don\'t have file-system permissions to write to the file'); ?></li>
+ <li><?= $this->translate('Something went wrong while writing the file'); ?></li>
+ <li><?= $this->translate('There\'s an application error preventing you from persisting the configuration'); ?></li>
+ </ul>
+</div>
+<p>
+ <?= $this->translate('Details can be found in the application log. (If you don\'t have access to this log, call your administrator in this case)'); ?>
+ <br>
+ <?= $this->translate('In case you can access the file by yourself, you can open it and insert the config manually:'); ?>
+</p>
+<p>
+ <pre>
+ <code><?= $this->escape($configString); ?></code>
+ </pre>
+</p>
diff --git a/application/views/scripts/simple-form.phtml b/application/views/scripts/simple-form.phtml
new file mode 100644
index 0000000..9bcba74
--- /dev/null
+++ b/application/views/scripts/simple-form.phtml
@@ -0,0 +1,6 @@
+<div class="controls">
+ <?= $tabs ?>
+</div>
+<div class="content">
+ <?= $form->create()->setTitle(null) // @TODO(el): create() has to be called because the UserForm is setting the title there ?>
+</div>
diff --git a/application/views/scripts/user/form.phtml b/application/views/scripts/user/form.phtml
new file mode 100644
index 0000000..cbf0659
--- /dev/null
+++ b/application/views/scripts/user/form.phtml
@@ -0,0 +1,6 @@
+<div class="controls">
+ <?= $tabs->showOnlyCloseButton(); ?>
+</div>
+<div class="content">
+ <?= $form; ?>
+</div> \ No newline at end of file
diff --git a/application/views/scripts/user/list.phtml b/application/views/scripts/user/list.phtml
new file mode 100644
index 0000000..bdb5f1a
--- /dev/null
+++ b/application/views/scripts/user/list.phtml
@@ -0,0 +1,90 @@
+<?php
+
+use Icinga\Data\Extensible;
+use Icinga\Data\Reducible;
+
+if (! $this->compact): ?>
+<div class="controls separated">
+ <?= $this->tabs ?>
+ <?= $this->paginator ?>
+ <div class="sort-controls-container">
+ <?= $this->limiter ?>
+ <?= $this->sortBox ?>
+ </div>
+ <?= $this->backendSelection ?>
+ <?= $this->filterEditor ?>
+</div>
+<?php endif ?>
+<div class="content">
+<?php
+
+if (! isset($backend)) {
+ echo $this->translate('No backend found which is able to list users') . '</div>';
+ return;
+} else {
+ $extensible = $this->hasPermission('config/access-control/users') && $backend instanceof Extensible;
+ $reducible = $this->hasPermission('config/access-control/users') && $backend instanceof Reducible;
+}
+?>
+
+<?php if ($extensible): ?>
+ <?= $this->qlink(
+ $this->translate('Add a New User') ,
+ 'user/add',
+ array('backend' => $backend->getName()),
+ array(
+ 'class' => 'button-link',
+ 'data-base-target' => '_next',
+ 'icon' => 'plus'
+ )
+ ) ?>
+<?php endif ?>
+
+<?php if (! $users->hasResult()): ?>
+ <p><?= $this->translate('No users found matching the filter') ?></p>
+</div>
+<?php return; endif ?>
+
+ <table data-base-target="_next" class="table-row-selectable common-table">
+ <thead>
+ <tr>
+ <th><?= $this->translate('Username') ?></th>
+ <?php if ($reducible): ?>
+ <th><?= $this->translate('Remove') ?></th>
+ <?php endif ?>
+ </tr>
+ </thead>
+ <tbody>
+ <?php foreach ($users as $user): ?>
+ <tr>
+ <td><?= $this->qlink(
+ $user->user_name,
+ 'user/show',
+ array(
+ 'backend' => $backend->getName(),
+ 'user' => $user->user_name
+ ),
+ array(
+ 'title' => sprintf($this->translate('Show detailed information about %s'), $user->user_name)
+ )
+ ) ?></td>
+ <?php if ($reducible): ?>
+ <td class="icon-col"><?= $this->qlink(
+ null,
+ 'user/remove',
+ array(
+ 'backend' => $backend->getName(),
+ 'user' => $user->user_name
+ ),
+ array(
+ 'class' => 'action-link',
+ 'icon' => 'cancel',
+ 'title' => sprintf($this->translate('Remove user %s'), $user->user_name)
+ )
+ ) ?></td>
+ <?php endif ?>
+ </tr>
+ <?php endforeach ?>
+ </tbody>
+ </table>
+</div>
diff --git a/application/views/scripts/user/show.phtml b/application/views/scripts/user/show.phtml
new file mode 100644
index 0000000..b19c15a
--- /dev/null
+++ b/application/views/scripts/user/show.phtml
@@ -0,0 +1,138 @@
+<?php
+
+use Icinga\Data\Updatable;
+use Icinga\Data\Reducible;
+use Icinga\Data\Selectable;
+
+?>
+<div class="controls separated">
+<?php if (! $this->compact): ?>
+ <?= $tabs; ?>
+<?php endif ?>
+ <h2><?= $this->escape($user->user_name) ?></h2>
+ <?php
+ if ($this->hasPermission('config/access-control/users') && $backend instanceof Updatable) {
+ echo $this->qlink(
+ $this->translate('Edit User'),
+ 'user/edit',
+ array(
+ 'backend' => $backend->getName(),
+ 'user' => $user->user_name
+ ),
+ array(
+ 'class' => 'button-link',
+ 'icon' => 'edit',
+ 'title' => sprintf($this->translate('Edit user %s'), $user->user_name)
+ )
+ );
+ }
+ ?>
+ <table class="name-value-table">
+ <tr>
+ <th><?= $this->translate('State'); ?></th>
+ <td><?= $user->is_active === null ? '-' : ($user->is_active ? $this->translate('Active') : $this->translate('Inactive')); ?></td>
+ </tr>
+ <tr>
+ <th><?= $this->translate('Created at'); ?></th>
+ <td><?= $user->created_at === null ? '-' : $this->formatDateTime($user->created_at); ?></td>
+ </tr>
+ <tr>
+ <th><?= $this->translate('Last modified'); ?></th>
+ <td><?= $user->last_modified === null ? '-' : $this->formatDateTime($user->last_modified); ?></td>
+ </tr>
+ <tr>
+ <th><?= $this->translate('Role Memberships'); ?></th>
+ <td>
+ <?php $roles = $userObj->getRoles(); ?>
+ <?php if (! empty($roles)): ?>
+ <ul class="role-memberships">
+ <?php foreach($roles as $role): ?>
+ <li>
+ <?php if ($this->allowedToEditRoles): ?>
+ <?= $this->qlink(
+ $role->getName(),
+ 'role/edit',
+ ['role' => $role->getName()],
+ ['title' => sprintf($this->translate('Edit role %s'), $role->getName())]
+ );
+ $role === end($roles) ? print '' : print ', '; ?>
+ <?php else: ?>
+ <?= $role->getName() ?>
+ <?php endif ?>
+ </li>
+ <?php endforeach ?>
+ </ul>
+ <?php else: ?>
+ <p><?= $this->translate('No memberships found'); ?></p>
+ <?php endif ?>
+ </td>
+ </tr>
+ </table>
+<?php if (! $this->compact): ?>
+ <h2><?= $this->translate('Group Memberships'); ?></h2>
+ <div class="sort-controls-container">
+ <?= $this->limiter; ?>
+ <?= $this->paginator; ?>
+ <?= $this->sortBox; ?>
+ </div>
+ <?= $this->filterEditor; ?>
+<?php endif ?>
+</div>
+<div class="content">
+<?php if ($showCreateMembershipLink): ?>
+ <?= $this->qlink(
+ $this->translate('Create New Membership'),
+ 'user/createmembership',
+ array(
+ 'backend' => $backend->getName(),
+ 'user' => $user->user_name
+ ),
+ array(
+ 'icon' => 'plus',
+ 'class' => 'button-link'
+ )
+ ) ?>
+<?php endif ?>
+
+<?php if (! $memberships->hasResult()): ?>
+ <p><?= $this->translate('No memberships found matching the filter'); ?></p>
+</div>
+<?php return; endif ?>
+
+ <table data-base-target="_next" class="table-row-selectable common-table">
+ <thead>
+ <tr>
+ <th><?= $this->translate('Group'); ?></th>
+ <th><?= $this->translate('Cancel', 'group.membership'); ?></th>
+ </tr>
+ </thead>
+ <tbody>
+ <?php foreach ($memberships as $membership): ?>
+ <tr>
+ <td>
+ <?php if ($this->hasPermission('config/access-control/groups') && $membership->backend instanceof Selectable): ?>
+ <?= $this->qlink($membership->group_name, 'group/show', array(
+ 'backend' => $membership->backend->getName(),
+ 'group' => $membership->group_name
+ ), array(
+ 'title' => sprintf($this->translate('Show detailed information for group %s'), $membership->group_name)
+ )); ?>
+ <?php else: ?>
+ <?= $this->escape($membership->group_name); ?>
+ <?php endif ?>
+ </td>
+ <td class="icon-col" data-base-target="_self">
+ <?php if (isset($removeForm) && $membership->backend instanceof Reducible): ?>
+ <?= $removeForm->setAction($this->url('group/removemember', array(
+ 'backend' => $membership->backend->getName(),
+ 'group' => $membership->group_name
+ ))); ?>
+ <?php else: ?>
+ -
+ <?php endif ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+ </tbody>
+ </table>
+</div>