Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/optimize for actual mobile devices #4886

Draft
wants to merge 42 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
1578bae
Introduce new widget `MobileNavigation`
flourish86 Apr 12, 2022
4a36de1
Add mobilemenu.less
flourish86 Apr 12, 2022
d9eda70
Load mobilemenu.less
flourish86 Apr 12, 2022
81b4163
body.html: Use MobileNavigation
flourish86 Apr 12, 2022
5183428
navigation.js: Handle toggle-more click
flourish86 May 19, 2022
b8a3441
body.phtml: Add search bar and user-menu placeholders
flourish86 May 19, 2022
9dc71cc
layout-structure.less: Keep default flex-direction in `.minimal-layout`
flourish86 May 19, 2022
8af044d
Introduce new Widget `MobileConfigMenu`
flourish86 Jun 1, 2022
997cc8d
Add `mobilconfigmenu` Stylesheet
flourish86 Jun 1, 2022
db8ddd5
Load `mobilemenuconfig` Stylesheet
flourish86 Jun 1, 2022
d4fdb8d
body.phtml: Use `MobileConfigMenu`
flourish86 Jun 1, 2022
2128fd3
layout.less: Adjust `#sidebar` styles for mobile
flourish86 Jun 1, 2022
3838d76
responsive.less: Adjust styles for `#sidebar`
flourish86 Jun 1, 2022
1372aba
navigation.js: Add event handlers for `#mobile-menu`
flourish86 Jun 1, 2022
a3e6df7
flyout.less: Add separate file for flyout
flourish86 Jun 7, 2022
1a9eda4
Stylesheet.php: Load flyout.less
flourish86 Jun 7, 2022
0d0ba52
configmenu.less: Don’t use level-2 only for `.config-menu`
flourish86 Jun 8, 2022
d6ce32b
LayoutController: Add actions for mobile menus
flourish86 Jun 14, 2022
29c1875
body.phtml: Enable autorefresh for mobile menus
flourish86 Jun 14, 2022
6d81ee5
Create new Trait `HealthBadgeTrait`
flourish86 Jun 14, 2022
6988871
Use `HealthBadgeTrait`
flourish86 Jun 14, 2022
71dabf0
navigation.phtml
flourish86 Jun 14, 2022
c6e5e30
configmenu.less: Remove redundant `.flyout-menu` styles
flourish86 Jul 25, 2022
2a99d50
mobileconfigmenu.less: Remove redundant `.flyout-menu` styles
flourish86 Jun 14, 2022
2273442
mobilemenu.less: Remove redundant `.flyout-menu` styles
flourish86 Jun 14, 2022
76ae05e
ConfigMenu: Add `.flyout-menu` css class
flourish86 Jun 22, 2022
18b8daf
MobileConfigMenu: Add `.flyout-menu` css class
flourish86 Jun 14, 2022
836b1c9
MobileNavigation: Add `.flyout-menu` css class
flourish86 Jun 14, 2022
d789197
Refactor MobileNavigation to MobileMenu
flourish86 Jun 14, 2022
67f3647
ConfigMenu: Add `.flyout-menu` css class
flourish86 Jun 15, 2022
85b9ff3
MobileConfigMenu: Add `.flyout-menu` css class
flourish86 Jun 14, 2022
ddf916b
Refactor MobileNavigation to MobileMenu
flourish86 Jun 14, 2022
f334e83
JS: Introduce behavior `FlyoutMenu`
flourish86 Jul 4, 2022
c31d0d9
JS: Load `flyout-menu.js`
flourish86 Jul 4, 2022
cc92459
navigation.js: Remove redundant event handlers
flourish86 Jul 5, 2022
d5e4559
Introduce Widget `Flyout`
flourish86 Jul 12, 2022
a85f0ea
navigation.js: Move flyout handlers to `flyout-menu.js`
flourish86 Jul 14, 2022
ce616f2
Add manifest.json for iOS “Add to Homescreen” feature
flourish86 Jul 26, 2022
06d3679
responsive.less: Use updated `env()` syntax for safe area insets
flourish86 Jul 26, 2022
d025659
mobilemenu.less: Add bottom safe area inset
flourish86 Jul 26, 2022
7fa1c60
layout.phtml: reference iOS manifest file
flourish86 Jul 26, 2022
66bc338
responsive.less: Prevent body from scrolling on mobile devices
flourish86 Jul 26, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions application/controllers/LayoutController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@

namespace Icinga\Controllers;

use Icinga\Web\Controller\ActionController;
use Icinga\Web\Menu;
use Icinga\Web\Navigation\Mobile\MobileConfigMenu;
use Icinga\Web\Navigation\Mobile\MobileMenu;
use ipl\Web\Compat\CompatController;

/**
* Create complex layout parts
*/
class LayoutController extends ActionController
class LayoutController extends CompatController
{
/**
* Render the menu
Expand All @@ -21,6 +23,22 @@ public function menuAction()
$this->view->menuRenderer = (new Menu())->getRenderer();
}

public function mobileConfigMenuAction()
{
$this->setAutorefreshInterval(15);
$this->_helper->layout()->disableLayout();
$this->view->compact = true;
$this->getDocument()->addHtml(new MobileConfigMenu());
}

public function mobileMenuAction()
{
$this->setAutorefreshInterval(15);
$this->_helper->layout()->disableLayout();
$this->view->compact = true;
$this->getDocument()->addHtml(new MobileMenu());
}

public function announcementsAction()
{
$this->_helper->layout()->disableLayout();
Expand Down
30 changes: 24 additions & 6 deletions application/layouts/scripts/body.phtml
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
<?php

use Icinga\Web\Url;
use Icinga\Web\Notification;
use Icinga\Authentication\Auth;
use Icinga\Web\Navigation\Mobile\MobileConfigMenu;
use Icinga\Web\Navigation\Mobile\MobileMenu;
use Icinga\Web\Notification;
use Icinga\Web\Url;
use Icinga\Web\Widget\SearchDashboard;
use ipl\Html\HtmlString;
use ipl\Web\Widget\Icon;

Expand All @@ -24,6 +27,9 @@ if ($this->layout()->inlineLayout) {
$inlineLayoutScript = 'inline.phtml';
}

$searchDashboard = new SearchDashboard();
$searchDashboard->setUser($this->Auth()->getUser());

?>
<div id="header">
<div id="announcements" class="container">
Expand All @@ -44,9 +50,18 @@ if ($this->layout()->inlineLayout) {
'id' => 'header-logo'
)
); ?>
<div id="mobile-menu-toggle">
<button type="button"><?= $this->icon('menu') ?><?= $this->icon('cancel') ?></button>
</div>
</div>
<?php if ($searchDashboard->search('dummy')->getPane('search')->hasDashlets()): ?>
<form id="mobile-searchbar" 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 everything') ?> &hellip;"
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">
<button class="search-reset icon-cancel" type="reset"></button>
</form>
<?php endif; ?>
<div id="mobile-config-menu" data-last-update="-1" data-base-target="_main" class="container"
data-icinga-url="<?= $this->href('layout/mobile-config-menu') ?>" data-icinga-refresh="15">
<?= new MobileConfigMenu() ?>
</div>
<?= $this->render('parts/navigation.phtml'); ?>
</div>
Expand All @@ -68,7 +83,6 @@ if ($this->layout()->inlineLayout) {
</div>
<div id="footer">
<ul role="alert" id="notifications"><?php

$notifications = Notification::getInstance();
if ($notifications->hasMessages()) {
foreach ($notifications->popMessages() as $m) {
Expand All @@ -95,4 +109,8 @@ if ($this->layout()->inlineLayout) {
}
?></ul>
<div id="application-state-summary" class="container" data-icinga-url="<?= $this->url('application-state/summary') ?>" data-last-update="-1" data-icinga-refresh="60"></div>
<div id="mobile-menu" data-last-update="-1" data-base-target="_main" class="container"
data-icinga-url="<?= $this->href('layout/mobile-menu') ?>" data-icinga-refresh="15">
<?= new MobileMenu() ?>
</div>
</div>
1 change: 1 addition & 0 deletions application/layouts/scripts/layout.phtml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ $innerLayoutScript = $this->layout()->innerLayout . '.phtml';
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="application-name" content="Icinga Web 2">
<meta name="apple-mobile-web-app-title" content="Icinga">
<link rel="manifest" href=<?= $this->baseUrl('img/manifest.json') ?>>
<link rel="mask-icon" href="<?= $this->baseUrl('img/website-icon.svg') ?>" color="#0096BF">
<?php if ($isIframe): ?>
<base target="_parent">
Expand Down
2 changes: 1 addition & 1 deletion application/layouts/scripts/parts/navigation.phtml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ if (! $this->auth()->isAuthenticated()) {
</ul>
</div>
<div id="menu" data-last-update="-1" data-base-target="_main" class="container"
data-icinga-url="<?= $this->href('layout/menu') ?>" data-icinga-refresh="15">
data-icinga-url="<?= $this->href('layout/menu') ?>" data-icinga-refresh="15">
<?= $this->partial(
'layout/menu.phtml',
'default',
Expand Down
78 changes: 78 additions & 0 deletions library/Icinga/Common/HealthBadgeTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php
/* Icinga Web 2 | (c) 2022 Icinga GmbH | GPLv2+ */

namespace Icinga\Common;

use Icinga\Application\Hook\HealthHook;
use ipl\Web\Widget\StateBadge;

trait HealthBadgeTrait
{
private $STATE_OK = 'ok';
private $STATE_CRITICAL = 'critical';
private $STATE_WARNING = 'warning';
private $STATE_PENDING = 'pending';
private $STATE_UNKNOWN = 'unknown';

/** @var string The state of the worst health problem item */
protected $state;

/** @var string The message of the worst health problem item */
protected $title;

/**
* Create Health Badge
*
* @return StateBadge $stateBadge
*/
protected function createHealthBadge()
{
$stateBadge = null;
if ($this->getHealthCount() > 0) {
$stateBadge = new StateBadge($this->getHealthCount(), $this->state);
$stateBadge->addAttributes(['class' => 'disabled', 'title' => $this->title]);
}

return $stateBadge;
}

/**
* Get the number of health problems
*
* @return int $count
*/
protected function getHealthCount()
{
$count = 0;
$title = null;
$worstState = null;
foreach (HealthHook::collectHealthData()->select() as $result) {
if ($worstState === null || $result->state > $worstState) {
$worstState = $result->state;
$title = $result->message;
$count = 1;
} elseif ($worstState === $result->state) {
$count++;
}
}

switch ($worstState) {
case HealthHook::STATE_OK:
$count = 0;
break;
case HealthHook::STATE_WARNING:
$this->state = $this->STATE_WARNING;
break;
case HealthHook::STATE_CRITICAL:
$this->state = $this->STATE_CRITICAL;
break;
case HealthHook::STATE_UNKNOWN:
$this->state = $this->STATE_UNKNOWN;
break;
}

$this->title = $title;

return $count;
}
}
1 change: 1 addition & 0 deletions library/Icinga/Web/JavaScript.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class JavaScript
'js/icinga/behavior/dropdown.js',
'js/icinga/behavior/navigation.js',
'js/icinga/behavior/form.js',
'js/icinga/behavior/flyout-menu.js',
'js/icinga/behavior/actiontable.js',
'js/icinga/behavior/flyover.js',
'js/icinga/behavior/filtereditor.js',
Expand Down
89 changes: 23 additions & 66 deletions library/Icinga/Web/Navigation/ConfigMenu.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,19 @@

namespace Icinga\Web\Navigation;

use Icinga\Application\Hook\HealthHook;
use Icinga\Application\Icinga;
use Icinga\Application\Logger;
use Icinga\Authentication\Auth;
use Icinga\Common\HealthBadgeTrait;
use ipl\Html\Attributes;
use ipl\Html\BaseHtmlElement;
use ipl\Html\HtmlElement;
use ipl\Html\Text;
use ipl\Web\Url;
use ipl\Web\Widget\Icon;
use ipl\Web\Widget\StateBadge;

class ConfigMenu extends BaseHtmlElement
{
const STATE_OK = 'ok';
const STATE_CRITICAL = 'critical';
const STATE_WARNING = 'warning';
const STATE_PENDING = 'pending';
const STATE_UNKNOWN = 'unknown';
use HealthBadgeTrait;

protected $tag = 'ul';

Expand All @@ -35,6 +29,10 @@ class ConfigMenu extends BaseHtmlElement

protected $state;

protected $healthBadge;

protected $flyoutID = 'config-menu-flyout';

public function __construct()
{
$this->children = [
Expand Down Expand Up @@ -89,6 +87,7 @@ public function __construct()
'items' => [
'logout' => [
'label' => t('Logout'),
'icon' => 'power-off',
'atts' => [
'target' => '_self',
'class' => 'nav-item-logout'
Expand All @@ -99,13 +98,7 @@ public function __construct()
]
];

if (Logger::writesToFile()) {
$this->children['system']['items']['application_log'] = [
'label' => t('Application Log'),
'url' => 'list/applicationlog',
'permission' => 'application/log'
];
}
$this->healthBadge = $this->createHealthBadge();
}

protected function assembleUserMenuItem(BaseHtmlElement $userMenuItem)
Expand Down Expand Up @@ -165,7 +158,7 @@ protected function assembleLevel2Nav(BaseHtmlElement $level2Nav)
));
}

$ul = HtmlElement::create('ul', ['class' => 'nav']);
$ul = HtmlElement::create('ul', ['class' => 'nav flyout-menu']);
foreach ($c['items'] as $key => $item) {
$ul->add($this->createLevel2MenuItem($item, $key));
}
Expand All @@ -176,41 +169,6 @@ protected function assembleLevel2Nav(BaseHtmlElement $level2Nav)
$level2Nav->add($navContent);
}

protected function getHealthCount()
{
$count = 0;
$title = null;
$worstState = null;
foreach (HealthHook::collectHealthData()->select() as $result) {
if ($worstState === null || $result->state > $worstState) {
$worstState = $result->state;
$title = $result->message;
$count = 1;
} elseif ($worstState === $result->state) {
$count++;
}
}

switch ($worstState) {
case HealthHook::STATE_OK:
$count = 0;
break;
case HealthHook::STATE_WARNING:
$this->state = self::STATE_WARNING;
break;
case HealthHook::STATE_CRITICAL:
$this->state = self::STATE_CRITICAL;
break;
case HealthHook::STATE_UNKNOWN:
$this->state = self::STATE_UNKNOWN;
break;
}

$this->title = $title;

return $count;
}

protected function isSelectedItem($item)
{
if ($item !== null && Icinga::app()->getRequest()->getUrl()->matches($item['url'])) {
Expand All @@ -221,22 +179,11 @@ protected function isSelectedItem($item)
return false;
}

protected function createHealthBadge()
{
$stateBadge = null;
if ($this->getHealthCount() > 0) {
$stateBadge = new StateBadge($this->getHealthCount(), $this->state);
$stateBadge->addAttributes(['class' => 'disabled']);
}

return $stateBadge;
}

protected function createLevel2Menu()
{
$level2Nav = HtmlElement::create(
'div',
Attributes::create(['class' => 'nav-level-1 flyout'])
Attributes::create(['class' => 'nav-level-1 flyout', 'id' => $this->flyoutID ])
);

$this->assembleLevel2Nav($level2Nav);
Expand All @@ -251,10 +198,16 @@ protected function createLevel2MenuItem($item, $key)
}

$healthBadge = null;
$class = null;
$class = '';
if ($key === 'health') {
$class = 'badge-nav-item';
$healthBadge = $this->createHealthBadge();
$healthBadge = $this->healthBadge;
}

$icon = null;
if (isset($item['icon'])) {
$icon = new Icon($item['icon']);
$class .= ' has-icon';
}

$li = HtmlElement::create(
Expand All @@ -265,6 +218,7 @@ protected function createLevel2MenuItem($item, $key)
'a',
Attributes::create(['href' => Url::fromPath($item['url'])]),
[
$icon,
$item['label'],
isset($healthBadge) ? $healthBadge : ''
]
Expand Down Expand Up @@ -292,7 +246,10 @@ protected function createUserMenuItem()

protected function createCogMenuItem()
{
$cogMenuItem = HtmlElement::create('li', ['class' => 'config-nav-item']);
$cogMenuItem = HtmlElement::create('li', [
'class' => 'config-nav-item',
'data-flyout-target' => $this->flyoutID
]);

$this->assembleCogMenuItem($cogMenuItem);

Expand Down
Loading