mirror of
https://github.com/Icinga/icingadb-web.git
synced 2026-05-28 04:36:06 -04:00
Use new item list implementation (#1141)
This is the first step to support my proposal for new item layouts in Icinga DB Web. I've only adjusted redundancy group items for now. The remaining adjustments will be performed by @sukhwinder33445 in the coming weeks. blocked by https://github.com/Icinga/ipl-web/pull/252
This commit is contained in:
commit
53374ab315
121 changed files with 5893 additions and 6378 deletions
|
|
@ -10,7 +10,7 @@ use Icinga\Module\Icingadb\Common\Links;
|
|||
use Icinga\Module\Icingadb\Model\Comment;
|
||||
use Icinga\Module\Icingadb\Web\Controller;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\CommentDetail;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\CommentList;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\ObjectHeader;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Url;
|
||||
|
||||
|
|
@ -49,13 +49,9 @@ class CommentController extends Controller
|
|||
|
||||
public function indexAction()
|
||||
{
|
||||
$this->addControl((new CommentList([$this->comment]))
|
||||
->setViewMode('minimal')
|
||||
->setDetailActionsDisabled()
|
||||
->setCaptionDisabled()
|
||||
->setNoSubjectLink());
|
||||
$this->addControl(new ObjectHeader($this->comment));
|
||||
|
||||
$this->addContent((new CommentDetail($this->comment))->setTicketLinkEnabled());
|
||||
$this->addContent(new CommentDetail($this->comment));
|
||||
|
||||
$this->setAutorefreshInterval(10);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ use Icinga\Module\Icingadb\Forms\Command\Object\DeleteCommentForm;
|
|||
use Icinga\Module\Icingadb\Model\Comment;
|
||||
use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
|
||||
use Icinga\Module\Icingadb\Web\Controller;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\CommentList;
|
||||
use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ObjectList;
|
||||
use Icinga\Module\Icingadb\Widget\ShowMore;
|
||||
use ipl\Web\Control\LimitControl;
|
||||
use ipl\Web\Control\SortControl;
|
||||
|
|
@ -81,7 +81,10 @@ class CommentsController extends Controller
|
|||
|
||||
$results = $comments->execute();
|
||||
|
||||
$this->addContent((new CommentList($results))->setViewMode($viewModeSwitcher->getViewMode()));
|
||||
$this->addContent(
|
||||
(new ObjectList($results))
|
||||
->setViewMode($viewModeSwitcher->getViewMode())
|
||||
);
|
||||
|
||||
if ($compact) {
|
||||
$this->addContent(
|
||||
|
|
@ -156,7 +159,11 @@ class CommentsController extends Controller
|
|||
|
||||
$rs = $comments->execute();
|
||||
|
||||
$this->addControl((new CommentList($rs))->setViewMode('minimal'));
|
||||
$this->addControl(
|
||||
(new ObjectList($rs))
|
||||
->setViewMode('minimal')
|
||||
->setDetailActionsDisabled()
|
||||
);
|
||||
|
||||
$this->addControl(new ShowMore(
|
||||
$rs,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use Icinga\Module\Icingadb\Common\Links;
|
|||
use Icinga\Module\Icingadb\Model\Downtime;
|
||||
use Icinga\Module\Icingadb\Web\Controller;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\DowntimeDetail;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\DowntimeList;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\ObjectHeader;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Url;
|
||||
|
||||
|
|
@ -61,11 +61,7 @@ class DowntimeController extends Controller
|
|||
{
|
||||
$detail = new DowntimeDetail($this->downtime);
|
||||
|
||||
$this->addControl((new DowntimeList([$this->downtime]))
|
||||
->setViewMode('minimal')
|
||||
->setDetailActionsDisabled()
|
||||
->setCaptionDisabled()
|
||||
->setNoSubjectLink());
|
||||
$this->addControl(new ObjectHeader($this->downtime));
|
||||
|
||||
$this->addContent($detail);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ use Icinga\Module\Icingadb\Forms\Command\Object\DeleteDowntimeForm;
|
|||
use Icinga\Module\Icingadb\Model\Downtime;
|
||||
use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
|
||||
use Icinga\Module\Icingadb\Web\Controller;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\DowntimeList;
|
||||
use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ObjectList;
|
||||
use Icinga\Module\Icingadb\Widget\ShowMore;
|
||||
use ipl\Web\Control\LimitControl;
|
||||
use ipl\Web\Control\SortControl;
|
||||
|
|
@ -87,7 +87,10 @@ class DowntimesController extends Controller
|
|||
|
||||
$results = $downtimes->execute();
|
||||
|
||||
$this->addContent((new DowntimeList($results))->setViewMode($viewModeSwitcher->getViewMode()));
|
||||
$this->addContent(
|
||||
(new ObjectList($results))
|
||||
->setViewMode($viewModeSwitcher->getViewMode())
|
||||
);
|
||||
|
||||
if ($compact) {
|
||||
$this->addContent(
|
||||
|
|
@ -162,7 +165,11 @@ class DowntimesController extends Controller
|
|||
|
||||
$rs = $downtimes->execute();
|
||||
|
||||
$this->addControl((new DowntimeList($rs))->setViewMode('minimal'));
|
||||
$this->addControl(
|
||||
(new ObjectList($rs))
|
||||
->setViewMode('minimal')
|
||||
->setDetailActionsDisabled()
|
||||
);
|
||||
|
||||
$this->addControl(new ShowMore(
|
||||
$rs,
|
||||
|
|
|
|||
|
|
@ -4,12 +4,10 @@
|
|||
|
||||
namespace Icinga\Module\Icingadb\Controllers;
|
||||
|
||||
use ArrayObject;
|
||||
use Icinga\Module\Icingadb\Model\History;
|
||||
use Icinga\Module\Icingadb\Web\Controller;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\EventDetail;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\HistoryList;
|
||||
use ipl\Orm\ResultSet;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\ObjectHeader;
|
||||
use ipl\Stdlib\Filter;
|
||||
|
||||
class EventController extends Controller
|
||||
|
|
@ -60,12 +58,7 @@ class EventController extends Controller
|
|||
|
||||
public function indexAction()
|
||||
{
|
||||
$this->addControl((new HistoryList(new ResultSet(new ArrayObject([$this->event]))))
|
||||
->setViewMode('minimal')
|
||||
->setPageSize(1)
|
||||
->setCaptionDisabled()
|
||||
->setNoSubjectLink()
|
||||
->setDetailActionsDisabled());
|
||||
$this->addContent((new EventDetail($this->event))->setTicketLinkEnabled());
|
||||
$this->addControl(new ObjectHeader($this->event));
|
||||
$this->addContent(new EventDetail($this->event));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ use GuzzleHttp\Psr7\ServerRequest;
|
|||
use Icinga\Module\Icingadb\Model\History;
|
||||
use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
|
||||
use Icinga\Module\Icingadb\Web\Controller;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\HistoryList;
|
||||
use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\LoadMoreObjectList;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Control\LimitControl;
|
||||
use ipl\Web\Control\SortControl;
|
||||
|
|
@ -98,7 +98,7 @@ class HistoryController extends Controller
|
|||
->onlyWith($preserveParams)
|
||||
->setFilter($filter);
|
||||
|
||||
$historyList = (new HistoryList($history->execute()))
|
||||
$historyList = (new LoadMoreObjectList($history->execute()))
|
||||
->setPageSize($limitControl->getLimit())
|
||||
->setViewMode($viewModeSwitcher->getViewMode())
|
||||
->setLoadMoreUrl($url->setParam('before', $before));
|
||||
|
|
|
|||
|
|
@ -26,11 +26,10 @@ use Icinga\Module\Icingadb\Web\Controller;
|
|||
use Icinga\Module\Icingadb\Widget\Detail\HostDetail;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\HostInspectionDetail;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\HostMetaInfo;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\ObjectHeader;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\QuickActions;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\DependencyNodeList;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\HostList;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\HistoryList;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ServiceList;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\LoadMoreObjectList;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ObjectList;
|
||||
use ipl\Orm\Query;
|
||||
use ipl\Sql\Expression;
|
||||
use ipl\Sql\Filter\Exists;
|
||||
|
|
@ -69,10 +68,7 @@ class HostController extends Controller
|
|||
$this->host = $host;
|
||||
$this->loadTabsForObject($host);
|
||||
|
||||
$this->addControl((new HostList([$host]))
|
||||
->setViewMode('objectHeader')
|
||||
->setDetailActionsDisabled()
|
||||
->setNoSubjectLink());
|
||||
$this->addControl(new ObjectHeader($host));
|
||||
|
||||
$this->setTitleTab($this->getRequest()->getActionName());
|
||||
$this->setTitle($host->display_name);
|
||||
|
|
@ -171,7 +167,7 @@ class HostController extends Controller
|
|||
$this->addControl($limitControl);
|
||||
$this->addControl($viewModeSwitcher);
|
||||
|
||||
$historyList = (new HistoryList($history->execute()))
|
||||
$historyList = (new LoadMoreObjectList($history->execute()))
|
||||
->setViewMode($viewModeSwitcher->getViewMode())
|
||||
->setPageSize($limitControl->getLimit())
|
||||
->setLoadMoreUrl($url->setParam('before', $before));
|
||||
|
|
@ -223,7 +219,7 @@ class HostController extends Controller
|
|||
|
||||
yield $this->export($services);
|
||||
|
||||
$serviceList = (new ServiceList($services))
|
||||
$serviceList = (new ObjectList($services))
|
||||
->setViewMode($viewModeSwitcher->getViewMode());
|
||||
|
||||
$this->addControl($paginationControl);
|
||||
|
|
@ -288,7 +284,7 @@ class HostController extends Controller
|
|||
$this->addControl($searchBar);
|
||||
|
||||
$this->addContent(
|
||||
(new DependencyNodeList($nodesQuery))
|
||||
(new ObjectList($nodesQuery))
|
||||
->setViewMode($viewModeSwitcher->getViewMode())
|
||||
);
|
||||
|
||||
|
|
@ -358,7 +354,7 @@ class HostController extends Controller
|
|||
$this->addControl($searchBar);
|
||||
|
||||
$this->addContent(
|
||||
(new DependencyNodeList($nodesQuery))
|
||||
(new ObjectList($nodesQuery))
|
||||
->setViewMode($viewModeSwitcher->getViewMode())
|
||||
);
|
||||
|
||||
|
|
@ -537,7 +533,7 @@ class HostController extends Controller
|
|||
|
||||
protected function getDefaultTabControls(): array
|
||||
{
|
||||
return [(new HostList([$this->host]))->setDetailActionsDisabled()->setNoSubjectLink()];
|
||||
return [new ObjectHeader($this->host)];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ use Icinga\Module\Icingadb\Redis\VolatileStateResults;
|
|||
use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
|
||||
use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher;
|
||||
use Icinga\Module\Icingadb\Web\Controller;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\HostList;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\ObjectHeader;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ObjectList;
|
||||
use Icinga\Module\Icingadb\Widget\ItemTable\HostgroupTableRow;
|
||||
use ipl\Html\Html;
|
||||
use ipl\Stdlib\Filter;
|
||||
|
|
@ -109,15 +110,15 @@ class HostgroupController extends Controller
|
|||
|
||||
yield $this->export($hosts);
|
||||
|
||||
$hostList = (new HostList($hosts->execute()))
|
||||
$hostList = (new ObjectList($hosts))
|
||||
->setViewMode($viewModeSwitcher->getViewMode());
|
||||
|
||||
// ICINGAWEB_EXPORT_FORMAT is not set yet and $this->format is inaccessible, yeah...
|
||||
if ($this->getRequest()->getParam('format') === 'pdf') {
|
||||
$this->addContent(new HostgroupTableRow($hostgroup));
|
||||
$this->addContent(new ObjectHeader($hostgroup));
|
||||
$this->addContent(Html::tag('h2', null, t('Hosts')));
|
||||
} else {
|
||||
$this->addControl(new HostgroupTableRow($hostgroup));
|
||||
$this->addControl(new ObjectHeader($hostgroup));
|
||||
}
|
||||
|
||||
$this->addControl($paginationControl);
|
||||
|
|
|
|||
|
|
@ -7,10 +7,13 @@ namespace Icinga\Module\Icingadb\Controllers;
|
|||
use GuzzleHttp\Psr7\ServerRequest;
|
||||
use Icinga\Module\Icingadb\Model\Hostgroup;
|
||||
use Icinga\Module\Icingadb\Model\Hostgroupsummary;
|
||||
use Icinga\Module\Icingadb\View\HostgroupGridRenderer;
|
||||
use Icinga\Module\Icingadb\View\HostgroupRenderer;
|
||||
use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
|
||||
use Icinga\Module\Icingadb\Web\Controller;
|
||||
use Icinga\Module\Icingadb\Widget\ItemTable\HostgroupTable;
|
||||
use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher;
|
||||
use Icinga\Module\Icingadb\Widget\ItemTable\ObjectGrid;
|
||||
use Icinga\Module\Icingadb\Widget\ItemTable\ObjectTable;
|
||||
use Icinga\Module\Icingadb\Widget\ShowMore;
|
||||
use ipl\Web\Control\LimitControl;
|
||||
use ipl\Web\Control\SortControl;
|
||||
|
|
@ -99,11 +102,13 @@ class HostgroupsController extends Controller
|
|||
|
||||
$results = $hostgroups->execute();
|
||||
|
||||
$this->addContent(
|
||||
(new HostgroupTable($results))
|
||||
->setBaseFilter($filter)
|
||||
->setViewMode($viewModeSwitcher->getViewMode())
|
||||
);
|
||||
if ($viewModeSwitcher->getViewMode() === 'grid') {
|
||||
$content = new ObjectGrid($results, (new HostgroupGridRenderer())->setBaseFilter($filter));
|
||||
} else {
|
||||
$content = new ObjectTable($results, (new HostgroupRenderer())->setBaseFilter($filter));
|
||||
}
|
||||
|
||||
$this->addContent($content);
|
||||
|
||||
if ($compact) {
|
||||
$this->addContent(
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
|
|||
use Icinga\Module\Icingadb\Web\Controller;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\MultiselectQuickActions;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\ObjectsDetail;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\HostList;
|
||||
use Icinga\Module\Icingadb\Widget\HostStatusBar;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ObjectList;
|
||||
use Icinga\Module\Icingadb\Widget\ItemTable\HostItemTable;
|
||||
use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher;
|
||||
use Icinga\Module\Icingadb\Widget\ShowMore;
|
||||
|
|
@ -104,7 +104,7 @@ class HostsController extends Controller
|
|||
$hostList = (new HostItemTable($results, HostItemTable::applyColumnMetaData($hosts, $columns)))
|
||||
->setSort($sortControl->getSort());
|
||||
} else {
|
||||
$hostList = (new HostList($results))
|
||||
$hostList = (new ObjectList($results))
|
||||
->setViewMode($viewModeSwitcher->getViewMode());
|
||||
}
|
||||
|
||||
|
|
@ -166,7 +166,7 @@ class HostsController extends Controller
|
|||
$summary->comments_total = $comments->count();
|
||||
|
||||
$this->addControl(
|
||||
(new HostList($results))
|
||||
(new ObjectList($results))
|
||||
->setViewMode('minimal')
|
||||
->setDetailActionsDisabled()
|
||||
);
|
||||
|
|
|
|||
|
|
@ -8,13 +8,11 @@ use GuzzleHttp\Psr7\ServerRequest;
|
|||
use Icinga\Module\Icingadb\Model\NotificationHistory;
|
||||
use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
|
||||
use Icinga\Module\Icingadb\Web\Controller;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\NotificationList;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\LoadMoreObjectList;
|
||||
use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher;
|
||||
use ipl\Sql\Sql;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Control\LimitControl;
|
||||
use ipl\Web\Control\SortControl;
|
||||
use ipl\Web\Filter\QueryString;
|
||||
use ipl\Web\Url;
|
||||
|
||||
class NotificationsController extends Controller
|
||||
|
|
@ -94,7 +92,7 @@ class NotificationsController extends Controller
|
|||
->onlyWith($preserveParams)
|
||||
->setFilter($filter);
|
||||
|
||||
$notificationList = (new NotificationList($notifications->execute()))
|
||||
$notificationList = (new LoadMoreObjectList($notifications->execute()))
|
||||
->setPageSize($limitControl->getLimit())
|
||||
->setViewMode($viewModeSwitcher->getViewMode())
|
||||
->setLoadMoreUrl($url->setParam('before', $before));
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher;
|
|||
use Icinga\Module\Icingadb\Web\Controller;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\MultiselectQuickActions;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\RedundancyGroupDetail;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\RedundancyGroupHeader;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\DependencyNodeList;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\ObjectHeader;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ObjectList;
|
||||
use Generator;
|
||||
use ipl\Orm\Query;
|
||||
use ipl\Stdlib\Filter;
|
||||
|
|
@ -68,14 +68,9 @@ class RedundancygroupController extends Controller
|
|||
$this->setTitleTab($this->getRequest()->getActionName());
|
||||
$this->setTitle($this->group->display_name);
|
||||
|
||||
$summary = RedundancyGroupSummary::on($this->getDb())
|
||||
->filter(Filter::equal('id', $this->groupId));
|
||||
$this->groupSummary = $this->group->summary;
|
||||
|
||||
$this->applyRestrictions($summary);
|
||||
|
||||
$this->groupSummary = $summary->first();
|
||||
|
||||
$this->addControl(new RedundancyGroupHeader($this->group, $this->groupSummary));
|
||||
$this->addControl(new ObjectHeader($this->group));
|
||||
}
|
||||
|
||||
public function indexAction(): void
|
||||
|
|
@ -145,7 +140,7 @@ class RedundancygroupController extends Controller
|
|||
$this->addControl($searchBar);
|
||||
|
||||
$this->addContent(
|
||||
(new DependencyNodeList($nodesQuery))
|
||||
(new ObjectList($nodesQuery))
|
||||
->setViewMode($viewModeSwitcher->getViewMode())
|
||||
);
|
||||
|
||||
|
|
@ -214,7 +209,7 @@ class RedundancygroupController extends Controller
|
|||
$this->addControl($searchBar);
|
||||
|
||||
$this->addContent(
|
||||
(new DependencyNodeList($nodesQuery))
|
||||
(new ObjectList($nodesQuery))
|
||||
->setViewMode($viewModeSwitcher->getViewMode())
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ use Icinga\Module\Icingadb\Redis\VolatileStateResults;
|
|||
use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
|
||||
use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher;
|
||||
use Icinga\Module\Icingadb\Web\Controller;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\ObjectHeader;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\QuickActions;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\ServiceDetail;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\ServiceInspectionDetail;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\ServiceMetaInfo;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\DependencyNodeList;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\HistoryList;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ServiceList;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\LoadMoreObjectList;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ObjectList;
|
||||
use ipl\Orm\Query;
|
||||
use ipl\Sql\Expression;
|
||||
use ipl\Stdlib\Filter;
|
||||
|
|
@ -79,10 +79,7 @@ class ServiceController extends Controller
|
|||
$this->service = $service;
|
||||
$this->loadTabsForObject($service);
|
||||
|
||||
$this->addControl((new ServiceList([$service]))
|
||||
->setViewMode('objectHeader')
|
||||
->setDetailActionsDisabled()
|
||||
->setNoSubjectLink());
|
||||
$this->addControl(new ObjectHeader($service));
|
||||
|
||||
$this->setTitleTab($this->getRequest()->getActionName());
|
||||
$this->setTitle(
|
||||
|
|
@ -159,7 +156,7 @@ class ServiceController extends Controller
|
|||
$this->addControl($searchBar);
|
||||
|
||||
$this->addContent(
|
||||
(new DependencyNodeList($nodesQuery))
|
||||
(new ObjectList($nodesQuery))
|
||||
->setViewMode($viewModeSwitcher->getViewMode())
|
||||
);
|
||||
|
||||
|
|
@ -230,7 +227,7 @@ class ServiceController extends Controller
|
|||
$this->addControl($searchBar);
|
||||
|
||||
$this->addContent(
|
||||
(new DependencyNodeList($nodesQuery))
|
||||
(new ObjectList($nodesQuery))
|
||||
->setViewMode($viewModeSwitcher->getViewMode())
|
||||
);
|
||||
|
||||
|
|
@ -316,7 +313,7 @@ class ServiceController extends Controller
|
|||
$this->addControl($limitControl);
|
||||
$this->addControl($viewModeSwitcher);
|
||||
|
||||
$historyList = (new HistoryList($history->execute()))
|
||||
$historyList = (new LoadMoreObjectList($history->execute()))
|
||||
->setViewMode($viewModeSwitcher->getViewMode())
|
||||
->setPageSize($limitControl->getLimit())
|
||||
->setLoadMoreUrl($url->setParam('before', $before));
|
||||
|
|
@ -515,6 +512,6 @@ class ServiceController extends Controller
|
|||
|
||||
protected function getDefaultTabControls(): array
|
||||
{
|
||||
return [(new ServiceList([$this->service]))->setDetailActionsDisabled()->setNoSubjectLink()];
|
||||
return [new ObjectHeader($this->service)];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ use Icinga\Module\Icingadb\Redis\VolatileStateResults;
|
|||
use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
|
||||
use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher;
|
||||
use Icinga\Module\Icingadb\Web\Controller;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ServiceList;
|
||||
use Icinga\Module\Icingadb\Widget\ItemTable\ServicegroupTableRow;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\ObjectHeader;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ObjectList;
|
||||
use ipl\Html\Html;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Control\LimitControl;
|
||||
|
|
@ -117,15 +117,15 @@ class ServicegroupController extends Controller
|
|||
|
||||
yield $this->export($services);
|
||||
|
||||
$serviceList = (new ServiceList($services->execute()))
|
||||
$serviceList = (new ObjectList($services))
|
||||
->setViewMode($viewModeSwitcher->getViewMode());
|
||||
|
||||
// ICINGAWEB_EXPORT_FORMAT is not set yet and $this->format is inaccessible, yeah...
|
||||
if ($this->getRequest()->getParam('format') === 'pdf') {
|
||||
$this->addContent(new ServicegroupTableRow($servicegroup));
|
||||
$this->addContent(new ObjectHeader($servicegroup));
|
||||
$this->addContent(Html::tag('h2', null, t('Services')));
|
||||
} else {
|
||||
$this->addControl(new ServicegroupTableRow($servicegroup));
|
||||
$this->addControl(new ObjectHeader($servicegroup));
|
||||
}
|
||||
|
||||
$this->addControl($paginationControl);
|
||||
|
|
|
|||
|
|
@ -7,14 +7,19 @@ namespace Icinga\Module\Icingadb\Controllers;
|
|||
use GuzzleHttp\Psr7\ServerRequest;
|
||||
use Icinga\Module\Icingadb\Model\Servicegroup;
|
||||
use Icinga\Module\Icingadb\Model\ServicegroupSummary;
|
||||
use Icinga\Module\Icingadb\View\ServicegroupGridRenderer;
|
||||
use Icinga\Module\Icingadb\View\ServicegroupRenderer;
|
||||
use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
|
||||
use Icinga\Module\Icingadb\Web\Controller;
|
||||
use Icinga\Module\Icingadb\Widget\ItemTable\ServicegroupTable;
|
||||
use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher;
|
||||
use Icinga\Module\Icingadb\Widget\ItemTable\ObjectGrid;
|
||||
use Icinga\Module\Icingadb\Widget\ItemTable\ObjectTable;
|
||||
use Icinga\Module\Icingadb\Widget\ShowMore;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Web\Control\LimitControl;
|
||||
use ipl\Web\Control\SortControl;
|
||||
use ipl\Web\Url;
|
||||
use ipl\Web\Widget\ItemList;
|
||||
|
||||
class ServicegroupsController extends Controller
|
||||
{
|
||||
|
|
@ -87,11 +92,13 @@ class ServicegroupsController extends Controller
|
|||
|
||||
$results = $servicegroups->execute();
|
||||
|
||||
$this->addContent(
|
||||
(new ServicegroupTable($results))
|
||||
->setBaseFilter($filter)
|
||||
->setViewMode($viewModeSwitcher->getViewMode())
|
||||
);
|
||||
if ($viewModeSwitcher->getViewMode() === 'grid') {
|
||||
$content = new ObjectGrid($results, (new ServicegroupGridRenderer())->setBaseFilter($filter));
|
||||
} else {
|
||||
$content = new ObjectTable($results, (new ServicegroupRenderer())->setBaseFilter($filter));
|
||||
}
|
||||
|
||||
$this->addContent($content);
|
||||
|
||||
if ($compact) {
|
||||
$this->addContent(
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
|
|||
use Icinga\Module\Icingadb\Web\Controller;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\MultiselectQuickActions;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\ObjectsDetail;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ServiceList;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ObjectList;
|
||||
use Icinga\Module\Icingadb\Widget\ItemTable\ServiceItemTable;
|
||||
use Icinga\Module\Icingadb\Widget\ServiceStatusBar;
|
||||
use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher;
|
||||
|
|
@ -115,7 +115,7 @@ class ServicesController extends Controller
|
|||
$serviceList = (new ServiceItemTable($results, ServiceItemTable::applyColumnMetaData($services, $columns)))
|
||||
->setSort($sortControl->getSort());
|
||||
} else {
|
||||
$serviceList = (new ServiceList($results))
|
||||
$serviceList = (new ObjectList($results))
|
||||
->setViewMode($viewModeSwitcher->getViewMode());
|
||||
}
|
||||
|
||||
|
|
@ -182,7 +182,7 @@ class ServicesController extends Controller
|
|||
$summary->comments_total = $comments->count();
|
||||
|
||||
$this->addControl(
|
||||
(new ServiceList($results))
|
||||
(new ObjectList($results))
|
||||
->setViewMode('minimal')
|
||||
->setDetailActionsDisabled()
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ namespace Icinga\Module\Icingadb\Controllers;
|
|||
use Icinga\Exception\NotFoundError;
|
||||
use Icinga\Module\Icingadb\Model\User;
|
||||
use Icinga\Module\Icingadb\Web\Controller;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\ObjectHeader;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\UserDetail;
|
||||
use Icinga\Module\Icingadb\Widget\ItemTable\UserTableRow;
|
||||
use ipl\Stdlib\Filter;
|
||||
|
||||
class UserController extends Controller
|
||||
|
|
@ -40,7 +40,7 @@ class UserController extends Controller
|
|||
|
||||
public function indexAction()
|
||||
{
|
||||
$this->addControl(new UserTableRow($this->user));
|
||||
$this->addControl(new ObjectHeader($this->user));
|
||||
$this->addContent(new UserDetail($this->user));
|
||||
|
||||
$this->setAutorefreshInterval(10);
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ namespace Icinga\Module\Icingadb\Controllers;
|
|||
use Icinga\Exception\NotFoundError;
|
||||
use Icinga\Module\Icingadb\Model\Usergroup;
|
||||
use Icinga\Module\Icingadb\Web\Controller;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\ObjectHeader;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\UsergroupDetail;
|
||||
use Icinga\Module\Icingadb\Widget\ItemTable\UsergroupTableRow;
|
||||
use ipl\Stdlib\Filter;
|
||||
|
||||
class UsergroupController extends Controller
|
||||
|
|
@ -40,7 +40,7 @@ class UsergroupController extends Controller
|
|||
|
||||
public function indexAction()
|
||||
{
|
||||
$this->addControl(new UsergroupTableRow($this->usergroup));
|
||||
$this->addControl(new ObjectHeader($this->usergroup));
|
||||
$this->addContent(new UsergroupDetail($this->usergroup));
|
||||
|
||||
$this->setAutorefreshInterval(10);
|
||||
|
|
|
|||
|
|
@ -8,10 +8,11 @@ use GuzzleHttp\Psr7\ServerRequest;
|
|||
use Icinga\Module\Icingadb\Model\Usergroup;
|
||||
use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
|
||||
use Icinga\Module\Icingadb\Web\Controller;
|
||||
use Icinga\Module\Icingadb\Widget\ItemTable\UsergroupTable;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ObjectList;
|
||||
use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher;
|
||||
use ipl\Web\Control\LimitControl;
|
||||
use ipl\Web\Control\SortControl;
|
||||
use ipl\Web\Url;
|
||||
|
||||
class UsergroupsController extends Controller
|
||||
{
|
||||
|
|
@ -64,7 +65,7 @@ class UsergroupsController extends Controller
|
|||
$this->addControl($limitControl);
|
||||
$this->addControl($searchBar);
|
||||
|
||||
$this->addContent(new UsergroupTable($usergroups));
|
||||
$this->addContent(new ObjectList($usergroups));
|
||||
|
||||
if (! $searchBar->hasBeenSubmitted() && $searchBar->hasBeenSent()) {
|
||||
$this->sendMultipartUpdate();
|
||||
|
|
|
|||
|
|
@ -8,10 +8,11 @@ use GuzzleHttp\Psr7\ServerRequest;
|
|||
use Icinga\Module\Icingadb\Model\User;
|
||||
use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
|
||||
use Icinga\Module\Icingadb\Web\Controller;
|
||||
use Icinga\Module\Icingadb\Widget\ItemTable\UserTable;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ObjectList;
|
||||
use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher;
|
||||
use ipl\Web\Control\LimitControl;
|
||||
use ipl\Web\Control\SortControl;
|
||||
use ipl\Web\Url;
|
||||
|
||||
class UsersController extends Controller
|
||||
{
|
||||
|
|
@ -66,7 +67,7 @@ class UsersController extends Controller
|
|||
$this->addControl($limitControl);
|
||||
$this->addControl($searchBar);
|
||||
|
||||
$this->addContent(new UserTable($users));
|
||||
$this->addContent(new ObjectList($users));
|
||||
|
||||
if (! $searchBar->hasBeenSubmitted() && $searchBar->hasBeenSent()) {
|
||||
$this->sendMultipartUpdate();
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Common;
|
||||
|
||||
trait CaptionDisabled
|
||||
{
|
||||
/** @var bool */
|
||||
protected $captionDisabled = false;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isCaptionDisabled(): bool
|
||||
{
|
||||
return $this->captionDisabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $captionDisabled
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCaptionDisabled(bool $captionDisabled = true): self
|
||||
{
|
||||
$this->captionDisabled = $captionDisabled;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Common;
|
||||
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
|
||||
trait ListItemCommonLayout
|
||||
{
|
||||
use CaptionDisabled;
|
||||
|
||||
protected function assembleHeader(BaseHtmlElement $header): void
|
||||
{
|
||||
$header->addHtml($this->createTitle());
|
||||
$header->add($this->createTimestamp());
|
||||
}
|
||||
|
||||
protected function assembleMain(BaseHtmlElement $main): void
|
||||
{
|
||||
$main->addHtml($this->createHeader());
|
||||
if (!$this->isCaptionDisabled()) {
|
||||
$main->addHtml($this->createCaption());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Common;
|
||||
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
|
||||
trait ListItemDetailedLayout
|
||||
{
|
||||
protected function assembleHeader(BaseHtmlElement $header): void
|
||||
{
|
||||
$header->add($this->createTitle());
|
||||
$header->add($this->createTimestamp());
|
||||
}
|
||||
|
||||
protected function assembleMain(BaseHtmlElement $main): void
|
||||
{
|
||||
$main->add($this->createHeader());
|
||||
$main->add($this->createCaption());
|
||||
$main->add($this->createFooter());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Common;
|
||||
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
|
||||
trait ListItemMinimalLayout
|
||||
{
|
||||
use CaptionDisabled;
|
||||
|
||||
protected function assembleHeader(BaseHtmlElement $header): void
|
||||
{
|
||||
$header->add($this->createTitle());
|
||||
if (! $this->isCaptionDisabled()) {
|
||||
$header->add($this->createCaption());
|
||||
}
|
||||
$header->add($this->createTimestamp());
|
||||
}
|
||||
|
||||
protected function assembleMain(BaseHtmlElement $main): void
|
||||
{
|
||||
$main->add($this->createHeader());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Common;
|
||||
|
||||
trait NoSubjectLink
|
||||
{
|
||||
/** @var bool */
|
||||
protected $noSubjectLink = false;
|
||||
|
||||
/**
|
||||
* Set whether a list item's subject should be a link
|
||||
*
|
||||
* @param bool $state
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setNoSubjectLink(bool $state = true): self
|
||||
{
|
||||
$this->noSubjectLink = $state;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether a list item's subject should be a link
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getNoSubjectLink(): bool
|
||||
{
|
||||
return $this->noSubjectLink;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Common;
|
||||
|
||||
trait ObjectLinkDisabled
|
||||
{
|
||||
/** @var bool */
|
||||
protected $objectLinkDisabled = false;
|
||||
|
||||
/**
|
||||
* Set whether list items should render host and service links
|
||||
*
|
||||
* @param bool $state
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setObjectLinkDisabled(bool $state = true): self
|
||||
{
|
||||
$this->objectLinkDisabled = $state;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether list items should render host and service links
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getObjectLinkDisabled(): bool
|
||||
{
|
||||
return $this->objectLinkDisabled;
|
||||
}
|
||||
}
|
||||
|
|
@ -5,34 +5,35 @@
|
|||
namespace Icinga\Module\Icingadb\Common;
|
||||
|
||||
use Icinga\Application\Hook;
|
||||
use Icinga\Application\Hook\TicketHook;
|
||||
|
||||
trait TicketLinks
|
||||
{
|
||||
/** @var bool */
|
||||
protected $ticketLinkEnabled = false;
|
||||
/** @var bool Whether the ticket link is disabled */
|
||||
protected $ticketLinkDisabled = false;
|
||||
|
||||
/**
|
||||
* Set whether list items should render host and service links
|
||||
* Set whether the ticket link is disabled
|
||||
*
|
||||
* @param bool $state
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setTicketLinkEnabled(bool $state = true): self
|
||||
public function setTicketLinkDisabled(bool $state = true): self
|
||||
{
|
||||
$this->ticketLinkEnabled = $state;
|
||||
$this->ticketLinkDisabled = $state;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether list items should render host and service links
|
||||
* Get whether the ticket link is disabled
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getTicketLinkEnabled(): bool
|
||||
public function isTicketLinkDisabled(): bool
|
||||
{
|
||||
return $this->ticketLinkEnabled;
|
||||
return $this->ticketLinkDisabled;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -42,15 +43,13 @@ trait TicketLinks
|
|||
*/
|
||||
public function createTicketLinks($text): string
|
||||
{
|
||||
if (Hook::has('ticket')) {
|
||||
$tickets = Hook::first('ticket');
|
||||
if ($this->isTicketLinkDisabled() || ! Hook::has('ticket')) {
|
||||
return $text ?? '';
|
||||
}
|
||||
|
||||
if ($this->getTicketLinkEnabled() && isset($tickets)) {
|
||||
/** @var \Icinga\Application\Hook\TicketHook $tickets */
|
||||
return $tickets->createLinks($text);
|
||||
}
|
||||
/** @var TicketHook $tickets */
|
||||
$tickets = Hook::first('ticket');
|
||||
|
||||
return $text ?? '';
|
||||
return $tickets->createLinks($text);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Common;
|
||||
|
||||
trait ViewMode
|
||||
{
|
||||
/** @var string */
|
||||
protected $viewMode;
|
||||
|
||||
/**
|
||||
* Get the view mode
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public function getViewMode()
|
||||
{
|
||||
return $this->viewMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the view mode
|
||||
*
|
||||
* @param string $viewMode
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setViewMode(string $viewMode): self
|
||||
{
|
||||
$this->viewMode = $viewMode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -4,13 +4,16 @@
|
|||
|
||||
namespace Icinga\Module\Icingadb\Model;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\Auth;
|
||||
use Icinga\Module\Icingadb\Common\Backend;
|
||||
use Icinga\Module\Icingadb\Model\Behavior\ReRoute;
|
||||
use ipl\Orm\Behavior\Binary;
|
||||
use ipl\Orm\Behavior\BoolCast;
|
||||
use ipl\Orm\Behaviors;
|
||||
use ipl\Orm\Defaults;
|
||||
use ipl\Orm\Model;
|
||||
use ipl\Orm\Query;
|
||||
use ipl\Orm\Relations;
|
||||
use ipl\Stdlib\Filter;
|
||||
|
||||
/**
|
||||
* Redundancy group model.
|
||||
|
|
@ -22,9 +25,13 @@ use ipl\Orm\Relations;
|
|||
* @property (?RedundancyGroupState)|Query $state
|
||||
* @property DependencyEdge|Query $from
|
||||
* @property DependencyEdge|Query $to
|
||||
*
|
||||
* @property RedundancyGroupSummary $summary
|
||||
*/
|
||||
class RedundancyGroup extends Model
|
||||
{
|
||||
use Auth;
|
||||
|
||||
public function getTableName(): string
|
||||
{
|
||||
return 'redundancy_group';
|
||||
|
|
@ -78,4 +85,16 @@ class RedundancyGroup extends Model
|
|||
->setTargetForeignKey('id')
|
||||
->through(DependencyNode::class);
|
||||
}
|
||||
|
||||
public function createDefaults(Defaults $defaults)
|
||||
{
|
||||
$defaults->add('summary', function (RedundancyGroup $group) {
|
||||
$summary = RedundancyGroupSummary::on(Backend::getDb())
|
||||
->filter(Filter::equal('id', $group->id));
|
||||
|
||||
$this->applyRestrictions($summary);
|
||||
|
||||
return $summary->first();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
304
library/Icingadb/View/BaseHostAndServiceRenderer.php
Normal file
304
library/Icingadb/View/BaseHostAndServiceRenderer.php
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\View;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\HostStates;
|
||||
use Icinga\Module\Icingadb\Common\Icons;
|
||||
use Icinga\Module\Icingadb\Common\ServiceStates;
|
||||
use Icinga\Module\Icingadb\Model\Host;
|
||||
use Icinga\Module\Icingadb\Model\Service;
|
||||
use Icinga\Module\Icingadb\Util\PerfDataSet;
|
||||
use Icinga\Module\Icingadb\Util\PluginOutput;
|
||||
use Icinga\Module\Icingadb\Widget\CheckAttempt;
|
||||
use Icinga\Module\Icingadb\Widget\IconImage;
|
||||
use Icinga\Module\Icingadb\Widget\PluginOutputContainer;
|
||||
use Icinga\Module\Icingadb\Widget\StateChange;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\Html;
|
||||
use ipl\Html\HtmlDocument;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\HtmlString;
|
||||
use ipl\Html\Text;
|
||||
use ipl\Html\ValidHtml;
|
||||
use ipl\I18n\Translation;
|
||||
use ipl\Web\Common\ItemRenderer;
|
||||
use ipl\Web\Layout\ItemLayout;
|
||||
use ipl\Web\Widget\EmptyState;
|
||||
use ipl\Web\Widget\Icon;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
use ipl\Web\Widget\TimeSince;
|
||||
|
||||
/**
|
||||
* @template Item of Host|Service
|
||||
*
|
||||
* @implements ItemRenderer<Item>
|
||||
*/
|
||||
abstract class BaseHostAndServiceRenderer implements ItemRenderer
|
||||
{
|
||||
use Translation;
|
||||
|
||||
/**
|
||||
* Create subject for the given item
|
||||
*
|
||||
* @param Item $item The item to create subject for
|
||||
*
|
||||
* @param string $layout The name of the layout
|
||||
*
|
||||
* @return ValidHtml
|
||||
*/
|
||||
abstract protected function createSubject($item, string $layout): ValidHtml;
|
||||
|
||||
public function assembleVisual($item, HtmlDocument $visual, string $layout): void
|
||||
{
|
||||
if ($layout === 'header') {
|
||||
if ($item->state->state_type === 'soft') {
|
||||
$stateType = 'soft_state';
|
||||
$previousStateType = 'previous_soft_state';
|
||||
|
||||
if ($item->state->previous_soft_state === 0) {
|
||||
$previousStateType = 'hard_state';
|
||||
}
|
||||
} else {
|
||||
$stateType = 'hard_state';
|
||||
$previousStateType = 'previous_hard_state';
|
||||
|
||||
if ($item->state->hard_state === $item->state->previous_hard_state) {
|
||||
$previousStateType = 'previous_soft_state';
|
||||
}
|
||||
}
|
||||
|
||||
if ($item instanceof Host) {
|
||||
$state = HostStates::text($item->state->$stateType);
|
||||
$previousState = HostStates::text($item->state->$previousStateType);
|
||||
} else {
|
||||
$state = ServiceStates::text($item->state->$stateType);
|
||||
$previousState = ServiceStates::text($item->state->$previousStateType);
|
||||
}
|
||||
|
||||
$stateChange = new StateChange($state, $previousState);
|
||||
if ($stateType === 'soft_state') {
|
||||
$stateChange->setCurrentStateBallSize(StateBall::SIZE_MEDIUM_LARGE);
|
||||
}
|
||||
|
||||
if ($previousStateType === 'previous_soft_state') {
|
||||
$stateChange->setPreviousStateBallSize(StateBall::SIZE_MEDIUM_LARGE);
|
||||
if ($stateType === 'soft_state') {
|
||||
$visual->getAttributes()->add('class', 'small-state-change');
|
||||
}
|
||||
}
|
||||
|
||||
$stateChange->setIcon($item->state->getIcon());
|
||||
$stateChange->setHandled(
|
||||
$item->state->is_problem && ($item->state->is_handled || ! $item->state->is_reachable)
|
||||
);
|
||||
|
||||
$visual->addHtml($stateChange);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$ballSize = $layout === 'minimal' ? StateBall::SIZE_BIG : StateBall::SIZE_LARGE;
|
||||
|
||||
$stateBall = new StateBall($item->state->getStateText(), $ballSize);
|
||||
$stateBall->add($item->state->getIcon());
|
||||
if ($item->state->is_problem && ($item->state->is_handled || ! $item->state->is_reachable)) {
|
||||
$stateBall->getAttributes()->add('class', 'handled');
|
||||
}
|
||||
|
||||
$visual->addHtml($stateBall);
|
||||
if ($layout !== 'minimal' && $item->state->state_type === 'soft') {
|
||||
$visual->addHtml(
|
||||
new CheckAttempt((int) $item->state->check_attempt, (int) $item->max_check_attempts)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function assembleCaption($item, HtmlDocument $caption, string $layout): void
|
||||
{
|
||||
if ($item->state->soft_state === null && $item->state->output === null) {
|
||||
$caption->addHtml(Text::create($this->translate('Waiting for Icinga DB to synchronize the state.')));
|
||||
} else {
|
||||
if (empty($item->state->output)) {
|
||||
$pluginOutput = new EmptyState($this->translate('Output unavailable.'));
|
||||
} else {
|
||||
$pluginOutput = new PluginOutputContainer(PluginOutput::fromObject($item));
|
||||
}
|
||||
|
||||
$caption->addHtml($pluginOutput);
|
||||
}
|
||||
}
|
||||
|
||||
public function assembleTitle($item, HtmlDocument $title, string $layout): void
|
||||
{
|
||||
$title->addHtml(Html::sprintf(
|
||||
$this->translate('%s is %s', '<hostname> is <state-text>'),
|
||||
$this->createSubject($item, $layout),
|
||||
Html::tag('span', ['class' => 'state-text'], $item->state->getStateTextTranslated())
|
||||
));
|
||||
|
||||
if (isset($item->state->affects_children) && $item->state->affects_children) {
|
||||
$total = (int) $item->total_children;
|
||||
|
||||
if ($total > 1000) {
|
||||
$total = '1000+';
|
||||
$tooltip = $this->translate('Up to 1000+ affected objects');
|
||||
} else {
|
||||
$tooltip = sprintf(
|
||||
$this->translatePlural(
|
||||
'%d affected object',
|
||||
'Up to %d affected objects',
|
||||
$total
|
||||
),
|
||||
$total
|
||||
);
|
||||
}
|
||||
|
||||
$icon = new Icon(Icons::UNREACHABLE);
|
||||
|
||||
$title->addHtml(new HtmlElement(
|
||||
'span',
|
||||
Attributes::create([
|
||||
'class' => 'affected-objects',
|
||||
'title' => $tooltip
|
||||
]),
|
||||
$icon,
|
||||
Text::create($total)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void
|
||||
{
|
||||
if ($item->state->is_overdue) {
|
||||
$since = new TimeSince($item->state->next_update->getTimestamp());
|
||||
$since->prepend($this->translate('Overdue') . ' ');
|
||||
$since->prependHtml(new Icon(Icons::WARNING));
|
||||
|
||||
$info->addHtml($since);
|
||||
} elseif ($item->state->last_state_change !== null && $item->state->last_state_change->getTimestamp() > 0) {
|
||||
$info->addHtml(new TimeSince($item->state->last_state_change->getTimestamp()));
|
||||
}
|
||||
}
|
||||
|
||||
public function assembleFooter($item, HtmlDocument $footer, string $layout): void
|
||||
{
|
||||
$pieChartLimit = 5;
|
||||
$statusIcons = new HtmlElement('div', Attributes::create(['class' => 'status-icons']));
|
||||
$isService = $item instanceof Service;
|
||||
|
||||
if (
|
||||
($isService && $item->state->last_comment->service_id === $item->id)
|
||||
|| $item->state->last_comment->host_id === $item->id
|
||||
) {
|
||||
$comment = $item->state->last_comment;
|
||||
|
||||
if ($isService) {
|
||||
$comment->service = $item;
|
||||
} else {
|
||||
$comment->host = $item;
|
||||
}
|
||||
|
||||
$commentItem = new ItemLayout(
|
||||
$comment,
|
||||
(new CommentRenderer())
|
||||
->setTicketLinkDisabled()
|
||||
->setNoObjectLink()
|
||||
->setNoSubjectLink()
|
||||
);
|
||||
|
||||
$statusIcons->addHtml(
|
||||
new HtmlElement(
|
||||
'div',
|
||||
Attributes::create(['class' => 'comment-wrapper']),
|
||||
new HtmlElement(
|
||||
'div',
|
||||
$commentItem->getAttributes()->add('class', 'comment-popup'),
|
||||
$commentItem
|
||||
),
|
||||
(new Icon('comments', ['class' => 'comment-icon']))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($item->state->is_flapping) {
|
||||
$title = $isService
|
||||
? sprintf(
|
||||
$this->translate('Service "%s" on "%s" is in flapping state'),
|
||||
$item->display_name,
|
||||
$item->host->display_name
|
||||
)
|
||||
: sprintf(
|
||||
$this->translate('Host "%s" is in flapping state'),
|
||||
$item->display_name
|
||||
);
|
||||
|
||||
$statusIcons->addHtml(new Icon('random', ['title' => $title]));
|
||||
}
|
||||
|
||||
if (! $item->notifications_enabled) {
|
||||
$statusIcons->addHtml(
|
||||
new Icon('bell-slash', ['title' => $this->translate('Notifications disabled')])
|
||||
);
|
||||
}
|
||||
|
||||
if (! $item->active_checks_enabled) {
|
||||
$statusIcons->addHtml(
|
||||
new Icon('eye-slash', ['title' => $this->translate('Active checks disabled')])
|
||||
);
|
||||
}
|
||||
|
||||
$performanceData = new HtmlElement('div', Attributes::create(['class' => 'performance-data']));
|
||||
if ($item->state->performance_data) {
|
||||
$pieChartData = PerfDataSet::fromString($item->state->normalized_performance_data)->asArray();
|
||||
|
||||
$pies = [];
|
||||
foreach ($pieChartData as $i => $perfdata) {
|
||||
if ($perfdata->isVisualizable()) {
|
||||
$pies[] = $perfdata->asInlinePie()->render();
|
||||
}
|
||||
|
||||
// Check if number of visualizable pie charts is larger than $PIE_CHART_LIMIT
|
||||
if (count($pies) > $pieChartLimit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$maxVisiblePies = $pieChartLimit - 2;
|
||||
$numOfPies = count($pies);
|
||||
foreach ($pies as $i => $pie) {
|
||||
if (
|
||||
// Show max. 5 elements: if there are more than 5, show 4 + `…`
|
||||
$i > $maxVisiblePies && $numOfPies > $pieChartLimit
|
||||
) {
|
||||
$performanceData->addHtml(new HtmlElement('span', null, Text::create('…')));
|
||||
break;
|
||||
}
|
||||
|
||||
$performanceData->addHtml(HtmlString::create($pie));
|
||||
}
|
||||
}
|
||||
|
||||
if (! $statusIcons->isEmpty()) {
|
||||
$footer->addHtml($statusIcons);
|
||||
}
|
||||
|
||||
if (! $performanceData->isEmpty()) {
|
||||
$footer->addHtml($performanceData);
|
||||
}
|
||||
}
|
||||
|
||||
public function assemble($item, string $name, HtmlDocument $element, string $layout): bool
|
||||
{
|
||||
if ($name === 'icon-image') {
|
||||
if (isset($item->icon_image->icon_image)) {
|
||||
$element->addHtml(new IconImage($item->icon_image->icon_image, $item->icon_image_alt));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
167
library/Icingadb/View/CommentRenderer.php
Normal file
167
library/Icingadb/View/CommentRenderer.php
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\View;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\HostLink;
|
||||
use Icinga\Module\Icingadb\Common\Icons;
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Common\ServiceLink;
|
||||
use Icinga\Module\Icingadb\Common\TicketLinks;
|
||||
use Icinga\Module\Icingadb\Model\Comment;
|
||||
use Icinga\Module\Icingadb\Widget\MarkdownLine;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\FormattedString;
|
||||
use ipl\Html\HtmlDocument;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\I18n\Translation;
|
||||
use ipl\Web\Common\ItemRenderer;
|
||||
use ipl\Web\Widget\Icon;
|
||||
use ipl\Web\Widget\Link;
|
||||
use ipl\Web\Widget\TimeAgo;
|
||||
use ipl\Web\Widget\TimeUntil;
|
||||
|
||||
/** @implements ItemRenderer<Comment> */
|
||||
class CommentRenderer implements ItemRenderer
|
||||
{
|
||||
use Translation;
|
||||
use TicketLinks;
|
||||
use HostLink;
|
||||
use ServiceLink;
|
||||
|
||||
/** @var bool Whether the object link for th item should be omitted */
|
||||
protected $noObjectLink = false;
|
||||
|
||||
/** @var bool Whether item's subject should be a link */
|
||||
protected $noSubjectLink = false;
|
||||
|
||||
/**
|
||||
* Set whether the object link for th item should be omitted
|
||||
*
|
||||
* @param bool $state
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setNoObjectLink(bool $state = true): self
|
||||
{
|
||||
$this->noObjectLink = $state;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether item's subject should be a link
|
||||
*
|
||||
* @param bool $state
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setNoSubjectLink(bool $state = true): self
|
||||
{
|
||||
$this->noSubjectLink = $state;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function assembleAttributes($item, Attributes $attributes, string $layout): void
|
||||
{
|
||||
$attributes->get('class')->addValue('comment');
|
||||
}
|
||||
|
||||
public function assembleVisual($item, HtmlDocument $visual, string $layout): void
|
||||
{
|
||||
$visual->addHtml(new HtmlElement(
|
||||
'div',
|
||||
Attributes::create(['class' => 'user-ball']),
|
||||
Text::create($item->author[0])
|
||||
));
|
||||
}
|
||||
|
||||
public function assembleTitle($item, HtmlDocument $title, string $layout): void
|
||||
{
|
||||
$isAck = $item->entry_type === 'ack';
|
||||
$expires = $item->expire_time;
|
||||
|
||||
$subjectText = sprintf(
|
||||
$isAck
|
||||
? $this->translate('%s acknowledged', '<username>..')
|
||||
: $this->translate('%s commented', '<username>..'),
|
||||
$item->author
|
||||
);
|
||||
|
||||
$headerParts = [
|
||||
new Icon(Icons::USER),
|
||||
$layout === 'header' || $this->noSubjectLink
|
||||
? new HtmlElement('span', Attributes::create(['class' => 'subject']), Text::create($subjectText))
|
||||
: new Link($subjectText, Links::comment($item), ['class' => 'subject'])
|
||||
];
|
||||
|
||||
if ($isAck) {
|
||||
$label = [Text::create('ack')];
|
||||
|
||||
if ($item->is_persistent) {
|
||||
array_unshift($label, new Icon(Icons::IS_PERSISTENT));
|
||||
}
|
||||
|
||||
$headerParts[] = Text::create(' ');
|
||||
$headerParts[] = new HtmlElement('span', Attributes::create(['class' => 'ack-badge badge']), ...$label);
|
||||
}
|
||||
|
||||
if ($expires !== null) {
|
||||
$headerParts[] = Text::create(' ');
|
||||
$headerParts[] = new HtmlElement(
|
||||
'span',
|
||||
Attributes::create(['class' => 'ack-badge badge']),
|
||||
Text::create($this->translate('EXPIRES'))
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->noObjectLink) {
|
||||
// pass
|
||||
} elseif ($item->object_type === 'host') {
|
||||
$headerParts[] = $this->createHostLink($item->host, true);
|
||||
} else {
|
||||
$headerParts[] = $this->createServiceLink($item->service, $item->service->host, true);
|
||||
}
|
||||
|
||||
$title->addHtml(...$headerParts);
|
||||
}
|
||||
|
||||
public function assembleCaption($item, HtmlDocument $caption, string $layout): void
|
||||
{
|
||||
$markdownLine = new MarkdownLine($this->createTicketLinks($item->text));
|
||||
|
||||
$caption->getAttributes()->add($markdownLine->getAttributes());
|
||||
$caption->addFrom($markdownLine);
|
||||
}
|
||||
|
||||
public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void
|
||||
{
|
||||
if ($item->expire_time) {
|
||||
$info->addHtml(
|
||||
FormattedString::create(
|
||||
$this->translate("expires %s"),
|
||||
new TimeUntil($item->expire_time->getTimestamp())
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$info->addHtml(
|
||||
FormattedString::create(
|
||||
$this->translate("created %s"),
|
||||
new TimeAgo($item->entry_time->getTimestamp())
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function assembleFooter($item, HtmlDocument $footer, string $layout): void
|
||||
{
|
||||
}
|
||||
|
||||
public function assemble($item, string $name, HtmlDocument $element, string $layout): bool
|
||||
{
|
||||
return false; // no custom sections
|
||||
}
|
||||
}
|
||||
246
library/Icingadb/View/DowntimeRenderer.php
Normal file
246
library/Icingadb/View/DowntimeRenderer.php
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\View;
|
||||
|
||||
use Icinga\Date\DateFormatter;
|
||||
use Icinga\Module\Icingadb\Common\HostLink;
|
||||
use Icinga\Module\Icingadb\Common\Icons;
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Common\ServiceLink;
|
||||
use Icinga\Module\Icingadb\Common\TicketLinks;
|
||||
use Icinga\Module\Icingadb\Model\Downtime;
|
||||
use Icinga\Module\Icingadb\Widget\MarkdownLine;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\Html;
|
||||
use ipl\Html\HtmlDocument;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\TemplateString;
|
||||
use ipl\Html\Text;
|
||||
use ipl\I18n\Translation;
|
||||
use ipl\Web\Common\ItemRenderer;
|
||||
use ipl\Web\Widget\Icon;
|
||||
use ipl\Web\Widget\Link;
|
||||
|
||||
/** @implements ItemRenderer<Downtime> */
|
||||
class DowntimeRenderer implements ItemRenderer
|
||||
{
|
||||
use Translation;
|
||||
use TicketLinks;
|
||||
use HostLink;
|
||||
use ServiceLink;
|
||||
|
||||
/** @var int Current Time */
|
||||
protected $currentTime;
|
||||
|
||||
/** @var int Duration */
|
||||
protected $duration;
|
||||
|
||||
/** @var int Downtime end time */
|
||||
protected $endTime;
|
||||
|
||||
/** @var bool Whether the downtime is active */
|
||||
protected $isActive;
|
||||
|
||||
/** @var int Downtime start time */
|
||||
protected $startTime;
|
||||
|
||||
/** @var bool Whether the state has been loaded */
|
||||
protected $stateLoaded = false;
|
||||
|
||||
/** @var bool Whether the object link for th item should be omitted */
|
||||
protected $noObjectLink = false;
|
||||
|
||||
/**
|
||||
* Set whether the object link for th item should be omitted
|
||||
*
|
||||
* @param bool $state
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setNoObjectLink(bool $state = true): self
|
||||
{
|
||||
$this->noObjectLink = $state;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the state of the downtime
|
||||
*
|
||||
* @param Downtime $item
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function loadState(Downtime $item): void
|
||||
{
|
||||
if ($this->stateLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
isset($item->start_time, $item->end_time)
|
||||
&& $item->is_flexible
|
||||
&& $item->is_in_effect
|
||||
) {
|
||||
$this->startTime = $item->start_time->getTimestamp();
|
||||
$this->endTime = $item->end_time->getTimestamp();
|
||||
} else {
|
||||
$this->startTime = $item->scheduled_start_time->getTimestamp();
|
||||
$this->endTime = $item->scheduled_end_time->getTimestamp();
|
||||
}
|
||||
|
||||
$this->currentTime = time();
|
||||
|
||||
$this->isActive = $item->is_in_effect
|
||||
|| ($item->is_flexible && $item->scheduled_start_time->getTimestamp() <= $this->currentTime);
|
||||
|
||||
$until = ($this->isActive ? $this->endTime : $this->startTime) - $this->currentTime;
|
||||
$this->duration = explode(' ', DateFormatter::formatDuration(
|
||||
$until <= 3600 ? $until : $until + (3600 - ((int) $until % 3600))
|
||||
), 2)[0];
|
||||
|
||||
$this->stateLoaded = true;
|
||||
}
|
||||
|
||||
public function assembleAttributes($item, Attributes $attributes, string $layout): void
|
||||
{
|
||||
$attributes->add(new Attributes(['class' => ['downtime', $item->is_in_effect ? 'in-effect' : '']]));
|
||||
}
|
||||
|
||||
public function assembleVisual($item, HtmlDocument $visual, string $layout): void
|
||||
{
|
||||
$this->loadState($item);
|
||||
|
||||
$dateTime = DateFormatter::formatDateTime($this->endTime);
|
||||
|
||||
if ($this->isActive) {
|
||||
$visual->addHtml(Html::sprintf(
|
||||
$this->translate('%s left', '<timespan>..'),
|
||||
Html::tag(
|
||||
'strong',
|
||||
Html::tag(
|
||||
'time',
|
||||
[
|
||||
'datetime' => $dateTime,
|
||||
'title' => $dateTime
|
||||
],
|
||||
$this->duration
|
||||
)
|
||||
)
|
||||
));
|
||||
} else {
|
||||
$visual->addHtml(Html::sprintf(
|
||||
$this->translate('in %s', '..<timespan>'),
|
||||
Html::tag('strong', $this->duration)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
public function assembleTitle($item, HtmlDocument $title, string $layout): void
|
||||
{
|
||||
if ($this->noObjectLink) {
|
||||
$link = null;
|
||||
} elseif ($item->object_type === 'host') {
|
||||
$link = $this->createHostLink($item->host, true);
|
||||
} else {
|
||||
$link = $this->createServiceLink($item->service, $item->service->host, true);
|
||||
}
|
||||
|
||||
if ($item->is_flexible) {
|
||||
if ($link !== null) {
|
||||
$template = $this->translate('{{#link}}Flexible Downtime{{/link}} for %s');
|
||||
} else {
|
||||
$template = $this->translate('Flexible Downtime');
|
||||
}
|
||||
} else {
|
||||
if ($link !== null) {
|
||||
$template = $this->translate('{{#link}}Fixed Downtime{{/link}} for %s');
|
||||
} else {
|
||||
$template = $this->translate('Fixed Downtime');
|
||||
}
|
||||
}
|
||||
|
||||
if ($layout === 'header') {
|
||||
if ($link === null) {
|
||||
$title->addHtml(HtmlElement::create('span', [ 'class' => 'subject'], $template));
|
||||
} else {
|
||||
$title->addHtml(TemplateString::create(
|
||||
$template,
|
||||
['link' => HtmlElement::create('span', [ 'class' => 'subject'])],
|
||||
$link
|
||||
));
|
||||
}
|
||||
} else {
|
||||
if ($link === null) {
|
||||
$title->addHtml(new Link($template, Links::downtime($item)));
|
||||
} else {
|
||||
$title->addHtml(TemplateString::create(
|
||||
$template,
|
||||
['link' => new Link('', Links::downtime($item))],
|
||||
$link
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function assembleCaption($item, HtmlDocument $caption, string $layout): void
|
||||
{
|
||||
$markdownLine = new MarkdownLine($this->createTicketLinks($item->comment));
|
||||
$caption->getAttributes()->add($markdownLine->getAttributes());
|
||||
$caption->addHtml(
|
||||
new HtmlElement(
|
||||
'span',
|
||||
null,
|
||||
new Icon(Icons::USER),
|
||||
Text::create($item->author)
|
||||
),
|
||||
Text::create(': ')
|
||||
)->addFrom($markdownLine);
|
||||
}
|
||||
|
||||
public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void
|
||||
{
|
||||
$this->loadState($item);
|
||||
|
||||
$dateTime = DateFormatter::formatDateTime($this->isActive ? $this->endTime : $this->startTime);
|
||||
|
||||
$info->addHtml(Html::tag(
|
||||
'time',
|
||||
[
|
||||
'datetime' => $dateTime,
|
||||
'title' => $dateTime
|
||||
],
|
||||
sprintf(
|
||||
$this->isActive
|
||||
? $this->translate('expires in %s', '..<timespan>')
|
||||
: $this->translate('starts in %s', '..<timespan>'),
|
||||
$this->duration
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
public function assembleFooter($item, HtmlDocument $footer, string $layout): void
|
||||
{
|
||||
}
|
||||
|
||||
public function assemble($item, string $name, HtmlDocument $element, string $layout): bool
|
||||
{
|
||||
if ($name === 'progress' && ($layout === 'detailed' || $layout === 'common')) {
|
||||
$this->loadState($item);
|
||||
|
||||
$element
|
||||
->addAttributes(Attributes::create([
|
||||
'data-animate-progress' => true,
|
||||
'data-start-time' => $this->startTime,
|
||||
'data-end-time' => $this->endTime
|
||||
]))
|
||||
->addHtml(new HtmlElement('div', Attributes::create(['class' => 'bar'])));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
425
library/Icingadb/View/EventRenderer.php
Normal file
425
library/Icingadb/View/EventRenderer.php
Normal file
|
|
@ -0,0 +1,425 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\View;
|
||||
|
||||
use Icinga\Date\DateFormatter;
|
||||
use Icinga\Module\Icingadb\Common\HostLink;
|
||||
use Icinga\Module\Icingadb\Common\HostStates;
|
||||
use Icinga\Module\Icingadb\Common\Icons;
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Common\ServiceLink;
|
||||
use Icinga\Module\Icingadb\Common\ServiceStates;
|
||||
use Icinga\Module\Icingadb\Common\TicketLinks;
|
||||
use Icinga\Module\Icingadb\Model\History;
|
||||
use Icinga\Module\Icingadb\Util\PluginOutput;
|
||||
use Icinga\Module\Icingadb\Widget\CheckAttempt;
|
||||
use Icinga\Module\Icingadb\Widget\MarkdownLine;
|
||||
use Icinga\Module\Icingadb\Widget\PluginOutputContainer;
|
||||
use Icinga\Module\Icingadb\Widget\StateChange;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\HtmlDocument;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\I18n\Translation;
|
||||
use ipl\Web\Common\ItemRenderer;
|
||||
use ipl\Web\Widget\EmptyState;
|
||||
use ipl\Web\Widget\Icon;
|
||||
use ipl\Web\Widget\Link;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
use ipl\Web\Widget\TimeAgo;
|
||||
|
||||
/** @implements ItemRenderer<History> */
|
||||
class EventRenderer implements ItemRenderer
|
||||
{
|
||||
use Translation;
|
||||
use TicketLinks;
|
||||
use HostLink;
|
||||
use ServiceLink;
|
||||
|
||||
/** @var NotificationRenderer To render NotificationHistory event */
|
||||
protected $notificationRenderer;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->notificationRenderer = new NotificationRenderer();
|
||||
}
|
||||
|
||||
public function assembleAttributes($item, Attributes $attributes, string $layout): void
|
||||
{
|
||||
$attributes->get('class')->addValue('history');
|
||||
}
|
||||
|
||||
public function assembleVisual($item, HtmlDocument $visual, string $layout): void
|
||||
{
|
||||
$ballSize = StateBall::SIZE_LARGE;
|
||||
if ($layout === 'minimal' || $layout === 'header') {
|
||||
$ballSize = StateBall::SIZE_BIG;
|
||||
}
|
||||
|
||||
switch ($item->event_type) {
|
||||
case 'comment_add':
|
||||
$visual->addHtml(
|
||||
HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $ballSize]],
|
||||
new Icon(Icons::COMMENT)
|
||||
)
|
||||
);
|
||||
|
||||
break;
|
||||
case 'comment_remove':
|
||||
case 'downtime_end':
|
||||
case 'ack_clear':
|
||||
$visual->addHtml(
|
||||
HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $ballSize]],
|
||||
new Icon(Icons::REMOVE)
|
||||
)
|
||||
);
|
||||
|
||||
break;
|
||||
case 'downtime_start':
|
||||
$visual->addHtml(
|
||||
HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $ballSize]],
|
||||
new Icon(Icons::IN_DOWNTIME)
|
||||
)
|
||||
);
|
||||
|
||||
break;
|
||||
case 'ack_set':
|
||||
$visual->addHtml(
|
||||
HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $ballSize]],
|
||||
new Icon(Icons::IS_ACKNOWLEDGED)
|
||||
)
|
||||
);
|
||||
|
||||
break;
|
||||
case 'flapping_end':
|
||||
case 'flapping_start':
|
||||
$visual->addHtml(
|
||||
HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $ballSize]],
|
||||
new Icon(Icons::IS_FLAPPING)
|
||||
)
|
||||
);
|
||||
|
||||
break;
|
||||
case 'notification':
|
||||
$visual->addHtml(
|
||||
HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $ballSize]],
|
||||
new Icon(Icons::NOTIFICATION)
|
||||
)
|
||||
);
|
||||
|
||||
break;
|
||||
case 'state_change':
|
||||
if ($layout !== 'minimal' && $layout !== 'header' && $item->state->state_type === 'soft') {
|
||||
$stateType = 'soft_state';
|
||||
$previousStateType = 'previous_soft_state';
|
||||
|
||||
if ($item->state->previous_soft_state === 0) {
|
||||
$previousStateType = 'hard_state';
|
||||
}
|
||||
|
||||
$visual->addHtml(
|
||||
new CheckAttempt(
|
||||
(int) $item->state->check_attempt,
|
||||
(int) $item->state->max_check_attempts
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$stateType = 'hard_state';
|
||||
$previousStateType = 'previous_hard_state';
|
||||
|
||||
if ($item->state->hard_state === $item->state->previous_hard_state) {
|
||||
$previousStateType = 'previous_soft_state';
|
||||
}
|
||||
}
|
||||
|
||||
if ($item->object_type === 'host') {
|
||||
$state = HostStates::text($item->state->$stateType);
|
||||
$previousState = HostStates::text($item->state->$previousStateType);
|
||||
} else {
|
||||
$state = ServiceStates::text($item->state->$stateType);
|
||||
$previousState = ServiceStates::text($item->state->$previousStateType);
|
||||
}
|
||||
|
||||
$stateChange = new StateChange($state, $previousState);
|
||||
if ($stateType === 'soft_state') {
|
||||
$stateChange->setCurrentStateBallSize(StateBall::SIZE_MEDIUM_LARGE);
|
||||
}
|
||||
|
||||
if ($previousStateType === 'previous_soft_state') {
|
||||
$stateChange->setPreviousStateBallSize(StateBall::SIZE_MEDIUM_LARGE);
|
||||
if ($stateType === 'soft_state') {
|
||||
$visual->getAttributes()->add('class', 'small-state-change');
|
||||
}
|
||||
}
|
||||
|
||||
$visual->prependHtml($stateChange);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function assembleTitle($item, HtmlDocument $title, string $layout): void
|
||||
{
|
||||
if ($item->event_type === 'notification' && isset($item->notification->id)) {
|
||||
$item->notification->history = $item;
|
||||
$item->notification->host = $item->host;
|
||||
$item->notification->service = $item->service;
|
||||
|
||||
$this->notificationRenderer->assembleTitle($item->notification, $title, $layout);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch ($item->event_type) {
|
||||
case 'comment_add':
|
||||
$subjectLabel = $this->translate('Comment added');
|
||||
|
||||
break;
|
||||
case 'comment_remove':
|
||||
if (! empty($item->comment->removed_by)) {
|
||||
if ($item->comment->removed_by !== $item->comment->author) {
|
||||
$subjectLabel = sprintf(
|
||||
$this->translate('Comment removed by %s', '..<username>'),
|
||||
$item->comment->removed_by
|
||||
);
|
||||
} else {
|
||||
$subjectLabel = $this->translate('Comment removed by author');
|
||||
}
|
||||
} elseif (isset($item->comment->expire_time)) {
|
||||
$subjectLabel = $this->translate('Comment expired');
|
||||
} else {
|
||||
$subjectLabel = $this->translate('Comment removed');
|
||||
}
|
||||
|
||||
break;
|
||||
case 'downtime_end':
|
||||
if (! empty($item->downtime->cancelled_by)) {
|
||||
if ($item->downtime->cancelled_by !== $item->downtime->author) {
|
||||
$subjectLabel = sprintf(
|
||||
$this->translate('Downtime cancelled by %s', '..<username>'),
|
||||
$item->downtime->cancelled_by
|
||||
);
|
||||
} else {
|
||||
$subjectLabel = $this->translate('Downtime cancelled by author');
|
||||
}
|
||||
} elseif ($item->downtime->has_been_cancelled === 'y') {
|
||||
$subjectLabel = $this->translate('Downtime cancelled');
|
||||
} else {
|
||||
$subjectLabel = $this->translate('Downtime ended');
|
||||
}
|
||||
|
||||
break;
|
||||
case 'downtime_start':
|
||||
$subjectLabel = $this->translate('Downtime started');
|
||||
|
||||
break;
|
||||
case 'flapping_start':
|
||||
$subjectLabel = $this->translate('Flapping started');
|
||||
|
||||
break;
|
||||
case 'flapping_end':
|
||||
$subjectLabel = $this->translate('Flapping stopped');
|
||||
|
||||
break;
|
||||
case 'ack_set':
|
||||
$subjectLabel = $this->translate('Acknowledgement set');
|
||||
|
||||
break;
|
||||
case 'ack_clear':
|
||||
if (! empty($item->acknowledgement->cleared_by)) {
|
||||
if ($item->acknowledgement->cleared_by !== $item->acknowledgement->author) {
|
||||
$subjectLabel = sprintf(
|
||||
$this->translate('Acknowledgement cleared by %s', '..<username>'),
|
||||
$item->acknowledgement->cleared_by
|
||||
);
|
||||
} else {
|
||||
$subjectLabel = $this->translate('Acknowledgement cleared by author');
|
||||
}
|
||||
} elseif (isset($item->acknowledgement->expire_time)) {
|
||||
$subjectLabel = $this->translate('Acknowledgement expired');
|
||||
} else {
|
||||
$subjectLabel = $this->translate('Acknowledgement cleared');
|
||||
}
|
||||
|
||||
break;
|
||||
case 'state_change':
|
||||
$state = $item->state->state_type === 'hard'
|
||||
? $item->state->hard_state
|
||||
: $item->state->soft_state;
|
||||
if ($state === 0) {
|
||||
if ($item->object_type === 'service') {
|
||||
$subjectLabel = $this->translate('Service recovered');
|
||||
} else {
|
||||
$subjectLabel = $this->translate('Host recovered');
|
||||
}
|
||||
} else {
|
||||
if ($item->state->state_type === 'hard') {
|
||||
$subjectLabel = $this->translate('Hard state changed');
|
||||
} else {
|
||||
$subjectLabel = $this->translate('Soft state changed');
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
$subjectLabel = $item->event_type;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ($layout === 'header') {
|
||||
$title->addHtml(HtmlElement::create('span', ['class' => 'subject'], $subjectLabel));
|
||||
} else {
|
||||
$title->addHtml(new Link($subjectLabel, Links::event($item), ['class' => 'subject']));
|
||||
}
|
||||
|
||||
if ($item->object_type === 'host' && isset($item->host->id)) {
|
||||
$link = $this->createHostLink($item->host, true);
|
||||
} elseif (isset($item->host->id, $item->service->id)) {
|
||||
$link = $this->createServiceLink($item->service, $item->host, true);
|
||||
}
|
||||
|
||||
$title->addHtml(Text::create(' '));
|
||||
if (isset($link)) {
|
||||
$title->addHtml($link);
|
||||
}
|
||||
}
|
||||
|
||||
public function assembleCaption($item, HtmlDocument $caption, string $layout): void
|
||||
{
|
||||
if ($item->event_type === 'notification') {
|
||||
$item->notification->host = $item->host;
|
||||
$item->notification->service = $item->service;
|
||||
|
||||
$this->notificationRenderer->assembleCaption($item->notification, $caption, $layout);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch ($item->event_type) {
|
||||
case 'comment_add':
|
||||
case 'comment_remove':
|
||||
$markdownLine = new MarkdownLine($this->createTicketLinks($item->comment->comment));
|
||||
$caption->getAttributes()->add($markdownLine->getAttributes());
|
||||
$caption->add([
|
||||
new Icon(Icons::USER),
|
||||
$item->comment->author,
|
||||
': '
|
||||
])->addFrom($markdownLine);
|
||||
|
||||
break;
|
||||
case 'downtime_end':
|
||||
case 'downtime_start':
|
||||
$markdownLine = new MarkdownLine($this->createTicketLinks($item->downtime->comment));
|
||||
$caption->getAttributes()->add($markdownLine->getAttributes());
|
||||
$caption->add([
|
||||
new Icon(Icons::USER),
|
||||
$item->downtime->author,
|
||||
': '
|
||||
])->addFrom($markdownLine);
|
||||
|
||||
break;
|
||||
case 'flapping_start':
|
||||
$caption
|
||||
->add(
|
||||
sprintf(
|
||||
$this->translate('State Change Rate: %.2f%%; Start Threshold: %.2f%%'),
|
||||
$item->flapping->percent_state_change_start,
|
||||
$item->flapping->flapping_threshold_high
|
||||
)
|
||||
)
|
||||
->getAttributes()
|
||||
->add('class', 'plugin-output');
|
||||
|
||||
break;
|
||||
case 'flapping_end':
|
||||
$caption
|
||||
->add(
|
||||
sprintf(
|
||||
$this->translate('State Change Rate: %.2f%%; End Threshold: %.2f%%; Flapping for %s'),
|
||||
$item->flapping->percent_state_change_end,
|
||||
$item->flapping->flapping_threshold_low,
|
||||
isset($item->flapping->end_time)
|
||||
? DateFormatter::formatDuration(
|
||||
$item->flapping->end_time->getTimestamp()
|
||||
- $item->flapping->start_time->getTimestamp()
|
||||
)
|
||||
: $this->translate('n. a.')
|
||||
)
|
||||
)
|
||||
->getAttributes()
|
||||
->add('class', 'plugin-output');
|
||||
|
||||
break;
|
||||
case 'ack_clear':
|
||||
case 'ack_set':
|
||||
if (! isset($item->acknowledgement->comment) && ! isset($item->acknowledgement->author)) {
|
||||
$caption->addHtml(
|
||||
new EmptyState(
|
||||
$this->translate('This acknowledgement was set before Icinga DB history recording')
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$markdownLine = new MarkdownLine($this->createTicketLinks($item->acknowledgement->comment));
|
||||
$caption->getAttributes()->add($markdownLine->getAttributes());
|
||||
$caption->add([
|
||||
new Icon(Icons::USER),
|
||||
$item->acknowledgement->author,
|
||||
': '
|
||||
])->addFrom($markdownLine);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'state_change':
|
||||
$commandName = $item->object_type === 'host'
|
||||
? $item->host->checkcommand_name
|
||||
: $item->service->checkcommand_name;
|
||||
if (isset($commandName)) {
|
||||
if (empty($item->state->output)) {
|
||||
$caption->addHtml(new EmptyState($this->translate('Output unavailable.')));
|
||||
} else {
|
||||
$caption->addHtml(
|
||||
new PluginOutputContainer(
|
||||
(new PluginOutput($item->state->output))
|
||||
->setCommandName($commandName)
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$caption->addHtml(
|
||||
new EmptyState($this->translate('Waiting for Icinga DB to synchronize the config.'))
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void
|
||||
{
|
||||
$info->addHtml(new TimeAgo($item->event_time->getTimestamp()));
|
||||
}
|
||||
|
||||
public function assembleFooter($item, HtmlDocument $footer, string $layout): void
|
||||
{
|
||||
}
|
||||
|
||||
public function assemble($item, string $name, HtmlDocument $element, string $layout): bool
|
||||
{
|
||||
return false; // no custom sections
|
||||
}
|
||||
}
|
||||
31
library/Icingadb/View/HostRenderer.php
Normal file
31
library/Icingadb/View/HostRenderer.php
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\View;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Model\Host;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\Html\ValidHtml;
|
||||
use ipl\Web\Widget\Link;
|
||||
|
||||
/** @extends BaseHostAndServiceRenderer<Host> */
|
||||
class HostRenderer extends BaseHostAndServiceRenderer
|
||||
{
|
||||
public function assembleAttributes($item, Attributes $attributes, string $layout): void
|
||||
{
|
||||
$attributes->get('class')->addValue('host');
|
||||
}
|
||||
|
||||
protected function createSubject($item, string $layout): ValidHtml
|
||||
{
|
||||
if ($layout === 'header') {
|
||||
return new HtmlElement('span', new Attributes(['class' => 'subject']), new Text($item->display_name));
|
||||
}
|
||||
|
||||
return new Link($item->display_name, Links::host($item), ['class' => 'subject']);
|
||||
}
|
||||
}
|
||||
169
library/Icingadb/View/HostgroupGridRenderer.php
Normal file
169
library/Icingadb/View/HostgroupGridRenderer.php
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\View;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Model\Hostgroupsummary;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\HtmlDocument;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\I18n\Translation;
|
||||
use ipl\Stdlib\BaseFilter;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Common\ItemRenderer;
|
||||
use ipl\Web\Url;
|
||||
use ipl\Web\Widget\Link;
|
||||
use ipl\Web\Widget\StateBadge;
|
||||
|
||||
/** @implements ItemRenderer<Hostgroupsummary> */
|
||||
class HostgroupGridRenderer implements ItemRenderer
|
||||
{
|
||||
use Translation;
|
||||
use BaseFilter;
|
||||
|
||||
public function assembleAttributes($item, Attributes $attributes, string $layout): void
|
||||
{
|
||||
$attributes->get('class')->addValue(['object-grid-cell', 'hostgroup']);
|
||||
}
|
||||
|
||||
public function assembleVisual($item, HtmlDocument $visual, string $layout): void
|
||||
{
|
||||
$url = Url::fromPath('icingadb/hosts');
|
||||
$urlFilter = Filter::all(Filter::equal('hostgroup.name', $item->name));
|
||||
|
||||
if ($item->hosts_down_unhandled > 0) {
|
||||
$urlFilter->add(Filter::equal('host.state.soft_state', 1))
|
||||
->add(Filter::equal('host.state.is_handled', 'n'))
|
||||
->add(Filter::equal('host.state.is_reachable', 'y'));
|
||||
|
||||
$link = new Link(
|
||||
new StateBadge($item->hosts_down_unhandled, 'down'),
|
||||
$url->setFilter($urlFilter),
|
||||
[
|
||||
'title' => sprintf(
|
||||
$this->translatePlural(
|
||||
'List %d host that is currently in DOWN state in host group "%s"',
|
||||
'List %d hosts which are currently in DOWN state in host group "%s"',
|
||||
$item->hosts_down_unhandled
|
||||
),
|
||||
$item->hosts_down_unhandled,
|
||||
$item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
} elseif ($item->hosts_down_handled > 0) {
|
||||
$urlFilter->add(Filter::equal('host.state.soft_state', 1))
|
||||
->add(Filter::any(
|
||||
Filter::equal('host.state.is_handled', 'y'),
|
||||
Filter::equal('host.state.is_reachable', 'n')
|
||||
));
|
||||
|
||||
$link = new Link(
|
||||
new StateBadge($item->hosts_down_handled, 'down', true),
|
||||
$url->setFilter($urlFilter),
|
||||
[
|
||||
'title' => sprintf(
|
||||
$this->translatePlural(
|
||||
'List %d host that is currently in DOWN (Acknowledged) state in host group "%s"',
|
||||
'List %d hosts which are currently in DOWN (Acknowledged) state in host group "%s"',
|
||||
$item->hosts_down_handled
|
||||
),
|
||||
$item->hosts_down_handled,
|
||||
$item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
} elseif ($item->hosts_pending > 0) {
|
||||
$urlFilter->add(Filter::equal('host.state.soft_state', 99));
|
||||
|
||||
$link = new Link(
|
||||
new StateBadge($item->hosts_pending, 'pending'),
|
||||
$url->setFilter($urlFilter),
|
||||
[
|
||||
'title' => sprintf(
|
||||
$this->translatePlural(
|
||||
'List %d host that is currently in PENDING state in host group "%s"',
|
||||
'List %d hosts which are currently in PENDING state in host group "%s"',
|
||||
$item->hosts_pending
|
||||
),
|
||||
$item->hosts_pending,
|
||||
$item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
} elseif ($item->hosts_up > 0) {
|
||||
$urlFilter->add(Filter::equal('host.state.soft_state', 0));
|
||||
|
||||
$link = new Link(
|
||||
new StateBadge($item->hosts_up, 'up'),
|
||||
$url->setFilter($urlFilter),
|
||||
[
|
||||
'title' => sprintf(
|
||||
$this->translatePlural(
|
||||
'List %d host that is currently in UP state in host group "%s"',
|
||||
'List %d hosts which are currently in UP state in host group "%s"',
|
||||
$item->hosts_up
|
||||
),
|
||||
$item->hosts_up,
|
||||
$item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
} else {
|
||||
$link = new Link(
|
||||
new StateBadge(0, 'none'),
|
||||
Links::hostgroup($item),
|
||||
[
|
||||
'title' => sprintf(
|
||||
$this->translate('There are no hosts in host group "%s"'),
|
||||
$item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$visual->addHtml($link);
|
||||
}
|
||||
|
||||
public function assembleTitle($item, HtmlDocument $title, string $layout): void
|
||||
{
|
||||
$link = new Link(
|
||||
$item->display_name,
|
||||
Links::hostgroup($item),
|
||||
[
|
||||
'class' => 'subject',
|
||||
'title' => sprintf(
|
||||
$this->translate('List all hosts in the group "%s"'),
|
||||
$item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
if ($this->hasBaseFilter()) {
|
||||
$link->getUrl()->setFilter($this->getBaseFilter());
|
||||
}
|
||||
|
||||
$title->addHtml($link);
|
||||
}
|
||||
|
||||
public function assembleCaption($item, HtmlDocument $caption, string $layout): void
|
||||
{
|
||||
$caption->addHtml(Text::create($item->name));
|
||||
}
|
||||
|
||||
public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void
|
||||
{
|
||||
}
|
||||
|
||||
public function assembleFooter($item, HtmlDocument $footer, string $layout): void
|
||||
{
|
||||
}
|
||||
|
||||
public function assemble($item, string $name, HtmlDocument $element, string $layout): bool
|
||||
{
|
||||
return false; // no custom sections
|
||||
}
|
||||
}
|
||||
115
library/Icingadb/View/HostgroupRenderer.php
Normal file
115
library/Icingadb/View/HostgroupRenderer.php
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\View;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Model\Hostgroupsummary;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\HostStatistics;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\ServiceStatistics;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\HtmlDocument;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\I18n\Translation;
|
||||
use ipl\Stdlib\BaseFilter;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Widget\ItemTable\ItemTableRenderer;
|
||||
use ipl\Web\Widget\Link;
|
||||
|
||||
/** @implements ItemTableRenderer<Hostgroupsummary> */
|
||||
class HostgroupRenderer implements ItemTableRenderer
|
||||
{
|
||||
use Translation;
|
||||
use BaseFilter;
|
||||
|
||||
public function assembleAttributes($item, Attributes $attributes, string $layout): void
|
||||
{
|
||||
$attributes->get('class')->addValue('hostgroup');
|
||||
}
|
||||
|
||||
public function assembleVisual($item, HtmlDocument $visual, string $layout): void
|
||||
{
|
||||
}
|
||||
|
||||
public function assembleTitle($item, HtmlDocument $title, string $layout): void
|
||||
{
|
||||
if ($layout === 'header') {
|
||||
$title->addHtml(new HtmlElement(
|
||||
'span',
|
||||
Attributes::create(['class' => 'subject']),
|
||||
Text::create($item->display_name)
|
||||
));
|
||||
} else {
|
||||
$link = new Link(
|
||||
$item->display_name,
|
||||
Links::hostgroup($item),
|
||||
[
|
||||
'class' => 'subject',
|
||||
'title' => sprintf(
|
||||
$this->translate('List all hosts in the group "%s"'),
|
||||
$item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
if ($this->hasBaseFilter()) {
|
||||
$link->getUrl()->setFilter($this->getBaseFilter());
|
||||
}
|
||||
|
||||
$title->addHtml($link);
|
||||
}
|
||||
}
|
||||
|
||||
public function assembleCaption($item, HtmlDocument $caption, string $layout): void
|
||||
{
|
||||
$caption->addHtml(Text::create($item->name));
|
||||
}
|
||||
|
||||
public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void
|
||||
{
|
||||
// assembleExtendedInfo() is only called when $layout == header
|
||||
$info->addHtml(...$this->createStatistics($item));
|
||||
}
|
||||
|
||||
public function assembleFooter($item, HtmlDocument $footer, string $layout): void
|
||||
{
|
||||
}
|
||||
|
||||
public function assemble($item, string $name, HtmlDocument $element, string $layout): bool
|
||||
{
|
||||
return false; // no custom sections
|
||||
}
|
||||
|
||||
public function assembleColumns($item, HtmlDocument $columns, string $layout): void
|
||||
{
|
||||
[$hostStats, $serviceStats] = $this->createStatistics($item);
|
||||
|
||||
if ($this->hasBaseFilter()) {
|
||||
$hostStats->setBaseFilter(Filter::all($hostStats->getBaseFilter(), $this->getBaseFilter()));
|
||||
$serviceStats->setBaseFilter(Filter::all($serviceStats->getBaseFilter(), $this->getBaseFilter()));
|
||||
}
|
||||
|
||||
$columns->addHtml($hostStats, $serviceStats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create statistics for the given item
|
||||
*
|
||||
* @param Hostgroupsummary $item
|
||||
*
|
||||
* @return array{0: HostStatistics, 1: ServiceStatistics}
|
||||
*/
|
||||
protected function createStatistics(Hostgroupsummary $item): array
|
||||
{
|
||||
$hostStats = (new HostStatistics($item))
|
||||
->setBaseFilter(Filter::equal('hostgroup.name', $item->name));
|
||||
|
||||
|
||||
$serviceStats = (new ServiceStatistics($item))
|
||||
->setBaseFilter(Filter::equal('hostgroup.name', $item->name));
|
||||
|
||||
return [$hostStats, $serviceStats];
|
||||
}
|
||||
}
|
||||
200
library/Icingadb/View/NotificationRenderer.php
Normal file
200
library/Icingadb/View/NotificationRenderer.php
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\View;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\HostLink;
|
||||
use Icinga\Module\Icingadb\Common\HostStates;
|
||||
use Icinga\Module\Icingadb\Common\Icons;
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Common\ServiceLink;
|
||||
use Icinga\Module\Icingadb\Common\ServiceStates;
|
||||
use Icinga\Module\Icingadb\Model\NotificationHistory;
|
||||
use Icinga\Module\Icingadb\Util\PluginOutput;
|
||||
use Icinga\Module\Icingadb\Widget\PluginOutputContainer;
|
||||
use Icinga\Module\Icingadb\Widget\StateChange;
|
||||
use InvalidArgumentException;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\HtmlDocument;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\I18n\Translation;
|
||||
use ipl\Web\Common\ItemRenderer;
|
||||
use ipl\Web\Widget\EmptyState;
|
||||
use ipl\Web\Widget\Icon;
|
||||
use ipl\Web\Widget\Link;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
use ipl\Web\Widget\TimeAgo;
|
||||
|
||||
/** @implements ItemRenderer<NotificationHistory> */
|
||||
class NotificationRenderer implements ItemRenderer
|
||||
{
|
||||
use Translation;
|
||||
use HostLink;
|
||||
use ServiceLink;
|
||||
|
||||
public function assembleAttributes($item, Attributes $attributes, string $layout): void
|
||||
{
|
||||
$attributes->get('class')->addValue('notification');
|
||||
}
|
||||
|
||||
public function assembleVisual($item, HtmlDocument $visual, string $layout): void
|
||||
{
|
||||
$ballSize = StateBall::SIZE_LARGE;
|
||||
if ($layout === 'minimal' || $layout === 'header') {
|
||||
$ballSize = StateBall::SIZE_BIG;
|
||||
}
|
||||
|
||||
switch ($item->type) {
|
||||
case 'acknowledgement':
|
||||
$visual->addHtml(HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $ballSize]],
|
||||
new Icon(Icons::IS_ACKNOWLEDGED)
|
||||
));
|
||||
|
||||
break;
|
||||
case 'custom':
|
||||
$visual->addHtml(HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $ballSize]],
|
||||
new Icon(Icons::NOTIFICATION)
|
||||
));
|
||||
|
||||
break;
|
||||
case 'downtime_end':
|
||||
case 'downtime_removed':
|
||||
case 'downtime_start':
|
||||
$visual->addHtml(HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $ballSize]],
|
||||
new Icon(Icons::IN_DOWNTIME)
|
||||
));
|
||||
|
||||
break;
|
||||
case 'flapping_end':
|
||||
case 'flapping_start':
|
||||
$visual->addHtml(HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $ballSize]],
|
||||
new Icon(Icons::IS_FLAPPING)
|
||||
));
|
||||
|
||||
break;
|
||||
case 'problem':
|
||||
case 'recovery':
|
||||
if ($item->object_type === 'host') {
|
||||
$state = HostStates::text($item->state);
|
||||
$previousHardState = HostStates::text($item->previous_hard_state);
|
||||
} else {
|
||||
$state = ServiceStates::text($item->state);
|
||||
$previousHardState = ServiceStates::text($item->previous_hard_state);
|
||||
}
|
||||
|
||||
$visual->addHtml(new StateChange($state, $previousHardState));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function assembleTitle($item, HtmlDocument $title, string $layout): void
|
||||
{
|
||||
if ($layout === 'header') {
|
||||
$title->addHtml(HtmlElement::create(
|
||||
'span',
|
||||
['class' => 'subject'],
|
||||
sprintf($this->phraseForType($item->type), ucfirst($item->object_type))
|
||||
));
|
||||
} else {
|
||||
$title->addHtml(new Link(
|
||||
sprintf($this->phraseForType($item->type), ucfirst($item->object_type)),
|
||||
Links::event($item->history),
|
||||
['class' => 'subject']
|
||||
));
|
||||
}
|
||||
|
||||
if ($item->object_type === 'host') {
|
||||
$link = $this->createHostLink($item->host, true);
|
||||
} else {
|
||||
$link = $this->createServiceLink($item->service, $item->host, true);
|
||||
}
|
||||
|
||||
$title->addHtml(Text::create(' '), $link);
|
||||
}
|
||||
|
||||
public function assembleCaption($item, HtmlDocument $caption, string $layout): void
|
||||
{
|
||||
if (in_array($item->type, ['flapping_end', 'flapping_start', 'problem', 'recovery'])) {
|
||||
$commandName = $item->object_type === 'host'
|
||||
? $item->host->checkcommand_name
|
||||
: $item->service->checkcommand_name;
|
||||
if (isset($commandName)) {
|
||||
if (empty($item->text)) {
|
||||
$caption->addHtml(new EmptyState($this->translate('Output unavailable.')));
|
||||
} else {
|
||||
$caption->addHtml(new PluginOutputContainer(
|
||||
(new PluginOutput($item->text))
|
||||
->setCommandName($commandName)
|
||||
));
|
||||
}
|
||||
} else {
|
||||
$caption->addHtml(new EmptyState($this->translate('Waiting for Icinga DB to synchronize the config.')));
|
||||
}
|
||||
} else {
|
||||
$caption->add([
|
||||
new Icon(Icons::USER),
|
||||
$item->author,
|
||||
': ',
|
||||
$item->text
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void
|
||||
{
|
||||
$info->addHtml(new TimeAgo($item->send_time->getTimestamp()));
|
||||
}
|
||||
|
||||
public function assembleFooter($item, HtmlDocument $footer, string $layout): void
|
||||
{
|
||||
}
|
||||
|
||||
public function assemble($item, string $name, HtmlDocument $element, string $layout): bool
|
||||
{
|
||||
return false; // no custom sections
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a localized phrase for the given notification type
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function phraseForType(string $type): string
|
||||
{
|
||||
switch ($type) {
|
||||
case 'acknowledgement':
|
||||
return $this->translate('Problem acknowledged');
|
||||
case 'custom':
|
||||
return $this->translate('Custom Notification triggered');
|
||||
case 'downtime_end':
|
||||
return $this->translate('Downtime ended');
|
||||
case 'downtime_removed':
|
||||
return $this->translate('Downtime removed');
|
||||
case 'downtime_start':
|
||||
return $this->translate('Downtime started');
|
||||
case 'flapping_end':
|
||||
return $this->translate('Flapping stopped');
|
||||
case 'flapping_start':
|
||||
return $this->translate('Flapping started');
|
||||
case 'problem':
|
||||
return $this->translate('%s ran into a problem');
|
||||
case 'recovery':
|
||||
return $this->translate('%s recovered');
|
||||
default:
|
||||
throw new InvalidArgumentException(sprintf('Type %s is not a valid notification type', $type));
|
||||
}
|
||||
}
|
||||
}
|
||||
91
library/Icingadb/View/RedundancyGroupRenderer.php
Normal file
91
library/Icingadb/View/RedundancyGroupRenderer.php
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\View;
|
||||
|
||||
use Icinga\Module\Icingadb\Model\RedundancyGroup;
|
||||
use Icinga\Module\Icingadb\Widget\DependencyNodeStatistics;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\Html;
|
||||
use ipl\Html\HtmlDocument;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\I18n\Translation;
|
||||
use ipl\Web\Common\ItemRenderer;
|
||||
use ipl\Web\Url;
|
||||
use ipl\Web\Widget\Link;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
use ipl\Web\Widget\TimeSince;
|
||||
|
||||
/** @implements ItemRenderer<RedundancyGroup> */
|
||||
class RedundancyGroupRenderer implements ItemRenderer
|
||||
{
|
||||
use Translation;
|
||||
|
||||
public function assembleAttributes($item, Attributes $attributes, string $layout): void
|
||||
{
|
||||
$attributes->get('class')->addValue('redundancy-group');
|
||||
}
|
||||
|
||||
public function assembleVisual($item, HtmlDocument $visual, string $layout): void
|
||||
{
|
||||
$ballSize = StateBall::SIZE_LARGE;
|
||||
if ($layout === 'minimal' || $layout === 'header') {
|
||||
$ballSize = StateBall::SIZE_BIG;
|
||||
}
|
||||
|
||||
$stateBall = new StateBall($item->state->getStateText(), $ballSize);
|
||||
$stateBall->add($item->state->getIcon());
|
||||
|
||||
$visual->addHtml($stateBall);
|
||||
}
|
||||
|
||||
public function assembleCaption($item, HtmlDocument $caption, string $layout): void
|
||||
{
|
||||
$caption->addHtml(new DependencyNodeStatistics($item->summary));
|
||||
}
|
||||
|
||||
public function assembleFooter($item, HtmlDocument $footer, string $layout): void
|
||||
{
|
||||
}
|
||||
|
||||
public function assembleTitle($item, HtmlDocument $title, string $layout): void
|
||||
{
|
||||
if ($layout === 'header') {
|
||||
$subject = new HtmlElement(
|
||||
'span',
|
||||
Attributes::create(['class' => 'subject']),
|
||||
Text::create($item->display_name)
|
||||
);
|
||||
} else {
|
||||
$subject = new Link(
|
||||
$item->display_name,
|
||||
Url::fromPath('icingadb/redundancygroup', ['id' => bin2hex($item->id)]),
|
||||
['class' => 'subject']
|
||||
);
|
||||
}
|
||||
|
||||
if ($item->state->failed) {
|
||||
$title->addHtml(Html::sprintf(
|
||||
$this->translate('%s has no working objects', '<groupname> has ...'),
|
||||
$subject
|
||||
));
|
||||
} else {
|
||||
$title->addHtml(Html::sprintf(
|
||||
$this->translate('%s has working objects', '<groupname> has ...'),
|
||||
$subject
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void
|
||||
{
|
||||
$info->addHtml(new TimeSince($item->state->last_state_change->getTimestamp()));
|
||||
}
|
||||
|
||||
public function assemble($item, string $name, HtmlDocument $element, string $layout): bool
|
||||
{
|
||||
return $name === 'icon-image'; // Always add the icon-image section
|
||||
}
|
||||
}
|
||||
43
library/Icingadb/View/ServiceRenderer.php
Normal file
43
library/Icingadb/View/ServiceRenderer.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\View;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Model\Service;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\Html;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\Html\ValidHtml;
|
||||
use ipl\Web\Widget\Link;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
/** @extends BaseHostAndServiceRenderer<Service> */
|
||||
class ServiceRenderer extends BaseHostAndServiceRenderer
|
||||
{
|
||||
public function assembleAttributes($item, Attributes $attributes, string $layout): void
|
||||
{
|
||||
$attributes->get('class')->addValue('service');
|
||||
}
|
||||
|
||||
protected function createSubject($item, string $layout): ValidHtml
|
||||
{
|
||||
$service = $item->display_name;
|
||||
$host = [
|
||||
new StateBall($item->host->state->getStateText(), StateBall::SIZE_MEDIUM),
|
||||
' ',
|
||||
$item->host->display_name
|
||||
];
|
||||
|
||||
$host = new Link($host, Links::host($item->host), ['class' => 'subject']);
|
||||
if ($layout === 'header') {
|
||||
$service = new HtmlElement('span', new Attributes(['class' => 'subject']), new Text($service));
|
||||
} else {
|
||||
$service = new Link($service, Links::service($item, $item->host), ['class' => 'subject']);
|
||||
}
|
||||
|
||||
return Html::sprintf($this->translate('%s on %s', '<service> on <host>'), $service, $host);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +1,68 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2023 Icinga GmbH | GPLv2 */
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemTable;
|
||||
namespace Icinga\Module\Icingadb\View;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Model\ServicegroupSummary;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\HtmlDocument;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\I18n\Translation;
|
||||
use ipl\Stdlib\BaseFilter;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Common\ItemRenderer;
|
||||
use ipl\Web\Url;
|
||||
use ipl\Web\Widget\Link;
|
||||
use ipl\Web\Widget\StateBadge;
|
||||
|
||||
class ServicegroupGridCell extends BaseServiceGroupItem
|
||||
/** @implements ItemRenderer<ServicegroupSummary> */
|
||||
class ServicegroupGridRenderer implements ItemRenderer
|
||||
{
|
||||
use GridCellLayout;
|
||||
use Translation;
|
||||
use BaseFilter;
|
||||
|
||||
protected $defaultAttributes = ['class' => ['group-grid-cell', 'servicegroup-grid-cell']];
|
||||
public function assembleAttributes($item, Attributes $attributes, string $layout): void
|
||||
{
|
||||
$attributes->get('class')->addValue(['object-grid-cell', 'servicegroup']);
|
||||
}
|
||||
|
||||
protected function createGroupBadge(): Link
|
||||
public function assembleVisual($item, HtmlDocument $visual, string $layout): void
|
||||
{
|
||||
$url = Url::fromPath('icingadb/services/grid');
|
||||
$urlFilter = Filter::all(Filter::equal('servicegroup.name', $this->item->name));
|
||||
$urlFilter = Filter::all(Filter::equal('servicegroup.name', $item->name));
|
||||
|
||||
if ($this->item->services_critical_unhandled > 0) {
|
||||
if ($item->services_critical_unhandled > 0) {
|
||||
$urlFilter->add(Filter::equal('service.state.soft_state', 2))
|
||||
->add(Filter::equal('service.state.is_handled', 'n'))
|
||||
->add(Filter::equal('service.state.is_reachable', 'y'));
|
||||
|
||||
return new Link(
|
||||
new StateBadge($this->item->services_critical_unhandled, 'critical'),
|
||||
$link = new Link(
|
||||
new StateBadge($item->services_critical_unhandled, 'critical'),
|
||||
$url->setFilter($urlFilter),
|
||||
[
|
||||
'title' => sprintf(
|
||||
$this->translatePlural(
|
||||
'List %d service that is currently in CRITICAL state in service group "%s"',
|
||||
'List %d services which are currently in CRITICAL state in service group "%s"',
|
||||
$this->item->services_critical_unhandled
|
||||
$item->services_critical_unhandled
|
||||
),
|
||||
$this->item->services_critical_unhandled,
|
||||
$this->item->display_name
|
||||
$item->services_critical_unhandled,
|
||||
$item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
} elseif ($this->item->services_critical_handled > 0) {
|
||||
} elseif ($item->services_critical_handled > 0) {
|
||||
$urlFilter->add(Filter::equal('service.state.soft_state', 2))
|
||||
->add(Filter::any(
|
||||
Filter::equal('service.state.is_handled', 'y'),
|
||||
Filter::equal('service.state.is_reachable', 'n')
|
||||
));
|
||||
|
||||
return new Link(
|
||||
new StateBadge($this->item->services_critical_handled, 'critical', true),
|
||||
$link = new Link(
|
||||
new StateBadge($item->services_critical_handled, 'critical', true),
|
||||
$url->setFilter($urlFilter),
|
||||
[
|
||||
'title' => sprintf(
|
||||
|
|
@ -57,42 +71,42 @@ class ServicegroupGridCell extends BaseServiceGroupItem
|
|||
. ' "%s"',
|
||||
'List %d services which are currently in CRITICAL (Acknowledged) state in service group'
|
||||
. ' "%s"',
|
||||
$this->item->services_critical_handled
|
||||
$item->services_critical_handled
|
||||
),
|
||||
$this->item->services_critical_handled,
|
||||
$this->item->display_name
|
||||
$item->services_critical_handled,
|
||||
$item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
} elseif ($this->item->services_warning_unhandled > 0) {
|
||||
} elseif ($item->services_warning_unhandled > 0) {
|
||||
$urlFilter->add(Filter::equal('service.state.soft_state', 1))
|
||||
->add(Filter::equal('service.state.is_handled', 'n'))
|
||||
->add(Filter::equal('service.state.is_reachable', 'y'));
|
||||
|
||||
return new Link(
|
||||
new StateBadge($this->item->services_warning_unhandled, 'warning'),
|
||||
$link = new Link(
|
||||
new StateBadge($item->services_warning_unhandled, 'warning'),
|
||||
$url->setFilter($urlFilter),
|
||||
[
|
||||
'title' => sprintf(
|
||||
$this->translatePlural(
|
||||
'List %d service that is currently in WARNING state in service group "%s"',
|
||||
'List %d services which are currently in WARNING state in service group "%s"',
|
||||
$this->item->services_warning_unhandled
|
||||
$item->services_warning_unhandled
|
||||
),
|
||||
$this->item->services_warning_unhandled,
|
||||
$this->item->display_name
|
||||
$item->services_warning_unhandled,
|
||||
$item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
} elseif ($this->item->services_warning_handled > 0) {
|
||||
} elseif ($item->services_warning_handled > 0) {
|
||||
$urlFilter->add(Filter::equal('service.state.soft_state', 1))
|
||||
->add(Filter::any(
|
||||
Filter::equal('service.state.is_handled', 'y'),
|
||||
Filter::equal('service.state.is_reachable', 'n')
|
||||
));
|
||||
|
||||
return new Link(
|
||||
new StateBadge($this->item->services_warning_handled, 'warning', true),
|
||||
$link = new Link(
|
||||
new StateBadge($item->services_warning_handled, 'warning', true),
|
||||
$url->setFilter($urlFilter),
|
||||
[
|
||||
'title' => sprintf(
|
||||
|
|
@ -101,42 +115,42 @@ class ServicegroupGridCell extends BaseServiceGroupItem
|
|||
. ' "%s"',
|
||||
'List %d services which are currently in WARNING (Acknowledged) state in service group'
|
||||
. ' "%s"',
|
||||
$this->item->services_warning_handled
|
||||
$item->services_warning_handled
|
||||
),
|
||||
$this->item->services_warning_handled,
|
||||
$this->item->display_name
|
||||
$item->services_warning_handled,
|
||||
$item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
} elseif ($this->item->services_unknown_unhandled > 0) {
|
||||
} elseif ($item->services_unknown_unhandled > 0) {
|
||||
$urlFilter->add(Filter::equal('service.state.soft_state', 3))
|
||||
->add(Filter::equal('service.state.is_handled', 'n'))
|
||||
->add(Filter::equal('service.state.is_reachable', 'y'));
|
||||
|
||||
return new Link(
|
||||
new StateBadge($this->item->services_unknown_unhandled, 'unknown'),
|
||||
$link = new Link(
|
||||
new StateBadge($item->services_unknown_unhandled, 'unknown'),
|
||||
$url->setFilter($urlFilter),
|
||||
[
|
||||
'title' => sprintf(
|
||||
$this->translatePlural(
|
||||
'List %d service that is currently in UNKNOWN state in service group "%s"',
|
||||
'List %d services which are currently in UNKNOWN state in service group "%s"',
|
||||
$this->item->services_unknown_unhandled
|
||||
$item->services_unknown_unhandled
|
||||
),
|
||||
$this->item->services_unknown_unhandled,
|
||||
$this->item->display_name
|
||||
$item->services_unknown_unhandled,
|
||||
$item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
} elseif ($this->item->services_unknown_handled > 0) {
|
||||
} elseif ($item->services_unknown_handled > 0) {
|
||||
$urlFilter->add(Filter::equal('service.state.soft_state', 3))
|
||||
->add(Filter::any(
|
||||
Filter::equal('service.state.is_handled', 'y'),
|
||||
Filter::equal('service.state.is_reachable', 'n')
|
||||
));
|
||||
|
||||
return new Link(
|
||||
new StateBadge($this->item->services_unknown_handled, 'unknown', true),
|
||||
$link = new Link(
|
||||
new StateBadge($item->services_unknown_handled, 'unknown', true),
|
||||
$url->setFilter($urlFilter),
|
||||
[
|
||||
'title' => sprintf(
|
||||
|
|
@ -145,60 +159,101 @@ class ServicegroupGridCell extends BaseServiceGroupItem
|
|||
. ' "%s"',
|
||||
'List %d services which are currently in UNKNOWN (Acknowledged) state in service group'
|
||||
. ' "%s"',
|
||||
$this->item->services_unknown_handled
|
||||
$item->services_unknown_handled
|
||||
),
|
||||
$this->item->services_unknown_handled,
|
||||
$this->item->display_name
|
||||
$item->services_unknown_handled,
|
||||
$item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
} elseif ($this->item->services_pending > 0) {
|
||||
} elseif ($item->services_pending > 0) {
|
||||
$urlFilter->add(Filter::equal('service.state.soft_state', 99));
|
||||
|
||||
return new Link(
|
||||
new StateBadge($this->item->services_pending, 'pending'),
|
||||
$link = new Link(
|
||||
new StateBadge($item->services_pending, 'pending'),
|
||||
$url->setFilter($urlFilter),
|
||||
[
|
||||
'title' => sprintf(
|
||||
$this->translatePlural(
|
||||
'List %d service that is currently in PENDING state in service group "%s"',
|
||||
'List %d services which are currently in PENDING state in service group "%s"',
|
||||
$this->item->services_pending
|
||||
$item->services_pending
|
||||
),
|
||||
$this->item->services_pending,
|
||||
$this->item->display_name
|
||||
$item->services_pending,
|
||||
$item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
} elseif ($this->item->services_ok > 0) {
|
||||
} elseif ($item->services_ok > 0) {
|
||||
$urlFilter->add(Filter::equal('service.state.soft_state', 0));
|
||||
|
||||
return new Link(
|
||||
new StateBadge($this->item->services_ok, 'ok'),
|
||||
$link = new Link(
|
||||
new StateBadge($item->services_ok, 'ok'),
|
||||
$url->setFilter($urlFilter),
|
||||
[
|
||||
'title' => sprintf(
|
||||
$this->translatePlural(
|
||||
'List %d service that is currently in OK state in service group "%s"',
|
||||
'List %d services which are currently in OK state in service group "%s"',
|
||||
$this->item->services_ok
|
||||
$item->services_ok
|
||||
),
|
||||
$this->item->services_ok,
|
||||
$this->item->display_name
|
||||
$item->services_ok,
|
||||
$item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
} else {
|
||||
$link = new Link(
|
||||
new StateBadge(0, 'none'),
|
||||
Links::servicegroup($item),
|
||||
[
|
||||
'title' => sprintf(
|
||||
$this->translate('There are no services in service group "%s"'),
|
||||
$item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return new Link(
|
||||
new StateBadge(0, 'none'),
|
||||
$url,
|
||||
$visual->addHtml($link);
|
||||
}
|
||||
|
||||
public function assembleTitle($item, HtmlDocument $title, string $layout): void
|
||||
{
|
||||
$link = new Link(
|
||||
$item->display_name,
|
||||
Links::servicegroup($item),
|
||||
[
|
||||
'class' => 'subject',
|
||||
'title' => sprintf(
|
||||
$this->translate('There are no services in service group "%s"'),
|
||||
$this->item->display_name
|
||||
$this->translate('List all services in the group "%s"'),
|
||||
$item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
if ($this->hasBaseFilter()) {
|
||||
$link->getUrl()->setFilter($this->getBaseFilter());
|
||||
}
|
||||
|
||||
$title->addHtml($link);
|
||||
}
|
||||
|
||||
public function assembleCaption($item, HtmlDocument $caption, string $layout): void
|
||||
{
|
||||
$caption->addHtml(Text::create($item->name));
|
||||
}
|
||||
|
||||
public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void
|
||||
{
|
||||
}
|
||||
|
||||
public function assembleFooter($item, HtmlDocument $footer, string $layout): void
|
||||
{
|
||||
}
|
||||
|
||||
public function assemble($item, string $name, HtmlDocument $element, string $layout): bool
|
||||
{
|
||||
return false; // no custom sections
|
||||
}
|
||||
}
|
||||
107
library/Icingadb/View/ServicegroupRenderer.php
Normal file
107
library/Icingadb/View/ServicegroupRenderer.php
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\View;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Model\ServicegroupSummary;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\ServiceStatistics;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\HtmlDocument;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\I18n\Translation;
|
||||
use ipl\Stdlib\BaseFilter;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Widget\ItemTable\ItemTableRenderer;
|
||||
use ipl\Web\Widget\Link;
|
||||
|
||||
/** @implements ItemTableRenderer<ServicegroupSummary> */
|
||||
class ServicegroupRenderer implements ItemTableRenderer
|
||||
{
|
||||
use Translation;
|
||||
use BaseFilter;
|
||||
|
||||
public function assembleAttributes($item, Attributes $attributes, string $layout): void
|
||||
{
|
||||
$attributes->get('class')->addValue('servicegroup');
|
||||
}
|
||||
|
||||
public function assembleVisual($item, HtmlDocument $visual, string $layout): void
|
||||
{
|
||||
}
|
||||
|
||||
public function assembleTitle($item, HtmlDocument $title, string $layout): void
|
||||
{
|
||||
if ($layout === 'header') {
|
||||
$title->addHtml(new HtmlElement(
|
||||
'span',
|
||||
Attributes::create(['class' => 'subject']),
|
||||
Text::create($item->display_name)
|
||||
));
|
||||
} else {
|
||||
$link = new Link(
|
||||
$item->display_name,
|
||||
Links::servicegroup($item),
|
||||
[
|
||||
'class' => 'subject',
|
||||
'title' => sprintf(
|
||||
$this->translate('List all services in the group "%s"'),
|
||||
$item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
if ($this->hasBaseFilter()) {
|
||||
$link->getUrl()->setFilter($this->getBaseFilter());
|
||||
}
|
||||
|
||||
$title->addHtml($link);
|
||||
}
|
||||
}
|
||||
|
||||
public function assembleCaption($item, HtmlDocument $caption, string $layout): void
|
||||
{
|
||||
$caption->addHtml(Text::create($item->name));
|
||||
}
|
||||
|
||||
public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void
|
||||
{
|
||||
// assembleExtendedInfo() is only called when $layout == header
|
||||
$info->addHtml($this->createStatistics($item));
|
||||
}
|
||||
|
||||
public function assembleFooter($item, HtmlDocument $footer, string $layout): void
|
||||
{
|
||||
}
|
||||
|
||||
public function assemble($item, string $name, HtmlDocument $element, string $layout): bool
|
||||
{
|
||||
return false; // no custom sections
|
||||
}
|
||||
|
||||
public function assembleColumns($item, HtmlDocument $columns, string $layout): void
|
||||
{
|
||||
$serviceStats = $this->createStatistics($item);
|
||||
|
||||
if ($this->hasBaseFilter()) {
|
||||
$serviceStats->setBaseFilter(Filter::all($serviceStats->getBaseFilter(), $this->getBaseFilter()));
|
||||
}
|
||||
|
||||
$columns->addHtml($serviceStats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create statistics for the given item
|
||||
*
|
||||
* @param ServicegroupSummary $item
|
||||
*
|
||||
* @return ServiceStatistics
|
||||
*/
|
||||
protected function createStatistics(ServicegroupSummary $item): ServiceStatistics
|
||||
{
|
||||
return (new ServiceStatistics($item))
|
||||
->setBaseFilter(Filter::equal('servicegroup.name', $item->name));
|
||||
}
|
||||
}
|
||||
63
library/Icingadb/View/UserRenderer.php
Normal file
63
library/Icingadb/View/UserRenderer.php
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\View;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Model\User;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\HtmlDocument;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\Web\Common\ItemRenderer;
|
||||
use ipl\Web\Widget\Link;
|
||||
|
||||
/** @implements ItemRenderer<User> */
|
||||
class UserRenderer implements ItemRenderer
|
||||
{
|
||||
public function assembleAttributes($item, Attributes $attributes, string $layout): void
|
||||
{
|
||||
$attributes->get('class')->addValue('user');
|
||||
}
|
||||
|
||||
public function assembleVisual($item, HtmlDocument $visual, string $layout): void
|
||||
{
|
||||
$visual->addHtml(new HtmlElement(
|
||||
'div',
|
||||
Attributes::create(['class' => 'user-ball']),
|
||||
Text::create($item->display_name[0])
|
||||
));
|
||||
}
|
||||
|
||||
public function assembleTitle($item, HtmlDocument $title, string $layout): void
|
||||
{
|
||||
if ($layout === 'header') {
|
||||
$title->addHtml(new HtmlElement(
|
||||
'span',
|
||||
Attributes::create(['class' => 'subject']),
|
||||
Text::create($item->display_name)
|
||||
));
|
||||
} else {
|
||||
$title->addHtml(new Link($item->display_name, Links::user($item), ['class' => 'subject']));
|
||||
}
|
||||
}
|
||||
|
||||
public function assembleCaption($item, HtmlDocument $caption, string $layout): void
|
||||
{
|
||||
$caption->addHtml(Text::create($item->name));
|
||||
}
|
||||
|
||||
public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void
|
||||
{
|
||||
}
|
||||
|
||||
public function assembleFooter($item, HtmlDocument $footer, string $layout): void
|
||||
{
|
||||
}
|
||||
|
||||
public function assemble($item, string $name, HtmlDocument $element, string $layout): bool
|
||||
{
|
||||
return false; // no custom sections
|
||||
}
|
||||
}
|
||||
63
library/Icingadb/View/UsergroupRenderer.php
Normal file
63
library/Icingadb/View/UsergroupRenderer.php
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\View;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Model\Usergroup;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\HtmlDocument;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\Web\Common\ItemRenderer;
|
||||
use ipl\Web\Widget\Link;
|
||||
|
||||
/** @implements ItemRenderer<Usergroup> */
|
||||
class UsergroupRenderer implements ItemRenderer
|
||||
{
|
||||
public function assembleAttributes($item, Attributes $attributes, string $layout): void
|
||||
{
|
||||
$attributes->get('class')->addValue('user-group');
|
||||
}
|
||||
|
||||
public function assembleVisual($item, HtmlDocument $visual, string $layout): void
|
||||
{
|
||||
$visual->addHtml(new HtmlElement(
|
||||
'div',
|
||||
Attributes::create(['class' => 'usergroup-ball']),
|
||||
Text::create($item->display_name[0])
|
||||
));
|
||||
}
|
||||
|
||||
public function assembleTitle($item, HtmlDocument $title, string $layout): void
|
||||
{
|
||||
if ($layout === 'header') {
|
||||
$title->addHtml(new HtmlElement(
|
||||
'span',
|
||||
Attributes::create(['class' => 'subject']),
|
||||
Text::create($item->display_name)
|
||||
));
|
||||
} else {
|
||||
$title->addHtml(new Link($item->display_name, Links::usergroup($item), ['class' => 'subject']));
|
||||
}
|
||||
}
|
||||
|
||||
public function assembleCaption($item, HtmlDocument $caption, string $layout): void
|
||||
{
|
||||
$caption->addHtml(Text::create($item->name));
|
||||
}
|
||||
|
||||
public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void
|
||||
{
|
||||
}
|
||||
|
||||
public function assembleFooter($item, HtmlDocument $footer, string $layout): void
|
||||
{
|
||||
}
|
||||
|
||||
public function assemble($item, string $name, HtmlDocument $element, string $layout): bool
|
||||
{
|
||||
return false; // no custom sections
|
||||
}
|
||||
}
|
||||
|
|
@ -37,8 +37,6 @@ use ipl\Html\ValidHtml;
|
|||
use ipl\Orm\Query;
|
||||
use ipl\Orm\UnionQuery;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Common\BaseItemList;
|
||||
use ipl\Web\Common\BaseItemTable;
|
||||
use ipl\Web\Compat\CompatController;
|
||||
use ipl\Web\Control\LimitControl;
|
||||
use ipl\Web\Control\PaginationControl;
|
||||
|
|
@ -486,9 +484,7 @@ class Controller extends CompatController
|
|||
|
||||
protected function addContent(ValidHtml $content)
|
||||
{
|
||||
if ($content instanceof BaseItemList || $content instanceof BaseItemTable) {
|
||||
$this->content->getAttributes()->add('class', 'full-width');
|
||||
} elseif ($content instanceof StateItemTable) {
|
||||
if ($content instanceof StateItemTable) {
|
||||
$this->content->getAttributes()->add('class', 'full-height');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,12 +10,13 @@ use Icinga\Module\Icingadb\Common\Auth;
|
|||
use Icinga\Module\Icingadb\Common\Database;
|
||||
use Icinga\Module\Icingadb\Common\HostLink;
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ObjectList;
|
||||
use Icinga\Module\Icingadb\Widget\MarkdownText;
|
||||
use Icinga\Module\Icingadb\Common\ServiceLink;
|
||||
use Icinga\Module\Icingadb\Forms\Command\Object\DeleteDowntimeForm;
|
||||
use Icinga\Module\Icingadb\Model\Downtime;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\DowntimeList;
|
||||
use Icinga\Module\Icingadb\Widget\ShowMore;
|
||||
use ipl\Web\Url;
|
||||
use ipl\Web\Widget\EmptyState;
|
||||
use ipl\Web\Widget\HorizontalKeyValue;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
|
|
@ -180,7 +181,7 @@ class DowntimeDetail extends BaseHtmlElement
|
|||
if ($children->hasResult()) {
|
||||
$this->addHtml(
|
||||
new HtmlElement('h2', null, Text::create(t('Children'))),
|
||||
new DowntimeList($children),
|
||||
new ObjectList($children),
|
||||
(new ShowMore($children, Links::downtimes()->setQueryString(
|
||||
QueryString::render(Filter::any(
|
||||
Filter::equal('downtime.parent.name', $this->downtime->name),
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use Icinga\Module\Icingadb\Common\HostStates;
|
|||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Common\TicketLinks;
|
||||
use Icinga\Module\Icingadb\Hook\ExtensionHook\ObjectDetailExtensionHook;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ObjectList;
|
||||
use Icinga\Module\Icingadb\Widget\MarkdownText;
|
||||
use Icinga\Module\Icingadb\Common\ServiceLink;
|
||||
use Icinga\Module\Icingadb\Common\ServiceStates;
|
||||
|
|
@ -27,10 +28,10 @@ use Icinga\Module\Icingadb\Model\NotificationHistory;
|
|||
use Icinga\Module\Icingadb\Model\StateHistory;
|
||||
use Icinga\Module\Icingadb\Util\PluginOutput;
|
||||
use Icinga\Module\Icingadb\Widget\ShowMore;
|
||||
use ipl\Web\Url;
|
||||
use ipl\Web\Widget\CopyToClipboard;
|
||||
use ipl\Web\Widget\EmptyState;
|
||||
use ipl\Web\Widget\HorizontalKeyValue;
|
||||
use Icinga\Module\Icingadb\Widget\ItemTable\UserTable;
|
||||
use Icinga\Module\Icingadb\Widget\PluginOutputContainer;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\FormattedString;
|
||||
|
|
@ -169,7 +170,7 @@ class EventDetail extends BaseHtmlElement
|
|||
$users = $users->execute();
|
||||
/** @var ResultSet $users */
|
||||
|
||||
$notifiedUsers[] = new UserTable($users);
|
||||
$notifiedUsers[] = new ObjectList($users);
|
||||
$notifiedUsers[] = (new ShowMore(
|
||||
$users,
|
||||
Links::users()->addParams(['notification_history.id' => bin2hex($notification->id)]),
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ use Icinga\Module\Icingadb\Model\Service;
|
|||
use Icinga\Module\Icingadb\Model\UnreachableParent;
|
||||
use Icinga\Module\Icingadb\Redis\VolatileStateResults;
|
||||
use Icinga\Module\Icingadb\Web\Navigation\Action;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\DependencyNodeList;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ObjectList;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\TicketLinkObjectList;
|
||||
use Icinga\Module\Icingadb\Widget\MarkdownText;
|
||||
use Icinga\Module\Icingadb\Common\ServiceLinks;
|
||||
use Icinga\Module\Icingadb\Forms\Command\Object\ToggleObjectFeaturesForm;
|
||||
|
|
@ -42,11 +43,11 @@ use Icinga\Module\Icingadb\Widget\ItemList\DowntimeList;
|
|||
use Icinga\Module\Icingadb\Widget\ShowMore;
|
||||
use ipl\Sql\Expression;
|
||||
use ipl\Sql\Filter\Exists;
|
||||
use ipl\Web\Url;
|
||||
use ipl\Web\Widget\CopyToClipboard;
|
||||
use ipl\Web\Widget\EmptyState;
|
||||
use ipl\Web\Widget\EmptyStateBar;
|
||||
use ipl\Web\Widget\HorizontalKeyValue;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\CommentList;
|
||||
use Icinga\Module\Icingadb\Widget\PluginOutputContainer;
|
||||
use Icinga\Module\Icingadb\Widget\TagList;
|
||||
use Icinga\Module\Monitoring\Hook\DetailviewExtensionHook;
|
||||
|
|
@ -231,7 +232,7 @@ class ObjectDetail extends BaseHtmlElement
|
|||
$content = [Html::tag('h2', t('Comments'))];
|
||||
|
||||
if ($comments->hasResult()) {
|
||||
$content[] = (new CommentList($comments))->setObjectLinkDisabled()->setTicketLinkEnabled();
|
||||
$content[] = new TicketLinkObjectList($comments);
|
||||
$content[] = (new ShowMore($comments, $link))->setBaseTarget('_next');
|
||||
} else {
|
||||
$content[] = new EmptyState(t('No comments created.'));
|
||||
|
|
@ -282,7 +283,7 @@ class ObjectDetail extends BaseHtmlElement
|
|||
$content = [Html::tag('h2', t('Downtimes'))];
|
||||
|
||||
if ($downtimes->hasResult()) {
|
||||
$content[] = (new DowntimeList($downtimes))->setObjectLinkDisabled()->setTicketLinkEnabled();
|
||||
$content[] = new TicketLinkObjectList($downtimes);
|
||||
$content[] = (new ShowMore($downtimes, $link))->setBaseTarget('_next');
|
||||
} else {
|
||||
$content[] = new EmptyState(t('No downtimes scheduled.'));
|
||||
|
|
@ -668,7 +669,7 @@ class ObjectDetail extends BaseHtmlElement
|
|||
|
||||
return [
|
||||
HtmlElement::create('h2', null, Text::create(t('Root Problems'))),
|
||||
(new DependencyNodeList($rootProblems))->setEmptyStateMessage(
|
||||
(new ObjectList($rootProblems))->setEmptyStateMessage(
|
||||
t('You are not authorized to view these objects.')
|
||||
)
|
||||
];
|
||||
|
|
@ -744,7 +745,7 @@ class ObjectDetail extends BaseHtmlElement
|
|||
|
||||
return [
|
||||
HtmlElement::create('h2', null, Text::create(t('Affected Objects'))),
|
||||
(new DependencyNodeList($affectedObjects))->setEmptyStateMessage(
|
||||
(new ObjectList($affectedObjects))->setEmptyStateMessage(
|
||||
t('You are not authorized to view these objects.')
|
||||
)
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,26 +1,48 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2024 Icinga GmbH | GPLv2 */
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\Detail;
|
||||
|
||||
use ipl\Html\Attributes;
|
||||
use Icinga\Exception\NotImplementedError;
|
||||
use Icinga\Module\Icingadb\Model\Comment;
|
||||
use Icinga\Module\Icingadb\Model\Downtime;
|
||||
use Icinga\Module\Icingadb\Model\History;
|
||||
use Icinga\Module\Icingadb\Model\Host;
|
||||
use Icinga\Module\Icingadb\Model\Hostgroupsummary;
|
||||
use Icinga\Module\Icingadb\Model\RedundancyGroup;
|
||||
use Icinga\Module\Icingadb\Model\Service;
|
||||
use Icinga\Module\Icingadb\Model\ServicegroupSummary;
|
||||
use Icinga\Module\Icingadb\Model\User;
|
||||
use Icinga\Module\Icingadb\Model\Usergroup;
|
||||
use Icinga\Module\Icingadb\View\CommentRenderer;
|
||||
use Icinga\Module\Icingadb\View\DowntimeRenderer;
|
||||
use Icinga\Module\Icingadb\View\EventRenderer;
|
||||
use Icinga\Module\Icingadb\View\HostgroupRenderer;
|
||||
use Icinga\Module\Icingadb\View\HostRenderer;
|
||||
use Icinga\Module\Icingadb\View\RedundancyGroupRenderer;
|
||||
use Icinga\Module\Icingadb\View\ServicegroupRenderer;
|
||||
use Icinga\Module\Icingadb\View\ServiceRenderer;
|
||||
use Icinga\Module\Icingadb\View\UsergroupRenderer;
|
||||
use Icinga\Module\Icingadb\View\UserRenderer;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\I18n\Translation;
|
||||
use ipl\Orm\Model;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
use ipl\Web\Widget\TimeSince;
|
||||
use ipl\Web\Layout\HeaderItemLayout;
|
||||
use ipl\Web\Layout\ItemLayout;
|
||||
|
||||
abstract class ObjectHeader extends BaseHtmlElement
|
||||
/**
|
||||
* ObjectHeader
|
||||
*
|
||||
* Create a header for icingadb object
|
||||
*
|
||||
* @phpstan-type _PART1 = RedundancyGroup|Service|Host|Usergroup|User|Comment|Downtime|History
|
||||
* @phpstan-type _PART2 = Hostgroupsummary|ServicegroupSummary
|
||||
*
|
||||
* @template Item of _PART1|_PART2
|
||||
*/
|
||||
class ObjectHeader extends BaseHtmlElement
|
||||
{
|
||||
use Translation;
|
||||
|
||||
/** @var array<string, mixed> */
|
||||
protected $baseAttributes = ['class' => 'object-header'];
|
||||
|
||||
/** @var Model The associated object */
|
||||
/** @var Item */
|
||||
protected $object;
|
||||
|
||||
protected $tag = 'div';
|
||||
|
|
@ -28,118 +50,70 @@ abstract class ObjectHeader extends BaseHtmlElement
|
|||
/**
|
||||
* Create a new object header
|
||||
*
|
||||
* @param Model $object
|
||||
* @param Item $object
|
||||
*/
|
||||
public function __construct(Model $object)
|
||||
{
|
||||
$this->object = $object;
|
||||
|
||||
$this->addAttributes($this->baseAttributes);
|
||||
|
||||
$this->init();
|
||||
}
|
||||
|
||||
abstract protected function assembleHeader(BaseHtmlElement $header): void;
|
||||
|
||||
abstract protected function assembleMain(BaseHtmlElement $main): void;
|
||||
|
||||
protected function assembleCaption(BaseHtmlElement $caption): void
|
||||
{
|
||||
}
|
||||
|
||||
protected function assembleTitle(BaseHtmlElement $title): void
|
||||
{
|
||||
}
|
||||
|
||||
protected function assembleVisual(BaseHtmlElement $visual): void
|
||||
{
|
||||
}
|
||||
|
||||
protected function getStateBallSize(): string
|
||||
{
|
||||
return StateBall::SIZE_BIG;
|
||||
}
|
||||
|
||||
protected function createCaption(): BaseHtmlElement
|
||||
{
|
||||
$caption = new HtmlElement('section', Attributes::create(['class' => 'caption']));
|
||||
|
||||
$this->assembleCaption($caption);
|
||||
|
||||
return $caption;
|
||||
}
|
||||
|
||||
protected function createHeader(): BaseHtmlElement
|
||||
{
|
||||
$header = new HtmlElement('header');
|
||||
|
||||
$this->assembleHeader($header);
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
protected function createMain(): BaseHtmlElement
|
||||
{
|
||||
$main = new HtmlElement('div', Attributes::create(['class' => 'main']));
|
||||
|
||||
$this->assembleMain($main);
|
||||
|
||||
return $main;
|
||||
}
|
||||
|
||||
protected function createTimestamp(): ?BaseHtmlElement
|
||||
{
|
||||
//TODO: add support for host/service
|
||||
return new TimeSince($this->object->state->last_state_change->getTimestamp());
|
||||
}
|
||||
|
||||
protected function createSubject(): BaseHtmlElement
|
||||
{
|
||||
return new HtmlElement(
|
||||
'span',
|
||||
Attributes::create(['class' => 'subject']),
|
||||
Text::create($this->object->display_name)
|
||||
);
|
||||
}
|
||||
|
||||
protected function createTitle(): BaseHtmlElement
|
||||
{
|
||||
$title = new HtmlElement('div', Attributes::create(['class' => 'title']));
|
||||
|
||||
$this->assembleTitle($title);
|
||||
|
||||
return $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ?BaseHtmlElement
|
||||
* @throws NotImplementedError When the object type is not supported
|
||||
*/
|
||||
protected function createVisual(): ?BaseHtmlElement
|
||||
{
|
||||
$visual = new HtmlElement('div', Attributes::create(['class' => 'visual']));
|
||||
|
||||
$this->assembleVisual($visual);
|
||||
if ($visual->isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $visual;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the list item
|
||||
*
|
||||
* If you want to adjust the object header after construction, override this method.
|
||||
*/
|
||||
protected function init(): void
|
||||
{
|
||||
}
|
||||
|
||||
protected function assemble(): void
|
||||
{
|
||||
$this->add([
|
||||
$this->createVisual(),
|
||||
$this->createMain()
|
||||
]);
|
||||
switch (true) {
|
||||
case $this->object instanceof RedundancyGroup:
|
||||
$renderer = new RedundancyGroupRenderer();
|
||||
|
||||
break;
|
||||
case $this->object instanceof Service:
|
||||
$renderer = new ServiceRenderer();
|
||||
|
||||
break;
|
||||
case $this->object instanceof Host:
|
||||
$renderer = new HostRenderer();
|
||||
|
||||
break;
|
||||
case $this->object instanceof Usergroup:
|
||||
$renderer = new UsergroupRenderer();
|
||||
|
||||
break;
|
||||
case $this->object instanceof User:
|
||||
$renderer = new UserRenderer();
|
||||
|
||||
break;
|
||||
case $this->object instanceof Comment:
|
||||
$renderer = (new CommentRenderer())->setTicketLinkDisabled();
|
||||
|
||||
break;
|
||||
case $this->object instanceof Downtime:
|
||||
$renderer = (new DowntimeRenderer())->setTicketLinkDisabled();
|
||||
|
||||
break;
|
||||
case $this->object instanceof History:
|
||||
$renderer = new EventRenderer();
|
||||
|
||||
break;
|
||||
case $this->object instanceof Hostgroupsummary:
|
||||
$renderer = new HostgroupRenderer();
|
||||
|
||||
break;
|
||||
case $this->object instanceof ServicegroupSummary:
|
||||
$renderer = new ServicegroupRenderer();
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedError('Not implemented');
|
||||
}
|
||||
|
||||
$layout = new HeaderItemLayout($this->object, $renderer);
|
||||
|
||||
if (isset($this->object->icon_image->icon_image)) {
|
||||
$layout->after(ItemLayout::VISUAL, 'icon-image');
|
||||
}
|
||||
|
||||
$this->addAttributes($layout->getAttributes());
|
||||
$this->addHtml($layout);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use Icinga\Module\Icingadb\Model\DependencyNode;
|
|||
use Icinga\Module\Icingadb\Model\RedundancyGroup;
|
||||
use Icinga\Module\Icingadb\Model\UnreachableParent;
|
||||
use Icinga\Module\Icingadb\Redis\VolatileStateResults;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\DependencyNodeList;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ObjectList;
|
||||
use Icinga\Module\Icingadb\Hook\ExtensionHook\ObjectDetailExtensionHook;
|
||||
use Icinga\Module\Icingadb\Widget\ShowMore;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
|
|
@ -96,7 +96,7 @@ class RedundancyGroupDetail extends BaseHtmlElement
|
|||
|
||||
return [
|
||||
HtmlElement::create('h2', null, Text::create($this->translate('Root Problems'))),
|
||||
(new DependencyNodeList($rootProblems))->setEmptyStateMessage(
|
||||
(new ObjectList($rootProblems))->setEmptyStateMessage(
|
||||
$this->translate('You are not authorized to view these objects.')
|
||||
)
|
||||
];
|
||||
|
|
@ -129,7 +129,7 @@ class RedundancyGroupDetail extends BaseHtmlElement
|
|||
|
||||
return [
|
||||
HtmlElement::create('h2', null, Text::create($this->translate('Group Members'))),
|
||||
(new DependencyNodeList($members))
|
||||
(new ObjectList($members))
|
||||
->setEmptyStateMessage($this->translate('You are not authorized to view these objects.')),
|
||||
(new ShowMore($members, Url::fromPath('icingadb/redundancygroup/members', ['id' => $this->group->id])))
|
||||
->setBaseTarget('_self')
|
||||
|
|
|
|||
|
|
@ -1,71 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2024 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\Detail;
|
||||
|
||||
use Icinga\Module\Icingadb\Model\RedundancyGroup;
|
||||
use Icinga\Module\Icingadb\Model\RedundancyGroupSummary;
|
||||
use Icinga\Module\Icingadb\Widget\DependencyNodeStatistics;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\Html;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
/**
|
||||
* @property RedundancyGroup $object
|
||||
*/
|
||||
class RedundancyGroupHeader extends ObjectHeader
|
||||
{
|
||||
/** @var RedundancyGroupSummary */
|
||||
protected $summary;
|
||||
|
||||
public function __construct(RedundancyGroup $object, RedundancyGroupSummary $summary)
|
||||
{
|
||||
$this->summary = $summary;
|
||||
|
||||
parent::__construct($object);
|
||||
}
|
||||
|
||||
protected function assembleVisual(BaseHtmlElement $visual): void
|
||||
{
|
||||
$stateBall = new StateBall($this->object->state->getStateText(), $this->getStateBallSize());
|
||||
$stateBall->add($this->object->state->getIcon());
|
||||
|
||||
$visual->addHtml($stateBall);
|
||||
}
|
||||
|
||||
protected function assembleTitle(BaseHtmlElement $title): void
|
||||
{
|
||||
$subject = $this->createSubject();
|
||||
if ($this->object->state->failed) {
|
||||
$title->addHtml(Html::sprintf(
|
||||
$this->translate('%s has no working objects', '<groupname> has ...'),
|
||||
$subject
|
||||
));
|
||||
} else {
|
||||
$title->addHtml(Html::sprintf(
|
||||
$this->translate('%s has working objects', '<groupname> has ...'),
|
||||
$subject
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
protected function createStatistics(): BaseHtmlElement
|
||||
{
|
||||
return new DependencyNodeStatistics($this->summary);
|
||||
}
|
||||
|
||||
protected function assembleHeader(BaseHtmlElement $header): void
|
||||
{
|
||||
$header->add($this->createTitle());
|
||||
$header->add($this->createStatistics());
|
||||
$header->add($this->createTimestamp());
|
||||
}
|
||||
|
||||
protected function assembleMain(BaseHtmlElement $main): void
|
||||
{
|
||||
$main->add($this->createHeader());
|
||||
}
|
||||
}
|
||||
|
|
@ -9,11 +9,12 @@ use Icinga\Module\Icingadb\Common\Database;
|
|||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Hook\ExtensionHook\ObjectDetailExtensionHook;
|
||||
use Icinga\Module\Icingadb\Model\User;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ObjectList;
|
||||
use Icinga\Module\Icingadb\Widget\ShowMore;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Web\Url;
|
||||
use ipl\Web\Widget\EmptyState;
|
||||
use ipl\Web\Widget\HorizontalKeyValue;
|
||||
use Icinga\Module\Icingadb\Widget\ItemTable\UsergroupTable;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
|
|
@ -89,7 +90,7 @@ class UserDetail extends BaseHtmlElement
|
|||
|
||||
return [
|
||||
new HtmlElement('h2', null, Text::create(t('Groups'))),
|
||||
new UsergroupTable($userGroups),
|
||||
new ObjectList($userGroups),
|
||||
$showMoreLink
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,12 +9,13 @@ use Icinga\Module\Icingadb\Common\Database;
|
|||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Hook\ExtensionHook\ObjectDetailExtensionHook;
|
||||
use Icinga\Module\Icingadb\Model\Usergroup;
|
||||
use Icinga\Module\Icingadb\Widget\ItemTable\UserTable;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\ObjectList;
|
||||
use Icinga\Module\Icingadb\Widget\ShowMore;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\Web\Url;
|
||||
use ipl\Web\Widget\EmptyState;
|
||||
use ipl\Web\Widget\HorizontalKeyValue;
|
||||
|
||||
|
|
@ -74,7 +75,7 @@ class UsergroupDetail extends BaseHtmlElement
|
|||
|
||||
return [
|
||||
new HtmlElement('h2', null, Text::create(t('Users'))),
|
||||
new UserTable($users),
|
||||
new ObjectList($users),
|
||||
$showMoreLink
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,131 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\NoSubjectLink;
|
||||
use Icinga\Module\Icingadb\Common\ObjectLinkDisabled;
|
||||
use Icinga\Module\Icingadb\Common\TicketLinks;
|
||||
use ipl\Html\Html;
|
||||
use Icinga\Module\Icingadb\Common\HostLink;
|
||||
use Icinga\Module\Icingadb\Common\Icons;
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Widget\MarkdownLine;
|
||||
use Icinga\Module\Icingadb\Common\ServiceLink;
|
||||
use Icinga\Module\Icingadb\Model\Comment;
|
||||
use ipl\Html\FormattedString;
|
||||
use ipl\Web\Common\BaseListItem;
|
||||
use ipl\Web\Widget\TimeAgo;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Widget\Icon;
|
||||
use ipl\Web\Widget\Link;
|
||||
use ipl\Web\Widget\TimeUntil;
|
||||
|
||||
/**
|
||||
* Comment item of a comment list. Represents one database row.
|
||||
*
|
||||
* @property Comment $item
|
||||
* @property CommentList $list
|
||||
*/
|
||||
abstract class BaseCommentListItem extends BaseListItem
|
||||
{
|
||||
use HostLink;
|
||||
use ServiceLink;
|
||||
use NoSubjectLink;
|
||||
use ObjectLinkDisabled;
|
||||
use TicketLinks;
|
||||
|
||||
protected function assembleCaption(BaseHtmlElement $caption): void
|
||||
{
|
||||
$markdownLine = new MarkdownLine($this->createTicketLinks($this->item->text));
|
||||
|
||||
$caption->getAttributes()->add($markdownLine->getAttributes());
|
||||
$caption->addFrom($markdownLine);
|
||||
}
|
||||
|
||||
protected function assembleTitle(BaseHtmlElement $title): void
|
||||
{
|
||||
$isAck = $this->item->entry_type === 'ack';
|
||||
$expires = $this->item->expire_time;
|
||||
|
||||
$subjectText = sprintf(
|
||||
$isAck ? t('%s acknowledged', '<username>..') : t('%s commented', '<username>..'),
|
||||
$this->item->author
|
||||
);
|
||||
|
||||
$headerParts = [
|
||||
new Icon(Icons::USER),
|
||||
$this->getNoSubjectLink()
|
||||
? new HtmlElement('span', Attributes::create(['class' => 'subject']), Text::create($subjectText))
|
||||
: new Link($subjectText, Links::comment($this->item), ['class' => 'subject'])
|
||||
];
|
||||
|
||||
if ($isAck) {
|
||||
$label = [Text::create('ack')];
|
||||
|
||||
if ($this->item->is_persistent) {
|
||||
array_unshift($label, new Icon(Icons::IS_PERSISTENT));
|
||||
}
|
||||
|
||||
$headerParts[] = Text::create(' ');
|
||||
$headerParts[] = new HtmlElement('span', Attributes::create(['class' => 'ack-badge badge']), ...$label);
|
||||
}
|
||||
|
||||
if ($expires !== null) {
|
||||
$headerParts[] = Text::create(' ');
|
||||
$headerParts[] = new HtmlElement(
|
||||
'span',
|
||||
Attributes::create(['class' => 'ack-badge badge']),
|
||||
Text::create(t('EXPIRES'))
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->getObjectLinkDisabled()) {
|
||||
// pass
|
||||
} elseif ($this->item->object_type === 'host') {
|
||||
$headerParts[] = $this->createHostLink($this->item->host, true);
|
||||
} else {
|
||||
$headerParts[] = $this->createServiceLink($this->item->service, $this->item->service->host, true);
|
||||
}
|
||||
|
||||
$title->addHtml(...$headerParts);
|
||||
}
|
||||
|
||||
protected function assembleVisual(BaseHtmlElement $visual): void
|
||||
{
|
||||
$visual->addHtml(new HtmlElement(
|
||||
'div',
|
||||
Attributes::create(['class' => 'user-ball']),
|
||||
Text::create($this->item->author[0])
|
||||
));
|
||||
}
|
||||
|
||||
protected function createTimestamp(): ?BaseHtmlElement
|
||||
{
|
||||
if ($this->item->expire_time) {
|
||||
return Html::tag(
|
||||
'span',
|
||||
FormattedString::create(t("expires %s"), new TimeUntil($this->item->expire_time->getTimestamp()))
|
||||
);
|
||||
}
|
||||
|
||||
return Html::tag(
|
||||
'span',
|
||||
FormattedString::create(t("created %s"), new TimeAgo($this->item->entry_time->getTimestamp()))
|
||||
);
|
||||
}
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
$this->setTicketLinkEnabled($this->list->getTicketLinkEnabled());
|
||||
$this->list->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name));
|
||||
$this->list->addMultiselectFilterAttribute($this, Filter::equal('name', $this->item->name));
|
||||
$this->setObjectLinkDisabled($this->list->getObjectLinkDisabled());
|
||||
$this->setNoSubjectLink($this->list->getNoSubjectLink());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,216 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Date\DateFormatter;
|
||||
use Icinga\Module\Icingadb\Common\HostLink;
|
||||
use Icinga\Module\Icingadb\Common\Icons;
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Common\NoSubjectLink;
|
||||
use Icinga\Module\Icingadb\Common\ObjectLinkDisabled;
|
||||
use Icinga\Module\Icingadb\Common\ServiceLink;
|
||||
use Icinga\Module\Icingadb\Common\TicketLinks;
|
||||
use Icinga\Module\Icingadb\Model\Downtime;
|
||||
use Icinga\Module\Icingadb\Widget\MarkdownLine;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\Html;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\TemplateString;
|
||||
use ipl\Html\Text;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Common\BaseListItem;
|
||||
use ipl\Web\Widget\Icon;
|
||||
use ipl\Web\Widget\Link;
|
||||
|
||||
/**
|
||||
* Downtime item of a downtime list. Represents one database row.
|
||||
*
|
||||
* @property Downtime $item
|
||||
* @property DowntimeList $list
|
||||
*/
|
||||
abstract class BaseDowntimeListItem extends BaseListItem
|
||||
{
|
||||
use HostLink;
|
||||
use ServiceLink;
|
||||
use NoSubjectLink;
|
||||
use ObjectLinkDisabled;
|
||||
use TicketLinks;
|
||||
|
||||
/** @var int Current Time */
|
||||
protected $currentTime;
|
||||
|
||||
/** @var int Duration */
|
||||
protected $duration;
|
||||
|
||||
/** @var int Downtime end time */
|
||||
protected $endTime;
|
||||
|
||||
/** @var bool Whether the downtime is active */
|
||||
protected $isActive;
|
||||
|
||||
/** @var int Downtime start time */
|
||||
protected $startTime;
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
if (
|
||||
isset($this->item->start_time, $this->item->end_time)
|
||||
&& $this->item->is_flexible
|
||||
&& $this->item->is_in_effect
|
||||
) {
|
||||
$this->startTime = $this->item->start_time->getTimestamp();
|
||||
$this->endTime = $this->item->end_time->getTimestamp();
|
||||
} else {
|
||||
$this->startTime = $this->item->scheduled_start_time->getTimestamp();
|
||||
$this->endTime = $this->item->scheduled_end_time->getTimestamp();
|
||||
}
|
||||
|
||||
$this->currentTime = time();
|
||||
|
||||
$this->isActive = $this->item->is_in_effect
|
||||
|| $this->item->is_flexible && $this->item->scheduled_start_time->getTimestamp() <= $this->currentTime;
|
||||
|
||||
$until = ($this->isActive ? $this->endTime : $this->startTime) - $this->currentTime;
|
||||
$this->duration = explode(' ', DateFormatter::formatDuration(
|
||||
$until <= 3600 ? $until : $until + (3600 - ((int) $until % 3600))
|
||||
), 2)[0];
|
||||
|
||||
$this->list->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name));
|
||||
$this->list->addMultiselectFilterAttribute($this, Filter::equal('name', $this->item->name));
|
||||
$this->setObjectLinkDisabled($this->list->getObjectLinkDisabled());
|
||||
$this->setNoSubjectLink($this->list->getNoSubjectLink());
|
||||
$this->setTicketLinkEnabled($this->list->getTicketLinkEnabled());
|
||||
|
||||
if ($this->item->is_in_effect) {
|
||||
$this->getAttributes()->add('class', 'in-effect');
|
||||
}
|
||||
}
|
||||
|
||||
protected function createProgress(): BaseHtmlElement
|
||||
{
|
||||
return new HtmlElement(
|
||||
'div',
|
||||
Attributes::create([
|
||||
'class' => 'progress',
|
||||
'data-animate-progress' => true,
|
||||
'data-start-time' => $this->startTime,
|
||||
'data-end-time' => $this->endTime
|
||||
]),
|
||||
new HtmlElement(
|
||||
'div',
|
||||
Attributes::create(['class' => 'bar'])
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected function assembleCaption(BaseHtmlElement $caption): void
|
||||
{
|
||||
$markdownLine = new MarkdownLine($this->createTicketLinks($this->item->comment));
|
||||
$caption->getAttributes()->add($markdownLine->getAttributes());
|
||||
$caption->addHtml(
|
||||
new HtmlElement(
|
||||
'span',
|
||||
null,
|
||||
new Icon(Icons::USER),
|
||||
Text::create($this->item->author)
|
||||
),
|
||||
Text::create(': ')
|
||||
)->addFrom($markdownLine);
|
||||
}
|
||||
|
||||
protected function assembleTitle(BaseHtmlElement $title): void
|
||||
{
|
||||
if ($this->getObjectLinkDisabled()) {
|
||||
$link = null;
|
||||
} elseif ($this->item->object_type === 'host') {
|
||||
$link = $this->createHostLink($this->item->host, true);
|
||||
} else {
|
||||
$link = $this->createServiceLink($this->item->service, $this->item->service->host, true);
|
||||
}
|
||||
|
||||
if ($this->item->is_flexible) {
|
||||
if ($link !== null) {
|
||||
$template = t('{{#link}}Flexible Downtime{{/link}} for %s');
|
||||
} else {
|
||||
$template = t('Flexible Downtime');
|
||||
}
|
||||
} else {
|
||||
if ($link !== null) {
|
||||
$template = t('{{#link}}Fixed Downtime{{/link}} for %s');
|
||||
} else {
|
||||
$template = t('Fixed Downtime');
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->getNoSubjectLink()) {
|
||||
if ($link === null) {
|
||||
$title->addHtml(HtmlElement::create('span', [ 'class' => 'subject'], $template));
|
||||
} else {
|
||||
$title->addHtml(TemplateString::create(
|
||||
$template,
|
||||
['link' => HtmlElement::create('span', [ 'class' => 'subject'])],
|
||||
$link
|
||||
));
|
||||
}
|
||||
} else {
|
||||
if ($link === null) {
|
||||
$title->addHtml(new Link($template, Links::downtime($this->item)));
|
||||
} else {
|
||||
$title->addHtml(TemplateString::create(
|
||||
$template,
|
||||
['link' => new Link('', Links::downtime($this->item))],
|
||||
$link
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function assembleVisual(BaseHtmlElement $visual): void
|
||||
{
|
||||
$dateTime = DateFormatter::formatDateTime($this->endTime);
|
||||
|
||||
if ($this->isActive) {
|
||||
$visual->addHtml(Html::sprintf(
|
||||
t('%s left', '<timespan>..'),
|
||||
Html::tag(
|
||||
'strong',
|
||||
Html::tag(
|
||||
'time',
|
||||
[
|
||||
'datetime' => $dateTime,
|
||||
'title' => $dateTime
|
||||
],
|
||||
$this->duration
|
||||
)
|
||||
)
|
||||
));
|
||||
} else {
|
||||
$visual->addHtml(Html::sprintf(
|
||||
t('in %s', '..<timespan>'),
|
||||
Html::tag('strong', $this->duration)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
protected function createTimestamp(): ?BaseHtmlElement
|
||||
{
|
||||
$dateTime = DateFormatter::formatDateTime($this->isActive ? $this->endTime : $this->startTime);
|
||||
|
||||
return Html::tag(
|
||||
'time',
|
||||
[
|
||||
'datetime' => $dateTime,
|
||||
'title' => $dateTime
|
||||
],
|
||||
sprintf(
|
||||
$this->isActive
|
||||
? t('expires in %s', '..<timespan>')
|
||||
: t('starts in %s', '..<timespan>'),
|
||||
$this->duration
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,407 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Date\DateFormatter;
|
||||
use Icinga\Module\Icingadb\Common\HostLink;
|
||||
use Icinga\Module\Icingadb\Common\HostStates;
|
||||
use Icinga\Module\Icingadb\Common\Icons;
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Common\NoSubjectLink;
|
||||
use Icinga\Module\Icingadb\Common\TicketLinks;
|
||||
use Icinga\Module\Icingadb\Widget\MarkdownLine;
|
||||
use Icinga\Module\Icingadb\Common\ServiceLink;
|
||||
use Icinga\Module\Icingadb\Common\ServiceStates;
|
||||
use Icinga\Module\Icingadb\Model\History;
|
||||
use Icinga\Module\Icingadb\Util\PluginOutput;
|
||||
use Icinga\Module\Icingadb\Widget\CheckAttempt;
|
||||
use Icinga\Module\Icingadb\Widget\PluginOutputContainer;
|
||||
use Icinga\Module\Icingadb\Widget\StateChange;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Common\BaseListItem;
|
||||
use ipl\Web\Widget\EmptyState;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
use ipl\Web\Widget\TimeAgo;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\Web\Widget\Icon;
|
||||
use ipl\Web\Widget\Link;
|
||||
|
||||
abstract class BaseHistoryListItem extends BaseListItem
|
||||
{
|
||||
use HostLink;
|
||||
use NoSubjectLink;
|
||||
use ServiceLink;
|
||||
use TicketLinks;
|
||||
|
||||
/** @var History */
|
||||
protected $item;
|
||||
|
||||
/** @var HistoryList */
|
||||
protected $list;
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
$this->setNoSubjectLink($this->list->getNoSubjectLink());
|
||||
$this->setTicketLinkEnabled($this->list->getTicketLinkEnabled());
|
||||
$this->list->addDetailFilterAttribute($this, Filter::equal('id', bin2hex($this->item->id)));
|
||||
}
|
||||
|
||||
abstract protected function getStateBallSize(): string;
|
||||
|
||||
protected function assembleCaption(BaseHtmlElement $caption): void
|
||||
{
|
||||
switch ($this->item->event_type) {
|
||||
case 'comment_add':
|
||||
case 'comment_remove':
|
||||
$markdownLine = new MarkdownLine($this->createTicketLinks($this->item->comment->comment));
|
||||
$caption->getAttributes()->add($markdownLine->getAttributes());
|
||||
$caption->add([
|
||||
new Icon(Icons::USER),
|
||||
$this->item->comment->author,
|
||||
': '
|
||||
])->addFrom($markdownLine);
|
||||
|
||||
break;
|
||||
case 'downtime_end':
|
||||
case 'downtime_start':
|
||||
$markdownLine = new MarkdownLine($this->createTicketLinks($this->item->downtime->comment));
|
||||
$caption->getAttributes()->add($markdownLine->getAttributes());
|
||||
$caption->add([
|
||||
new Icon(Icons::USER),
|
||||
$this->item->downtime->author,
|
||||
': '
|
||||
])->addFrom($markdownLine);
|
||||
|
||||
break;
|
||||
case 'flapping_start':
|
||||
$caption
|
||||
->add(sprintf(
|
||||
t('State Change Rate: %.2f%%; Start Threshold: %.2f%%'),
|
||||
$this->item->flapping->percent_state_change_start,
|
||||
$this->item->flapping->flapping_threshold_high
|
||||
))
|
||||
->getAttributes()
|
||||
->add('class', 'plugin-output');
|
||||
|
||||
break;
|
||||
case 'flapping_end':
|
||||
$caption
|
||||
->add(sprintf(
|
||||
t('State Change Rate: %.2f%%; End Threshold: %.2f%%; Flapping for %s'),
|
||||
$this->item->flapping->percent_state_change_end,
|
||||
$this->item->flapping->flapping_threshold_low,
|
||||
isset($this->item->flapping->end_time)
|
||||
? DateFormatter::formatDuration(
|
||||
$this->item->flapping->end_time->getTimestamp()
|
||||
- $this->item->flapping->start_time->getTimestamp()
|
||||
)
|
||||
: t('n. a.')
|
||||
))
|
||||
->getAttributes()
|
||||
->add('class', 'plugin-output');
|
||||
|
||||
break;
|
||||
case 'ack_clear':
|
||||
case 'ack_set':
|
||||
if (! isset($this->item->acknowledgement->comment) && ! isset($this->item->acknowledgement->author)) {
|
||||
$caption->addHtml(new EmptyState(
|
||||
t('This acknowledgement was set before Icinga DB history recording')
|
||||
));
|
||||
} else {
|
||||
$markdownLine = new MarkdownLine($this->createTicketLinks($this->item->acknowledgement->comment));
|
||||
$caption->getAttributes()->add($markdownLine->getAttributes());
|
||||
$caption->add([
|
||||
new Icon(Icons::USER),
|
||||
$this->item->acknowledgement->author,
|
||||
': '
|
||||
])->addFrom($markdownLine);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'notification':
|
||||
if (! empty($this->item->notification->author)) {
|
||||
$caption->add([
|
||||
new Icon(Icons::USER),
|
||||
$this->item->notification->author,
|
||||
': ',
|
||||
$this->item->notification->text
|
||||
]);
|
||||
} else {
|
||||
$commandName = $this->item->object_type === 'host'
|
||||
? $this->item->host->checkcommand_name
|
||||
: $this->item->service->checkcommand_name;
|
||||
if (isset($commandName)) {
|
||||
if (empty($this->item->notification->text)) {
|
||||
$caption->addHtml(new EmptyState(t('Output unavailable.')));
|
||||
} else {
|
||||
$caption->addHtml(new PluginOutputContainer(
|
||||
(new PluginOutput($this->item->notification->text))
|
||||
->setCommandName($commandName)
|
||||
));
|
||||
}
|
||||
} else {
|
||||
$caption->addHtml(new EmptyState(t('Waiting for Icinga DB to synchronize the config.')));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case 'state_change':
|
||||
$commandName = $this->item->object_type === 'host'
|
||||
? $this->item->host->checkcommand_name
|
||||
: $this->item->service->checkcommand_name;
|
||||
if (isset($commandName)) {
|
||||
if (empty($this->item->state->output)) {
|
||||
$caption->addHtml(new EmptyState(t('Output unavailable.')));
|
||||
} else {
|
||||
$caption->addHtml(new PluginOutputContainer(
|
||||
(new PluginOutput($this->item->state->output))
|
||||
->setCommandName($commandName)
|
||||
));
|
||||
}
|
||||
} else {
|
||||
$caption->addHtml(new EmptyState(t('Waiting for Icinga DB to synchronize the config.')));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function assembleVisual(BaseHtmlElement $visual): void
|
||||
{
|
||||
switch ($this->item->event_type) {
|
||||
case 'comment_add':
|
||||
$visual->addHtml(HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]],
|
||||
new Icon(Icons::COMMENT)
|
||||
));
|
||||
|
||||
break;
|
||||
case 'comment_remove':
|
||||
case 'downtime_end':
|
||||
case 'ack_clear':
|
||||
$visual->addHtml(HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]],
|
||||
new Icon(Icons::REMOVE)
|
||||
));
|
||||
|
||||
break;
|
||||
case 'downtime_start':
|
||||
$visual->addHtml(HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]],
|
||||
new Icon(Icons::IN_DOWNTIME)
|
||||
));
|
||||
|
||||
break;
|
||||
case 'ack_set':
|
||||
$visual->addHtml(HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]],
|
||||
new Icon(Icons::IS_ACKNOWLEDGED)
|
||||
));
|
||||
|
||||
break;
|
||||
case 'flapping_end':
|
||||
case 'flapping_start':
|
||||
$visual->addHtml(HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]],
|
||||
new Icon(Icons::IS_FLAPPING)
|
||||
));
|
||||
|
||||
break;
|
||||
case 'notification':
|
||||
$visual->addHtml(HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]],
|
||||
new Icon(Icons::NOTIFICATION)
|
||||
));
|
||||
|
||||
break;
|
||||
case 'state_change':
|
||||
if ($this->item->state->state_type === 'soft') {
|
||||
$stateType = 'soft_state';
|
||||
$previousStateType = 'previous_soft_state';
|
||||
|
||||
if ($this->item->state->previous_soft_state === 0) {
|
||||
$previousStateType = 'hard_state';
|
||||
}
|
||||
|
||||
$visual->addHtml(new CheckAttempt(
|
||||
(int) $this->item->state->check_attempt,
|
||||
(int) $this->item->state->max_check_attempts
|
||||
));
|
||||
} else {
|
||||
$stateType = 'hard_state';
|
||||
$previousStateType = 'previous_hard_state';
|
||||
|
||||
if ($this->item->state->hard_state === $this->item->state->previous_hard_state) {
|
||||
$previousStateType = 'previous_soft_state';
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->item->object_type === 'host') {
|
||||
$state = HostStates::text($this->item->state->$stateType);
|
||||
$previousState = HostStates::text($this->item->state->$previousStateType);
|
||||
} else {
|
||||
$state = ServiceStates::text($this->item->state->$stateType);
|
||||
$previousState = ServiceStates::text($this->item->state->$previousStateType);
|
||||
}
|
||||
|
||||
$stateChange = new StateChange($state, $previousState);
|
||||
if ($stateType === 'soft_state') {
|
||||
$stateChange->setCurrentStateBallSize(StateBall::SIZE_MEDIUM_LARGE);
|
||||
}
|
||||
|
||||
if ($previousStateType === 'previous_soft_state') {
|
||||
$stateChange->setPreviousStateBallSize(StateBall::SIZE_MEDIUM_LARGE);
|
||||
if ($stateType === 'soft_state') {
|
||||
$visual->getAttributes()->add('class', 'small-state-change');
|
||||
}
|
||||
}
|
||||
|
||||
$visual->prependHtml($stateChange);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function assembleTitle(BaseHtmlElement $title): void
|
||||
{
|
||||
switch ($this->item->event_type) {
|
||||
case 'comment_add':
|
||||
$subjectLabel = t('Comment added');
|
||||
|
||||
break;
|
||||
case 'comment_remove':
|
||||
if (! empty($this->item->comment->removed_by)) {
|
||||
if ($this->item->comment->removed_by !== $this->item->comment->author) {
|
||||
$subjectLabel = sprintf(
|
||||
t('Comment removed by %s', '..<username>'),
|
||||
$this->item->comment->removed_by
|
||||
);
|
||||
} else {
|
||||
$subjectLabel = t('Comment removed by author');
|
||||
}
|
||||
} elseif (isset($this->item->comment->expire_time)) {
|
||||
$subjectLabel = t('Comment expired');
|
||||
} else {
|
||||
$subjectLabel = t('Comment removed');
|
||||
}
|
||||
|
||||
break;
|
||||
case 'downtime_end':
|
||||
if (! empty($this->item->downtime->cancelled_by)) {
|
||||
if ($this->item->downtime->cancelled_by !== $this->item->downtime->author) {
|
||||
$subjectLabel = sprintf(
|
||||
t('Downtime cancelled by %s', '..<username>'),
|
||||
$this->item->downtime->cancelled_by
|
||||
);
|
||||
} else {
|
||||
$subjectLabel = t('Downtime cancelled by author');
|
||||
}
|
||||
} elseif ($this->item->downtime->has_been_cancelled === 'y') {
|
||||
$subjectLabel = t('Downtime cancelled');
|
||||
} else {
|
||||
$subjectLabel = t('Downtime ended');
|
||||
}
|
||||
|
||||
break;
|
||||
case 'downtime_start':
|
||||
$subjectLabel = t('Downtime started');
|
||||
|
||||
break;
|
||||
case 'flapping_start':
|
||||
$subjectLabel = t('Flapping started');
|
||||
|
||||
break;
|
||||
case 'flapping_end':
|
||||
$subjectLabel = t('Flapping stopped');
|
||||
|
||||
break;
|
||||
case 'ack_set':
|
||||
$subjectLabel = t('Acknowledgement set');
|
||||
|
||||
break;
|
||||
case 'ack_clear':
|
||||
if (! empty($this->item->acknowledgement->cleared_by)) {
|
||||
if ($this->item->acknowledgement->cleared_by !== $this->item->acknowledgement->author) {
|
||||
$subjectLabel = sprintf(
|
||||
t('Acknowledgement cleared by %s', '..<username>'),
|
||||
$this->item->acknowledgement->cleared_by
|
||||
);
|
||||
} else {
|
||||
$subjectLabel = t('Acknowledgement cleared by author');
|
||||
}
|
||||
} elseif (isset($this->item->acknowledgement->expire_time)) {
|
||||
$subjectLabel = t('Acknowledgement expired');
|
||||
} else {
|
||||
$subjectLabel = t('Acknowledgement cleared');
|
||||
}
|
||||
|
||||
break;
|
||||
case 'notification':
|
||||
$subjectLabel = isset($this->item->notification->type) ? sprintf(
|
||||
NotificationListItem::phraseForType($this->item->notification->type),
|
||||
ucfirst($this->item->object_type)
|
||||
) : $this->item->event_type;
|
||||
|
||||
break;
|
||||
case 'state_change':
|
||||
$state = $this->item->state->state_type === 'hard'
|
||||
? $this->item->state->hard_state
|
||||
: $this->item->state->soft_state;
|
||||
if ($state === 0) {
|
||||
if ($this->item->object_type === 'service') {
|
||||
$subjectLabel = t('Service recovered');
|
||||
} else {
|
||||
$subjectLabel = t('Host recovered');
|
||||
}
|
||||
} else {
|
||||
if ($this->item->state->state_type === 'hard') {
|
||||
$subjectLabel = t('Hard state changed');
|
||||
} else {
|
||||
$subjectLabel = t('Soft state changed');
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
$subjectLabel = $this->item->event_type;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ($this->getNoSubjectLink()) {
|
||||
$title->addHtml(HtmlElement::create('span', ['class' => 'subject'], $subjectLabel));
|
||||
} else {
|
||||
$title->addHtml(new Link($subjectLabel, Links::event($this->item), ['class' => 'subject']));
|
||||
}
|
||||
|
||||
if ($this->item->object_type === 'host') {
|
||||
if (isset($this->item->host->id)) {
|
||||
$link = $this->createHostLink($this->item->host, true);
|
||||
}
|
||||
} else {
|
||||
if (isset($this->item->host->id, $this->item->service->id)) {
|
||||
$link = $this->createServiceLink($this->item->service, $this->item->host, true);
|
||||
}
|
||||
}
|
||||
|
||||
$title->addHtml(Text::create(' '));
|
||||
if (isset($link)) {
|
||||
$title->addHtml($link);
|
||||
}
|
||||
}
|
||||
|
||||
protected function createTimestamp(): ?BaseHtmlElement
|
||||
{
|
||||
return new TimeAgo($this->item->event_time->getTimestamp());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Common\NoSubjectLink;
|
||||
use Icinga\Module\Icingadb\Model\Host;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Widget\Link;
|
||||
|
||||
/**
|
||||
* Host item of a host list. Represents one database row.
|
||||
*
|
||||
* @property Host $item
|
||||
* @property HostList $list
|
||||
*/
|
||||
abstract class BaseHostListItem extends StateListItem
|
||||
{
|
||||
use NoSubjectLink;
|
||||
|
||||
/**
|
||||
* Create new subject link
|
||||
*
|
||||
* @return BaseHtmlElement
|
||||
*/
|
||||
protected function createSubject()
|
||||
{
|
||||
if ($this->getNoSubjectLink()) {
|
||||
return new HtmlElement(
|
||||
'span',
|
||||
Attributes::create(['class' => 'subject']),
|
||||
Text::create($this->item->display_name)
|
||||
);
|
||||
} else {
|
||||
return new Link($this->item->display_name, Links::host($this->item), ['class' => 'subject']);
|
||||
}
|
||||
}
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
parent::init();
|
||||
|
||||
if ($this->list->getNoSubjectLink()) {
|
||||
$this->setNoSubjectLink();
|
||||
}
|
||||
|
||||
$this->list->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name))
|
||||
->addMultiselectFilterAttribute($this, Filter::equal('host.name', $this->item->name));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,189 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\HostLink;
|
||||
use Icinga\Module\Icingadb\Common\HostStates;
|
||||
use Icinga\Module\Icingadb\Common\Icons;
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Common\NoSubjectLink;
|
||||
use Icinga\Module\Icingadb\Common\ServiceLink;
|
||||
use Icinga\Module\Icingadb\Common\ServiceStates;
|
||||
use Icinga\Module\Icingadb\Util\PluginOutput;
|
||||
use Icinga\Module\Icingadb\Widget\PluginOutputContainer;
|
||||
use Icinga\Module\Icingadb\Widget\StateChange;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Common\BaseListItem;
|
||||
use ipl\Web\Widget\EmptyState;
|
||||
use ipl\Web\Widget\TimeAgo;
|
||||
use InvalidArgumentException;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\Web\Widget\Icon;
|
||||
use ipl\Web\Widget\Link;
|
||||
|
||||
abstract class BaseNotificationListItem extends BaseListItem
|
||||
{
|
||||
use HostLink;
|
||||
use NoSubjectLink;
|
||||
use ServiceLink;
|
||||
|
||||
/** @var NotificationList */
|
||||
protected $list;
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
$this->setNoSubjectLink($this->list->getNoSubjectLink());
|
||||
$this->list->addDetailFilterAttribute($this, Filter::equal('id', bin2hex($this->item->history->id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a localized phrase for the given notification type
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function phraseForType(string $type): string
|
||||
{
|
||||
switch ($type) {
|
||||
case 'acknowledgement':
|
||||
return t('Problem acknowledged');
|
||||
case 'custom':
|
||||
return t('Custom Notification triggered');
|
||||
case 'downtime_end':
|
||||
return t('Downtime ended');
|
||||
case 'downtime_removed':
|
||||
return t('Downtime removed');
|
||||
case 'downtime_start':
|
||||
return t('Downtime started');
|
||||
case 'flapping_end':
|
||||
return t('Flapping stopped');
|
||||
case 'flapping_start':
|
||||
return t('Flapping started');
|
||||
case 'problem':
|
||||
return t('%s ran into a problem');
|
||||
case 'recovery':
|
||||
return t('%s recovered');
|
||||
default:
|
||||
throw new InvalidArgumentException(sprintf('Type %s is not a valid notification type', $type));
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected function getStateBallSize();
|
||||
|
||||
protected function assembleCaption(BaseHtmlElement $caption): void
|
||||
{
|
||||
if (in_array($this->item->type, ['flapping_end', 'flapping_start', 'problem', 'recovery'])) {
|
||||
$commandName = $this->item->object_type === 'host'
|
||||
? $this->item->host->checkcommand_name
|
||||
: $this->item->service->checkcommand_name;
|
||||
if (isset($commandName)) {
|
||||
if (empty($this->item->text)) {
|
||||
$caption->addHtml(new EmptyState(t('Output unavailable.')));
|
||||
} else {
|
||||
$caption->addHtml(new PluginOutputContainer(
|
||||
(new PluginOutput($this->item->text))
|
||||
->setCommandName($commandName)
|
||||
));
|
||||
}
|
||||
} else {
|
||||
$caption->addHtml(new EmptyState(t('Waiting for Icinga DB to synchronize the config.')));
|
||||
}
|
||||
} else {
|
||||
$caption->add([
|
||||
new Icon(Icons::USER),
|
||||
$this->item->author,
|
||||
': ',
|
||||
$this->item->text
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function assembleVisual(BaseHtmlElement $visual): void
|
||||
{
|
||||
switch ($this->item->type) {
|
||||
case 'acknowledgement':
|
||||
$visual->addHtml(HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]],
|
||||
new Icon(Icons::IS_ACKNOWLEDGED)
|
||||
));
|
||||
|
||||
break;
|
||||
case 'custom':
|
||||
$visual->addHtml(HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]],
|
||||
new Icon(Icons::NOTIFICATION)
|
||||
));
|
||||
|
||||
break;
|
||||
case 'downtime_end':
|
||||
case 'downtime_removed':
|
||||
case 'downtime_start':
|
||||
$visual->addHtml(HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]],
|
||||
new Icon(Icons::IN_DOWNTIME)
|
||||
));
|
||||
|
||||
break;
|
||||
case 'flapping_end':
|
||||
case 'flapping_start':
|
||||
$visual->addHtml(HtmlElement::create(
|
||||
'div',
|
||||
['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]],
|
||||
new Icon(Icons::IS_FLAPPING)
|
||||
));
|
||||
|
||||
break;
|
||||
case 'problem':
|
||||
case 'recovery':
|
||||
if ($this->item->object_type === 'host') {
|
||||
$state = HostStates::text($this->item->state);
|
||||
$previousHardState = HostStates::text($this->item->previous_hard_state);
|
||||
} else {
|
||||
$state = ServiceStates::text($this->item->state);
|
||||
$previousHardState = ServiceStates::text($this->item->previous_hard_state);
|
||||
}
|
||||
|
||||
$visual->addHtml(new StateChange($state, $previousHardState));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function assembleTitle(BaseHtmlElement $title): void
|
||||
{
|
||||
if ($this->getNoSubjectLink()) {
|
||||
$title->addHtml(HtmlElement::create(
|
||||
'span',
|
||||
['class' => 'subject'],
|
||||
sprintf(self::phraseForType($this->item->type), ucfirst($this->item->object_type))
|
||||
));
|
||||
} else {
|
||||
$title->addHtml(new Link(
|
||||
sprintf(self::phraseForType($this->item->type), ucfirst($this->item->object_type)),
|
||||
Links::event($this->item->history),
|
||||
['class' => 'subject']
|
||||
));
|
||||
}
|
||||
|
||||
if ($this->item->object_type === 'host') {
|
||||
$link = $this->createHostLink($this->item->host, true);
|
||||
} else {
|
||||
$link = $this->createServiceLink($this->item->service, $this->item->host, true);
|
||||
}
|
||||
|
||||
$title->addHtml(Text::create(' '), $link);
|
||||
}
|
||||
|
||||
protected function createTimestamp(): ?BaseHtmlElement
|
||||
{
|
||||
return new TimeAgo($this->item->send_time->getTimestamp());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Common\NoSubjectLink;
|
||||
use Icinga\Module\Icingadb\Model\Service;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\Html;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Widget\Link;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
/**
|
||||
* Service item of a service list. Represents one database row.
|
||||
*
|
||||
* @property Service $item
|
||||
* @property ServiceList $list
|
||||
*/
|
||||
abstract class BaseServiceListItem extends StateListItem
|
||||
{
|
||||
use NoSubjectLink;
|
||||
|
||||
protected function createSubject()
|
||||
{
|
||||
$service = $this->item->display_name;
|
||||
$host = [
|
||||
new StateBall($this->item->host->state->getStateText(), StateBall::SIZE_MEDIUM),
|
||||
' ',
|
||||
$this->item->host->display_name
|
||||
];
|
||||
|
||||
$host = new Link($host, Links::host($this->item->host), ['class' => 'subject']);
|
||||
if ($this->getNoSubjectLink()) {
|
||||
$service = new HtmlElement('span', Attributes::create(['class' => 'subject']), Text::create($service));
|
||||
} else {
|
||||
$service = new Link($service, Links::service($this->item, $this->item->host), ['class' => 'subject']);
|
||||
}
|
||||
|
||||
return [Html::sprintf(t('%s on %s', '<service> on <host>'), $service, $host)];
|
||||
}
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
parent::init();
|
||||
|
||||
if ($this->list->getNoSubjectLink()) {
|
||||
$this->setNoSubjectLink();
|
||||
}
|
||||
|
||||
$this->list->addMultiselectFilterAttribute(
|
||||
$this,
|
||||
Filter::all(
|
||||
Filter::equal('service.name', $this->item->name),
|
||||
Filter::equal('host.name', $this->item->host->name)
|
||||
)
|
||||
);
|
||||
$this->list->addDetailFilterAttribute(
|
||||
$this,
|
||||
Filter::all(
|
||||
Filter::equal('name', $this->item->name),
|
||||
Filter::equal('host.name', $this->item->host->name)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\CaptionDisabled;
|
||||
use Icinga\Module\Icingadb\Common\DetailActions;
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Common\NoSubjectLink;
|
||||
use Icinga\Module\Icingadb\Common\ObjectLinkDisabled;
|
||||
use Icinga\Module\Icingadb\Common\TicketLinks;
|
||||
use Icinga\Module\Icingadb\Common\ViewMode;
|
||||
use ipl\Web\Common\BaseItemList;
|
||||
use ipl\Web\Url;
|
||||
|
||||
class CommentList extends BaseItemList
|
||||
{
|
||||
use CaptionDisabled;
|
||||
use NoSubjectLink;
|
||||
use ObjectLinkDisabled;
|
||||
use ViewMode;
|
||||
use TicketLinks;
|
||||
use DetailActions;
|
||||
|
||||
protected $defaultAttributes = ['class' => 'comment-list'];
|
||||
|
||||
protected function getItemClass(): string
|
||||
{
|
||||
$viewMode = $this->getViewMode();
|
||||
|
||||
$this->addAttributes(['class' => $viewMode]);
|
||||
|
||||
if ($viewMode === 'minimal') {
|
||||
return CommentListItemMinimal::class;
|
||||
} elseif ($viewMode === 'detailed') {
|
||||
$this->removeAttribute('class', 'default-layout');
|
||||
}
|
||||
|
||||
return CommentListItem::class;
|
||||
}
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
$this->initializeDetailActions();
|
||||
$this->setMultiselectUrl(Links::commentsDetails());
|
||||
$this->setDetailUrl(Url::fromPath('icingadb/comment'));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\ListItemCommonLayout;
|
||||
|
||||
class CommentListItem extends BaseCommentListItem
|
||||
{
|
||||
use ListItemCommonLayout;
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\ListItemMinimalLayout;
|
||||
|
||||
class CommentListItemMinimal extends BaseCommentListItem
|
||||
{
|
||||
use ListItemMinimalLayout;
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
parent::init();
|
||||
|
||||
if ($this->list->isCaptionDisabled()) {
|
||||
$this->setCaptionDisabled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2024 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Model\DependencyNode;
|
||||
use Icinga\Module\Icingadb\Model\Host;
|
||||
use Icinga\Module\Icingadb\Model\UnreachableParent;
|
||||
use ipl\Web\Common\BaseListItem;
|
||||
|
||||
/**
|
||||
* Dependency node list
|
||||
*/
|
||||
class DependencyNodeList extends StateList
|
||||
{
|
||||
protected $defaultAttributes = ['class' => ['dependency-node-list']];
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
$this->initializeDetailActions();
|
||||
}
|
||||
|
||||
protected function getItemClass(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
protected function createListItem(object $data): BaseListItem
|
||||
{
|
||||
$viewMode = $this->getViewMode();
|
||||
/** @var UnreachableParent|DependencyNode $data */
|
||||
if ($data->redundancy_group_id !== null) {
|
||||
if ($viewMode === 'minimal') {
|
||||
return new RedundancyGroupListItemMinimal($data->redundancy_group, $this);
|
||||
}
|
||||
|
||||
if ($viewMode === 'detailed') {
|
||||
$this->removeAttribute('class', 'default-layout');
|
||||
}
|
||||
|
||||
return new RedundancyGroupListItem($data->redundancy_group, $this);
|
||||
}
|
||||
|
||||
$object = $data->service_id !== null ? $data->service : $data->host;
|
||||
|
||||
switch ($viewMode) {
|
||||
case 'minimal':
|
||||
$class = $object instanceof Host ? HostListItemMinimal::class : ServiceListItemMinimal::class;
|
||||
break;
|
||||
case 'detailed':
|
||||
$this->removeAttribute('class', 'default-layout');
|
||||
|
||||
$class = $object instanceof Host ? HostListItemDetailed::class : ServiceListItemDetailed::class;
|
||||
break;
|
||||
default:
|
||||
$class = $object instanceof Host ? HostListItem::class : ServiceListItem::class;
|
||||
}
|
||||
|
||||
return new $class($object, $this);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\CaptionDisabled;
|
||||
use Icinga\Module\Icingadb\Common\DetailActions;
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Common\NoSubjectLink;
|
||||
use Icinga\Module\Icingadb\Common\ObjectLinkDisabled;
|
||||
use Icinga\Module\Icingadb\Common\TicketLinks;
|
||||
use Icinga\Module\Icingadb\Common\ViewMode;
|
||||
use ipl\Web\Common\BaseItemList;
|
||||
use ipl\Web\Url;
|
||||
|
||||
class DowntimeList extends BaseItemList
|
||||
{
|
||||
use CaptionDisabled;
|
||||
use NoSubjectLink;
|
||||
use ObjectLinkDisabled;
|
||||
use ViewMode;
|
||||
use TicketLinks;
|
||||
use DetailActions;
|
||||
|
||||
protected $defaultAttributes = ['class' => 'downtime-list'];
|
||||
|
||||
protected function getItemClass(): string
|
||||
{
|
||||
$viewMode = $this->getViewMode();
|
||||
|
||||
$this->addAttributes(['class' => $viewMode]);
|
||||
|
||||
if ($viewMode === 'minimal') {
|
||||
return DowntimeListItemMinimal::class;
|
||||
} elseif ($viewMode === 'detailed') {
|
||||
$this->removeAttribute('class', 'default-layout');
|
||||
}
|
||||
|
||||
return DowntimeListItem::class;
|
||||
}
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
$this->initializeDetailActions();
|
||||
$this->setMultiselectUrl(Links::downtimesDetails());
|
||||
$this->setDetailUrl(Url::fromPath('icingadb/downtime'));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\ListItemCommonLayout;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
|
||||
class DowntimeListItem extends BaseDowntimeListItem
|
||||
{
|
||||
use ListItemCommonLayout;
|
||||
|
||||
protected function assembleMain(BaseHtmlElement $main): void
|
||||
{
|
||||
if ($this->item->is_in_effect) {
|
||||
$main->add($this->createProgress());
|
||||
}
|
||||
|
||||
$main->add($this->createHeader());
|
||||
$main->add($this->createCaption());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\ListItemMinimalLayout;
|
||||
|
||||
class DowntimeListItemMinimal extends BaseDowntimeListItem
|
||||
{
|
||||
use ListItemMinimalLayout;
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
parent::init();
|
||||
|
||||
if ($this->list->isCaptionDisabled()) {
|
||||
$this->setCaptionDisabled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\CaptionDisabled;
|
||||
use Icinga\Module\Icingadb\Common\DetailActions;
|
||||
use Icinga\Module\Icingadb\Common\LoadMore;
|
||||
use Icinga\Module\Icingadb\Common\NoSubjectLink;
|
||||
use Icinga\Module\Icingadb\Common\TicketLinks;
|
||||
use Icinga\Module\Icingadb\Common\ViewMode;
|
||||
use ipl\Orm\ResultSet;
|
||||
use ipl\Web\Common\BaseItemList;
|
||||
use ipl\Web\Url;
|
||||
|
||||
class HistoryList extends BaseItemList
|
||||
{
|
||||
use CaptionDisabled;
|
||||
use NoSubjectLink;
|
||||
use ViewMode;
|
||||
use LoadMore;
|
||||
use TicketLinks;
|
||||
use DetailActions;
|
||||
|
||||
protected $defaultAttributes = ['class' => 'history-list'];
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
/** @var ResultSet $data */
|
||||
$data = $this->data;
|
||||
$this->data = $this->getIterator($data);
|
||||
$this->initializeDetailActions();
|
||||
$this->setDetailUrl(Url::fromPath('icingadb/event'));
|
||||
}
|
||||
|
||||
protected function getItemClass(): string
|
||||
{
|
||||
switch ($this->getViewMode()) {
|
||||
case 'minimal':
|
||||
return HistoryListItemMinimal::class;
|
||||
case 'detailed':
|
||||
$this->removeAttribute('class', 'default-layout');
|
||||
|
||||
return HistoryListItemDetailed::class;
|
||||
default:
|
||||
return HistoryListItem::class;
|
||||
}
|
||||
}
|
||||
|
||||
protected function assemble(): void
|
||||
{
|
||||
$this->addAttributes(['class' => $this->getViewMode()]);
|
||||
|
||||
parent::assemble();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\ListItemCommonLayout;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
class HistoryListItem extends BaseHistoryListItem
|
||||
{
|
||||
use ListItemCommonLayout;
|
||||
|
||||
protected function getStateBallSize(): string
|
||||
{
|
||||
return StateBall::SIZE_LARGE;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\ListItemDetailedLayout;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
class HistoryListItemDetailed extends BaseHistoryListItem
|
||||
{
|
||||
use ListItemDetailedLayout;
|
||||
|
||||
protected function getStateBallSize(): string
|
||||
{
|
||||
return StateBall::SIZE_LARGE;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\ListItemMinimalLayout;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
class HistoryListItemMinimal extends BaseHistoryListItem
|
||||
{
|
||||
use ListItemMinimalLayout;
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
parent::init();
|
||||
|
||||
if ($this->list->isCaptionDisabled()) {
|
||||
$this->setCaptionDisabled();
|
||||
}
|
||||
}
|
||||
|
||||
protected function getStateBallSize(): string
|
||||
{
|
||||
return StateBall::SIZE_BIG;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\HostStates;
|
||||
use Icinga\Module\Icingadb\Widget\StateChange;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
class HostDetailHeader extends HostListItemMinimal
|
||||
{
|
||||
protected function getStateBallSize(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
protected function assembleVisual(BaseHtmlElement $visual): void
|
||||
{
|
||||
if ($this->state->state_type === 'soft') {
|
||||
$stateType = 'soft_state';
|
||||
$previousStateType = 'previous_soft_state';
|
||||
|
||||
if ($this->state->previous_soft_state === 0) {
|
||||
$previousStateType = 'hard_state';
|
||||
}
|
||||
} else {
|
||||
$stateType = 'hard_state';
|
||||
$previousStateType = 'previous_hard_state';
|
||||
|
||||
if ($this->state->hard_state === $this->state->previous_hard_state) {
|
||||
$previousStateType = 'previous_soft_state';
|
||||
}
|
||||
}
|
||||
|
||||
$state = HostStates::text($this->state->$stateType);
|
||||
$previousState = HostStates::text($this->state->$previousStateType);
|
||||
|
||||
$stateChange = new StateChange($state, $previousState);
|
||||
if ($stateType === 'soft_state') {
|
||||
$stateChange->setCurrentStateBallSize(StateBall::SIZE_MEDIUM_LARGE);
|
||||
}
|
||||
|
||||
if ($previousStateType === 'previous_soft_state') {
|
||||
$stateChange->setPreviousStateBallSize(StateBall::SIZE_MEDIUM_LARGE);
|
||||
if ($stateType === 'soft_state') {
|
||||
$visual->getAttributes()->add('class', 'small-state-change');
|
||||
}
|
||||
}
|
||||
|
||||
$stateChange->setIcon($this->state->getIcon());
|
||||
$stateChange->setHandled(
|
||||
$this->state->is_problem && ($this->state->is_handled || ! $this->state->is_reachable)
|
||||
);
|
||||
|
||||
$visual->addHtml($stateChange);
|
||||
}
|
||||
|
||||
protected function assemble(): void
|
||||
{
|
||||
$attributes = $this->list->getAttributes();
|
||||
if (! in_array('minimal', $attributes->get('class')->getValue())) {
|
||||
$attributes->add('class', 'minimal');
|
||||
}
|
||||
|
||||
parent::assemble();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use ipl\Web\Url;
|
||||
|
||||
/**
|
||||
* Host list
|
||||
*/
|
||||
class HostList extends StateList
|
||||
{
|
||||
protected $defaultAttributes = ['class' => 'host-list'];
|
||||
|
||||
protected function getItemClass(): string
|
||||
{
|
||||
switch ($this->getViewMode()) {
|
||||
case 'minimal':
|
||||
return HostListItemMinimal::class;
|
||||
case 'detailed':
|
||||
$this->removeAttribute('class', 'default-layout');
|
||||
|
||||
return HostListItemDetailed::class;
|
||||
case 'objectHeader':
|
||||
return HostDetailHeader::class;
|
||||
default:
|
||||
return HostListItem::class;
|
||||
}
|
||||
}
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
$this->initializeDetailActions();
|
||||
$this->setMultiselectUrl(Links::hostsDetails());
|
||||
$this->setDetailUrl(Url::fromPath('icingadb/host'));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\ListItemCommonLayout;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
class HostListItem extends BaseHostListItem
|
||||
{
|
||||
use ListItemCommonLayout;
|
||||
|
||||
protected function getStateBallSize(): string
|
||||
{
|
||||
return StateBall::SIZE_LARGE;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\ListItemDetailedLayout;
|
||||
use Icinga\Module\Icingadb\Util\PerfDataSet;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\CommentList;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\HtmlString;
|
||||
use ipl\Html\Text;
|
||||
use ipl\Web\Widget\Icon;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
class HostListItemDetailed extends BaseHostListItem
|
||||
{
|
||||
use ListItemDetailedLayout;
|
||||
|
||||
/** @var int Max pie charts to be shown */
|
||||
const PIE_CHART_LIMIT = 5;
|
||||
|
||||
protected function getStateBallSize(): string
|
||||
{
|
||||
return StateBall::SIZE_LARGE;
|
||||
}
|
||||
|
||||
protected function assembleFooter(BaseHtmlElement $footer): void
|
||||
{
|
||||
$statusIcons = new HtmlElement('div', Attributes::create(['class' => 'status-icons']));
|
||||
|
||||
if ($this->item->state->last_comment->host_id === $this->item->id) {
|
||||
$comment = $this->item->state->last_comment;
|
||||
$comment->host = $this->item;
|
||||
$comment = (new CommentList([$comment]))
|
||||
->setNoSubjectLink()
|
||||
->setObjectLinkDisabled()
|
||||
->setDetailActionsDisabled();
|
||||
|
||||
$statusIcons->addHtml(
|
||||
new HtmlElement(
|
||||
'div',
|
||||
Attributes::create(['class' => 'comment-wrapper']),
|
||||
new HtmlElement('div', Attributes::create(['class' => 'comment-popup']), $comment),
|
||||
(new Icon('comments', ['class' => 'comment-icon']))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->item->state->is_flapping) {
|
||||
$statusIcons->addHtml(new Icon(
|
||||
'random',
|
||||
[
|
||||
'title' => sprintf(t('Host "%s" is in flapping state'), $this->item->display_name),
|
||||
]
|
||||
));
|
||||
}
|
||||
|
||||
if (! $this->item->notifications_enabled) {
|
||||
$statusIcons->addHtml(new Icon('bell-slash', ['title' => t('Notifications disabled')]));
|
||||
}
|
||||
|
||||
if (! $this->item->active_checks_enabled) {
|
||||
$statusIcons->addHtml(new Icon('eye-slash', ['title' => t('Active checks disabled')]));
|
||||
}
|
||||
|
||||
$performanceData = new HtmlElement('div', Attributes::create(['class' => 'performance-data']));
|
||||
if ($this->item->state->performance_data) {
|
||||
$pieChartData = PerfDataSet::fromString($this->item->state->normalized_performance_data)->asArray();
|
||||
|
||||
$pies = [];
|
||||
foreach ($pieChartData as $i => $perfdata) {
|
||||
if ($perfdata->isVisualizable()) {
|
||||
$pies[] = $perfdata->asInlinePie()->render();
|
||||
}
|
||||
|
||||
// Check if number of visualizable pie charts is larger than PIE_CHART_LIMIT
|
||||
if (count($pies) > HostListItemDetailed::PIE_CHART_LIMIT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$maxVisiblePies = HostListItemDetailed::PIE_CHART_LIMIT - 2;
|
||||
$numOfPies = count($pies);
|
||||
foreach ($pies as $i => $pie) {
|
||||
if (
|
||||
// Show max. 5 elements: if there are more than 5, show 4 + `…`
|
||||
$i > $maxVisiblePies && $numOfPies > HostListItemDetailed::PIE_CHART_LIMIT
|
||||
) {
|
||||
$performanceData->addHtml(new HtmlElement('span', null, Text::create('…')));
|
||||
break;
|
||||
}
|
||||
|
||||
$performanceData->addHtml(HtmlString::create($pie));
|
||||
}
|
||||
}
|
||||
|
||||
if (! $statusIcons->isEmpty()) {
|
||||
$footer->addHtml($statusIcons);
|
||||
}
|
||||
|
||||
if (! $performanceData->isEmpty()) {
|
||||
$footer->addHtml($performanceData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\ListItemMinimalLayout;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
class HostListItemMinimal extends BaseHostListItem
|
||||
{
|
||||
use ListItemMinimalLayout;
|
||||
|
||||
protected function getStateBallSize(): string
|
||||
{
|
||||
return StateBall::SIZE_BIG;
|
||||
}
|
||||
}
|
||||
44
library/Icingadb/Widget/ItemList/LoadMoreObjectList.php
Normal file
44
library/Icingadb/Widget/ItemList/LoadMoreObjectList.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Exception\NotImplementedError;
|
||||
use Icinga\Module\Icingadb\Common\LoadMore;
|
||||
use Icinga\Module\Icingadb\Model\History;
|
||||
use Icinga\Module\Icingadb\Model\NotificationHistory;
|
||||
use Icinga\Module\Icingadb\View\EventRenderer;
|
||||
use Icinga\Module\Icingadb\View\NotificationRenderer;
|
||||
use ipl\Orm\Model;
|
||||
use ipl\Orm\ResultSet;
|
||||
use ipl\Web\Widget\ItemList;
|
||||
|
||||
/**
|
||||
* LoadMoreObjectList
|
||||
*
|
||||
* Create a list of icingadb objects with Load more link
|
||||
*
|
||||
* @template Item of NotificationHistory|History
|
||||
*
|
||||
* @extends ObjectList<Item>
|
||||
*/
|
||||
class LoadMoreObjectList extends ObjectList
|
||||
{
|
||||
use LoadMore;
|
||||
|
||||
public function __construct(ResultSet $data)
|
||||
{
|
||||
ItemList::__construct($data, function (Model $item) {
|
||||
if ($item instanceof NotificationHistory) {
|
||||
return new NotificationRenderer();
|
||||
} elseif ($item instanceof History) {
|
||||
return new EventRenderer();
|
||||
}
|
||||
|
||||
throw new NotImplementedError('Not implemented');
|
||||
});
|
||||
|
||||
$this->data = $this->getIterator($data);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\CaptionDisabled;
|
||||
use Icinga\Module\Icingadb\Common\DetailActions;
|
||||
use Icinga\Module\Icingadb\Common\LoadMore;
|
||||
use Icinga\Module\Icingadb\Common\NoSubjectLink;
|
||||
use Icinga\Module\Icingadb\Common\ViewMode;
|
||||
use ipl\Orm\ResultSet;
|
||||
use ipl\Web\Common\BaseItemList;
|
||||
use ipl\Web\Url;
|
||||
|
||||
class NotificationList extends BaseItemList
|
||||
{
|
||||
use CaptionDisabled;
|
||||
use NoSubjectLink;
|
||||
use ViewMode;
|
||||
use LoadMore;
|
||||
use DetailActions;
|
||||
|
||||
protected $defaultAttributes = ['class' => 'notification-list'];
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
/** @var ResultSet $data */
|
||||
$data = $this->data;
|
||||
$this->data = $this->getIterator($data);
|
||||
$this->initializeDetailActions();
|
||||
$this->setDetailUrl(Url::fromPath('icingadb/event'));
|
||||
}
|
||||
|
||||
protected function getItemClass(): string
|
||||
{
|
||||
switch ($this->getViewMode()) {
|
||||
case 'minimal':
|
||||
return NotificationListItemMinimal::class;
|
||||
case 'detailed':
|
||||
$this->removeAttribute('class', 'default-layout');
|
||||
|
||||
return NotificationListItemDetailed::class;
|
||||
default:
|
||||
return NotificationListItem::class;
|
||||
}
|
||||
}
|
||||
|
||||
protected function assemble(): void
|
||||
{
|
||||
$this->addAttributes(['class' => $this->getViewMode()]);
|
||||
|
||||
parent::assemble();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\ListItemCommonLayout;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
class NotificationListItem extends BaseNotificationListItem
|
||||
{
|
||||
use ListItemCommonLayout;
|
||||
|
||||
protected function getStateBallSize(): string
|
||||
{
|
||||
return StateBall::SIZE_LARGE;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\ListItemDetailedLayout;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
class NotificationListItemDetailed extends BaseNotificationListItem
|
||||
{
|
||||
use ListItemDetailedLayout;
|
||||
|
||||
protected function getStateBallSize(): string
|
||||
{
|
||||
return StateBall::SIZE_LARGE;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\ListItemMinimalLayout;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
class NotificationListItemMinimal extends BaseNotificationListItem
|
||||
{
|
||||
use ListItemMinimalLayout;
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
parent::init();
|
||||
|
||||
if ($this->list->isCaptionDisabled()) {
|
||||
$this->setCaptionDisabled();
|
||||
}
|
||||
}
|
||||
|
||||
protected function getStateBallSize(): string
|
||||
{
|
||||
return StateBall::SIZE_BIG;
|
||||
}
|
||||
}
|
||||
266
library/Icingadb/Widget/ItemList/ObjectList.php
Normal file
266
library/Icingadb/Widget/ItemList/ObjectList.php
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Exception\NotImplementedError;
|
||||
use Icinga\Module\Icingadb\Common\DetailActions;
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Model\Comment;
|
||||
use Icinga\Module\Icingadb\Model\DependencyNode;
|
||||
use Icinga\Module\Icingadb\Model\Downtime;
|
||||
use Icinga\Module\Icingadb\Model\History;
|
||||
use Icinga\Module\Icingadb\Model\Host;
|
||||
use Icinga\Module\Icingadb\Model\NotificationHistory;
|
||||
use Icinga\Module\Icingadb\Model\RedundancyGroup;
|
||||
use Icinga\Module\Icingadb\Model\Service;
|
||||
use Icinga\Module\Icingadb\Model\User;
|
||||
use Icinga\Module\Icingadb\Model\Usergroup;
|
||||
use Icinga\Module\Icingadb\Redis\VolatileStateResults;
|
||||
use Icinga\Module\Icingadb\View\CommentRenderer;
|
||||
use Icinga\Module\Icingadb\View\DowntimeRenderer;
|
||||
use Icinga\Module\Icingadb\View\HostRenderer;
|
||||
use Icinga\Module\Icingadb\View\RedundancyGroupRenderer;
|
||||
use Icinga\Module\Icingadb\View\ServiceRenderer;
|
||||
use Icinga\Module\Icingadb\View\UsergroupRenderer;
|
||||
use Icinga\Module\Icingadb\View\UserRenderer;
|
||||
use Icinga\Module\Icingadb\Widget\Notice;
|
||||
use InvalidArgumentException;
|
||||
use ipl\Html\HtmlDocument;
|
||||
use ipl\I18n\Translation;
|
||||
use ipl\Orm\Model;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Layout\DetailedItemLayout;
|
||||
use ipl\Web\Layout\ItemLayout;
|
||||
use ipl\Web\Layout\MinimalItemLayout;
|
||||
use ipl\Web\Url;
|
||||
use ipl\Web\Widget\ItemList;
|
||||
|
||||
/**
|
||||
* ObjectList
|
||||
*
|
||||
* Create a list of icingadb objects
|
||||
*
|
||||
* @template Result of DependencyNode|Service|Host|Usergroup|User|Comment|Downtime
|
||||
* @template Item of RedundancyGroup|Service|Host|Usergroup|User|Comment|Downtime = Result
|
||||
*
|
||||
* @extends ItemList<Result, Item>
|
||||
*/
|
||||
class ObjectList extends ItemList
|
||||
{
|
||||
use DetailActions;
|
||||
use Translation;
|
||||
|
||||
/** @var bool Whether the list contains at least one item with an icon_image */
|
||||
protected $hasIconImages = false;
|
||||
|
||||
public function __construct($data)
|
||||
{
|
||||
parent::__construct($data, function (Model $item) {
|
||||
if ($item instanceof RedundancyGroup) {
|
||||
return new RedundancyGroupRenderer();
|
||||
} elseif ($item instanceof Service) {
|
||||
return new ServiceRenderer();
|
||||
} elseif ($item instanceof Host) {
|
||||
return new HostRenderer();
|
||||
} elseif ($item instanceof Usergroup) {
|
||||
return new UsergroupRenderer();
|
||||
} elseif ($item instanceof User) {
|
||||
return new UserRenderer();
|
||||
} elseif ($item instanceof Comment) {
|
||||
return new CommentRenderer();
|
||||
} elseif ($item instanceof Downtime) {
|
||||
return new DowntimeRenderer();
|
||||
}
|
||||
|
||||
throw new NotImplementedError('Not implemented');
|
||||
});
|
||||
}
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
$this->initializeDetailActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the list contains at least one item with an icon_image
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasIconImages(): bool
|
||||
{
|
||||
return $this->hasIconImages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the list contains at least one item with an icon_image
|
||||
*
|
||||
* @param bool $hasIconImages
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setHasIconImages(bool $hasIconImages): self
|
||||
{
|
||||
$this->hasIconImages = $hasIconImages;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the view mode
|
||||
*
|
||||
* @param 'minimal'|'common'|'detailed' $mode
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setViewMode(string $mode): self
|
||||
{
|
||||
switch ($mode) {
|
||||
case 'minimal':
|
||||
$this->setItemLayoutClass(MinimalItemLayout::class);
|
||||
|
||||
break;
|
||||
case 'detailed':
|
||||
$this->setItemLayoutClass(DetailedItemLayout::class);
|
||||
|
||||
break;
|
||||
case 'common':
|
||||
$this->setItemLayoutClass(ItemLayout::class);
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException('Invalid view mode');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getItemLayout($item): ItemLayout
|
||||
{
|
||||
$layout = parent::getItemLayout($item);
|
||||
if ($this->hasIconImages()) {
|
||||
$layout->after(ItemLayout::VISUAL, 'icon-image');
|
||||
}
|
||||
|
||||
if ($item instanceof Downtime) {
|
||||
$layout->before(ItemLayout::HEADER, 'progress');
|
||||
}
|
||||
|
||||
return $layout;
|
||||
}
|
||||
|
||||
protected function createListItem(object $data)
|
||||
{
|
||||
if ($data instanceof DependencyNode) {
|
||||
if (isset($data->redundancy_group_id)) {
|
||||
$object = $data->redundancy_group;
|
||||
} else {
|
||||
$object = isset($data->service_id) ? $data->service : $data->host;
|
||||
}
|
||||
} else {
|
||||
$object = $data;
|
||||
}
|
||||
|
||||
if (isset($object->icon_image->icon_image)) {
|
||||
$this->setHasIconImages(true);
|
||||
}
|
||||
|
||||
$item = parent::createListItem($object);
|
||||
|
||||
if ($this->getDetailActionsDisabled()) {
|
||||
return $item;
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case $object instanceof RedundancyGroup:
|
||||
$this->addDetailFilterAttribute($item, Filter::equal('id', bin2hex($object->id)));
|
||||
|
||||
break;
|
||||
case $object instanceof Service:
|
||||
$this
|
||||
->setDetailUrl(Url::fromPath('icingadb/service'))
|
||||
->setMultiselectUrl(Links::servicesDetails())
|
||||
->addDetailFilterAttribute(
|
||||
$item,
|
||||
Filter::all(
|
||||
Filter::equal('name', $object->name),
|
||||
Filter::equal('host.name', $object->host->name)
|
||||
)
|
||||
);
|
||||
|
||||
$this->addMultiSelectFilterAttribute(
|
||||
$item,
|
||||
Filter::all(
|
||||
Filter::equal('service.name', $object->name),
|
||||
Filter::equal('host.name', $object->host->name)
|
||||
)
|
||||
);
|
||||
|
||||
break;
|
||||
case $object instanceof Host:
|
||||
$this
|
||||
->setDetailUrl(Url::fromPath('icingadb/host'))
|
||||
->setMultiselectUrl(Links::hostsDetails())
|
||||
->addDetailFilterAttribute($item, Filter::equal('name', $object->name))
|
||||
->addMultiSelectFilterAttribute($item, Filter::equal('host.name', $object->name));
|
||||
|
||||
break;
|
||||
|
||||
case $data instanceof User:
|
||||
$this
|
||||
->setDetailUrl(Url::fromPath('icingadb/user'))
|
||||
->addDetailFilterAttribute($item, Filter::equal('name', $object->name));
|
||||
|
||||
break;
|
||||
case $object instanceof Usergroup:
|
||||
$this
|
||||
->setDetailUrl(Url::fromPath('icingadb/usergroup'))
|
||||
->addDetailFilterAttribute($item, Filter::equal('name', $object->name));
|
||||
|
||||
break;
|
||||
case $object instanceof Comment:
|
||||
$this
|
||||
->setDetailUrl(Url::fromPath('icingadb/comment'))
|
||||
->setMultiselectUrl(Links::commentsDetails())
|
||||
->addDetailFilterAttribute($item, Filter::equal('name', $object->name))
|
||||
->addMultiSelectFilterAttribute($item, Filter::equal('name', $object->name));
|
||||
|
||||
break;
|
||||
case $object instanceof Downtime:
|
||||
$this
|
||||
->setDetailUrl(Url::fromPath('icingadb/downtime'))
|
||||
->setMultiselectUrl(Links::downtimesDetails())
|
||||
->addDetailFilterAttribute($item, Filter::equal('name', $object->name))
|
||||
->addMultiSelectFilterAttribute($item, Filter::equal('name', $object->name));
|
||||
|
||||
break;
|
||||
case $object instanceof NotificationHistory:
|
||||
$this
|
||||
->setDetailUrl(Url::fromPath('icingadb/event'))
|
||||
->addDetailFilterAttribute($item, Filter::equal('id', bin2hex($object->history->id)));
|
||||
|
||||
break;
|
||||
|
||||
case $object instanceof History:
|
||||
$this
|
||||
->setDetailUrl(Url::fromPath('icingadb/event'))
|
||||
->addDetailFilterAttribute($item, Filter::equal('id', bin2hex($object->id)));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
protected function assemble(): void
|
||||
{
|
||||
parent::assemble();
|
||||
|
||||
if ($this->data instanceof VolatileStateResults && $this->data->isRedisUnavailable()) {
|
||||
$this->prependWrapper((new HtmlDocument())->addHtml(new Notice(
|
||||
$this->translate('Redis is currently unavailable. The shown information might be outdated.')
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2024 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\Auth;
|
||||
use Icinga\Module\Icingadb\Common\Database;
|
||||
use Icinga\Module\Icingadb\Common\ListItemCommonLayout;
|
||||
use Icinga\Module\Icingadb\Model\RedundancyGroup;
|
||||
use Icinga\Module\Icingadb\Model\RedundancyGroupSummary;
|
||||
use Icinga\Module\Icingadb\Model\RedundancyGroupState;
|
||||
use Icinga\Module\Icingadb\Widget\DependencyNodeStatistics;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\Html;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Url;
|
||||
use ipl\Web\Widget\Link;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\Web\Widget\TimeSince;
|
||||
|
||||
/**
|
||||
* Redundancy group list item. Represents one database row.
|
||||
*
|
||||
* @property RedundancyGroup $item
|
||||
* @property RedundancyGroupState $state
|
||||
*/
|
||||
class RedundancyGroupListItem extends StateListItem
|
||||
{
|
||||
use ListItemCommonLayout;
|
||||
use Database;
|
||||
use Auth;
|
||||
|
||||
protected $defaultAttributes = ['class' => ['redundancy-group-list-item']];
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
parent::init();
|
||||
|
||||
$this->addAttributes(['data-action-item' => true]);
|
||||
}
|
||||
|
||||
protected function getStateBallSize(): string
|
||||
{
|
||||
return StateBall::SIZE_LARGE;
|
||||
}
|
||||
|
||||
protected function createTimestamp(): BaseHtmlElement
|
||||
{
|
||||
return new TimeSince($this->state->last_state_change->getTimestamp());
|
||||
}
|
||||
|
||||
protected function createSubject(): Link
|
||||
{
|
||||
return new Link(
|
||||
$this->item->display_name,
|
||||
Url::fromPath('icingadb/redundancygroup', ['id' => bin2hex($this->item->id)]),
|
||||
['class' => 'subject']
|
||||
);
|
||||
}
|
||||
|
||||
protected function assembleVisual(BaseHtmlElement $visual): void
|
||||
{
|
||||
$stateBall = new StateBall($this->state->getStateText(), $this->getStateBallSize());
|
||||
$stateBall->add($this->state->getIcon());
|
||||
|
||||
$visual->addHtml($stateBall);
|
||||
}
|
||||
|
||||
protected function assembleCaption(BaseHtmlElement $caption): void
|
||||
{
|
||||
$summary = RedundancyGroupSummary::on($this->getDb())
|
||||
->filter(Filter::equal('id', $this->item->id));
|
||||
|
||||
$this->applyRestrictions($summary);
|
||||
|
||||
$caption->addHtml(new DependencyNodeStatistics($summary->first()));
|
||||
}
|
||||
|
||||
protected function assembleTitle(BaseHtmlElement $title): void
|
||||
{
|
||||
$subject = $this->createSubject();
|
||||
if ($this->state->failed) {
|
||||
$title->addHtml(Html::sprintf(
|
||||
$this->translate('%s has no working objects', '<groupname> has ...'),
|
||||
$subject
|
||||
));
|
||||
} else {
|
||||
$title->addHtml(Html::sprintf(
|
||||
$this->translate('%s has working objects', '<groupname> has ...'),
|
||||
$subject
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
protected function assemble(): void
|
||||
{
|
||||
$this->add([
|
||||
$this->createVisual(),
|
||||
$this->createIconImage(),
|
||||
$this->createMain()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2024 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\ListItemMinimalLayout;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
class RedundancyGroupListItemMinimal extends RedundancyGroupListItem
|
||||
{
|
||||
use ListItemMinimalLayout;
|
||||
|
||||
protected function getStateBallSize(): string
|
||||
{
|
||||
return StateBall::SIZE_BIG;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\ServiceStates;
|
||||
use Icinga\Module\Icingadb\Widget\StateChange;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
class ServiceDetailHeader extends ServiceListItemMinimal
|
||||
{
|
||||
protected function getStateBallSize(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
protected function assembleVisual(BaseHtmlElement $visual): void
|
||||
{
|
||||
if ($this->state->state_type === 'soft') {
|
||||
$stateType = 'soft_state';
|
||||
$previousStateType = 'previous_soft_state';
|
||||
|
||||
if ($this->state->previous_soft_state === 0) {
|
||||
$previousStateType = 'hard_state';
|
||||
}
|
||||
} else {
|
||||
$stateType = 'hard_state';
|
||||
$previousStateType = 'previous_hard_state';
|
||||
|
||||
if ($this->state->hard_state === $this->state->previous_hard_state) {
|
||||
$previousStateType = 'previous_soft_state';
|
||||
}
|
||||
}
|
||||
|
||||
$state = ServiceStates::text($this->state->$stateType);
|
||||
$previousState = ServiceStates::text($this->state->$previousStateType);
|
||||
|
||||
$stateChange = new StateChange($state, $previousState);
|
||||
if ($stateType === 'soft_state') {
|
||||
$stateChange->setCurrentStateBallSize(StateBall::SIZE_MEDIUM_LARGE);
|
||||
}
|
||||
|
||||
if ($previousStateType === 'previous_soft_state') {
|
||||
$stateChange->setPreviousStateBallSize(StateBall::SIZE_MEDIUM_LARGE);
|
||||
if ($stateType === 'soft_state') {
|
||||
$visual->getAttributes()->add('class', 'small-state-change');
|
||||
}
|
||||
}
|
||||
|
||||
$stateChange->setIcon($this->state->getIcon());
|
||||
$stateChange->setHandled(
|
||||
$this->state->is_problem && ($this->state->is_handled || ! $this->state->is_reachable)
|
||||
);
|
||||
|
||||
$visual->addHtml($stateChange);
|
||||
}
|
||||
|
||||
protected function assemble(): void
|
||||
{
|
||||
$attributes = $this->list->getAttributes();
|
||||
if (! in_array('minimal', $attributes->get('class')->getValue())) {
|
||||
$attributes->add('class', 'minimal');
|
||||
}
|
||||
|
||||
parent::assemble();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use ipl\Web\Url;
|
||||
|
||||
class ServiceList extends StateList
|
||||
{
|
||||
protected $defaultAttributes = ['class' => 'service-list'];
|
||||
|
||||
protected function getItemClass(): string
|
||||
{
|
||||
switch ($this->getViewMode()) {
|
||||
case 'minimal':
|
||||
return ServiceListItemMinimal::class;
|
||||
case 'detailed':
|
||||
$this->removeAttribute('class', 'default-layout');
|
||||
|
||||
return ServiceListItemDetailed::class;
|
||||
case 'objectHeader':
|
||||
return ServiceDetailHeader::class;
|
||||
default:
|
||||
return ServiceListItem::class;
|
||||
}
|
||||
}
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
$this->initializeDetailActions();
|
||||
$this->setMultiselectUrl(Links::servicesDetails());
|
||||
$this->setDetailUrl(Url::fromPath('icingadb/service'));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\ListItemCommonLayout;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
class ServiceListItem extends BaseServiceListItem
|
||||
{
|
||||
use ListItemCommonLayout;
|
||||
|
||||
protected function getStateBallSize(): string
|
||||
{
|
||||
return StateBall::SIZE_LARGE;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\ListItemDetailedLayout;
|
||||
use Icinga\Module\Icingadb\Util\PerfDataSet;
|
||||
use Icinga\Module\Icingadb\Widget\ItemList\CommentList;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\HtmlString;
|
||||
use ipl\Html\Text;
|
||||
use ipl\Web\Widget\Icon;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
class ServiceListItemDetailed extends BaseServiceListItem
|
||||
{
|
||||
use ListItemDetailedLayout;
|
||||
|
||||
/** @var int Max pie charts to be shown */
|
||||
const PIE_CHART_LIMIT = 5;
|
||||
|
||||
protected function getStateBallSize(): string
|
||||
{
|
||||
return StateBall::SIZE_LARGE;
|
||||
}
|
||||
|
||||
protected function assembleFooter(BaseHtmlElement $footer): void
|
||||
{
|
||||
$statusIcons = new HtmlElement('div', Attributes::create(['class' => 'status-icons']));
|
||||
|
||||
if ($this->item->state->last_comment->service_id === $this->item->id) {
|
||||
$comment = $this->item->state->last_comment;
|
||||
$comment->service = $this->item;
|
||||
$comment = (new CommentList([$comment]))
|
||||
->setNoSubjectLink()
|
||||
->setObjectLinkDisabled()
|
||||
->setDetailActionsDisabled();
|
||||
|
||||
$statusIcons->addHtml(
|
||||
new HtmlElement(
|
||||
'div',
|
||||
Attributes::create(['class' => 'comment-wrapper']),
|
||||
new HtmlElement('div', Attributes::create(['class' => 'comment-popup']), $comment),
|
||||
(new Icon('comments', ['class' => 'comment-icon']))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->item->state->is_flapping) {
|
||||
$statusIcons->addHtml(new Icon(
|
||||
'random',
|
||||
[
|
||||
'title' => sprintf(
|
||||
t('Service "%s" on "%s" is in flapping state'),
|
||||
$this->item->display_name,
|
||||
$this->item->host->display_name
|
||||
),
|
||||
]
|
||||
));
|
||||
}
|
||||
|
||||
if (! $this->item->notifications_enabled) {
|
||||
$statusIcons->addHtml(new Icon('bell-slash', ['title' => t('Notifications disabled')]));
|
||||
}
|
||||
|
||||
if (! $this->item->active_checks_enabled) {
|
||||
$statusIcons->addHtml(new Icon('eye-slash', ['title' => t('Active checks disabled')]));
|
||||
}
|
||||
|
||||
$performanceData = new HtmlElement('div', Attributes::create(['class' => 'performance-data']));
|
||||
if ($this->item->state->performance_data) {
|
||||
$pieChartData = PerfDataSet::fromString($this->item->state->normalized_performance_data)->asArray();
|
||||
|
||||
$pies = [];
|
||||
foreach ($pieChartData as $i => $perfdata) {
|
||||
if ($perfdata->isVisualizable()) {
|
||||
$pies[] = $perfdata->asInlinePie()->render();
|
||||
}
|
||||
|
||||
// Check if number of visualizable pie charts is larger than PIE_CHART_LIMIT
|
||||
if (count($pies) > ServiceListItemDetailed::PIE_CHART_LIMIT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$maxVisiblePies = ServiceListItemDetailed::PIE_CHART_LIMIT - 2;
|
||||
$numOfPies = count($pies);
|
||||
foreach ($pies as $i => $pie) {
|
||||
if (
|
||||
// Show max. 5 elements: if there are more than 5, show 4 + `…`
|
||||
$i > $maxVisiblePies && $numOfPies > ServiceListItemDetailed::PIE_CHART_LIMIT
|
||||
) {
|
||||
$performanceData->addHtml(new HtmlElement('span', null, Text::create('…')));
|
||||
break;
|
||||
}
|
||||
|
||||
$performanceData->addHtml(HtmlString::create($pie));
|
||||
}
|
||||
}
|
||||
|
||||
if (! $statusIcons->isEmpty()) {
|
||||
$footer->addHtml($statusIcons);
|
||||
}
|
||||
|
||||
if (! $performanceData->isEmpty()) {
|
||||
$footer->addHtml($performanceData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\ListItemMinimalLayout;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
class ServiceListItemMinimal extends BaseServiceListItem
|
||||
{
|
||||
use ListItemMinimalLayout;
|
||||
|
||||
protected function getStateBallSize(): string
|
||||
{
|
||||
return StateBall::SIZE_BIG;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\DetailActions;
|
||||
use Icinga\Module\Icingadb\Common\NoSubjectLink;
|
||||
use Icinga\Module\Icingadb\Common\ViewMode;
|
||||
use Icinga\Module\Icingadb\Redis\VolatileStateResults;
|
||||
use Icinga\Module\Icingadb\Widget\Notice;
|
||||
use ipl\Html\HtmlDocument;
|
||||
use ipl\Web\Common\BaseItemList;
|
||||
|
||||
abstract class StateList extends BaseItemList
|
||||
{
|
||||
use ViewMode;
|
||||
use NoSubjectLink;
|
||||
use DetailActions;
|
||||
|
||||
/** @var bool Whether the list contains at least one item with an icon_image */
|
||||
protected $hasIconImages = false;
|
||||
|
||||
/**
|
||||
* Get whether the list contains at least one item with an icon_image
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasIconImages(): bool
|
||||
{
|
||||
return $this->hasIconImages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the list contains at least one item with an icon_image
|
||||
*
|
||||
* @param bool $hasIconImages
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setHasIconImages(bool $hasIconImages): self
|
||||
{
|
||||
$this->hasIconImages = $hasIconImages;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function assemble(): void
|
||||
{
|
||||
$this->addAttributes(['class' => $this->getViewMode()]);
|
||||
|
||||
parent::assemble();
|
||||
|
||||
if ($this->data instanceof VolatileStateResults && $this->data->isRedisUnavailable()) {
|
||||
$this->prependWrapper((new HtmlDocument())->addHtml(new Notice(
|
||||
t('Redis is currently unavailable. The shown information might be outdated.')
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,174 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\Icons;
|
||||
use Icinga\Module\Icingadb\Model\State;
|
||||
use Icinga\Module\Icingadb\Util\PluginOutput;
|
||||
use Icinga\Module\Icingadb\Widget\CheckAttempt;
|
||||
use Icinga\Module\Icingadb\Widget\IconImage;
|
||||
use Icinga\Module\Icingadb\Widget\PluginOutputContainer;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\I18n\Translation;
|
||||
use ipl\Web\Common\BaseListItem;
|
||||
use ipl\Web\Widget\EmptyState;
|
||||
use ipl\Web\Widget\TimeSince;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\Html;
|
||||
use ipl\Html\Text;
|
||||
use ipl\Web\Widget\Icon;
|
||||
use ipl\Web\Widget\StateBall;
|
||||
|
||||
/**
|
||||
* Host or service item of a host or service list. Represents one database row.
|
||||
*/
|
||||
abstract class StateListItem extends BaseListItem
|
||||
{
|
||||
use Translation;
|
||||
|
||||
/** @var StateList The list where the item is part of */
|
||||
protected $list;
|
||||
|
||||
/** @var State The state of the item */
|
||||
protected $state;
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
$this->state = $this->item->state;
|
||||
|
||||
if (isset($this->item->icon_image->icon_image)) {
|
||||
$this->list->setHasIconImages(true);
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected function createSubject();
|
||||
|
||||
abstract protected function getStateBallSize(): string;
|
||||
|
||||
/**
|
||||
* @return ?BaseHtmlElement
|
||||
*/
|
||||
protected function createIconImage(): ?BaseHtmlElement
|
||||
{
|
||||
if (! $this->list->hasIconImages()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$iconImage = HtmlElement::create('div', [
|
||||
'class' => 'icon-image',
|
||||
]);
|
||||
|
||||
$this->assembleIconImage($iconImage);
|
||||
|
||||
return $iconImage;
|
||||
}
|
||||
|
||||
protected function assembleCaption(BaseHtmlElement $caption): void
|
||||
{
|
||||
if ($this->state->soft_state === null && $this->state->output === null) {
|
||||
$caption->addHtml(Text::create($this->translate('Waiting for Icinga DB to synchronize the state.')));
|
||||
} else {
|
||||
if (empty($this->state->output)) {
|
||||
$pluginOutput = new EmptyState($this->translate('Output unavailable.'));
|
||||
} else {
|
||||
$pluginOutput = new PluginOutputContainer(PluginOutput::fromObject($this->item));
|
||||
}
|
||||
|
||||
$caption->addHtml($pluginOutput);
|
||||
}
|
||||
}
|
||||
|
||||
protected function assembleIconImage(BaseHtmlElement $iconImage): void
|
||||
{
|
||||
if (isset($this->item->icon_image->icon_image)) {
|
||||
$iconImage->addHtml(new IconImage($this->item->icon_image->icon_image, $this->item->icon_image_alt));
|
||||
} else {
|
||||
$iconImage->addAttributes(['class' => 'placeholder']);
|
||||
}
|
||||
}
|
||||
|
||||
protected function assembleTitle(BaseHtmlElement $title): void
|
||||
{
|
||||
$title->addHtml(Html::sprintf(
|
||||
$this->translate('%s is %s', '<hostname> is <state-text>'),
|
||||
$this->createSubject(),
|
||||
Html::tag('span', ['class' => 'state-text'], $this->state->getStateTextTranslated())
|
||||
));
|
||||
|
||||
if (isset($this->state->affects_children) && $this->state->affects_children) {
|
||||
$total = (int) $this->item->total_children;
|
||||
|
||||
if ($total > 1000) {
|
||||
$total = '1000+';
|
||||
$tooltip = $this->translate('Up to 1000+ affected objects');
|
||||
} else {
|
||||
$tooltip = sprintf(
|
||||
$this->translatePlural(
|
||||
'%d affected object',
|
||||
'Up to %d affected objects',
|
||||
$total
|
||||
),
|
||||
$total
|
||||
);
|
||||
}
|
||||
|
||||
$icon = new Icon(Icons::UNREACHABLE);
|
||||
|
||||
$title->addHtml(new HtmlElement(
|
||||
'span',
|
||||
Attributes::create([
|
||||
'class' => 'affected-objects',
|
||||
'title' => $tooltip
|
||||
]),
|
||||
$icon,
|
||||
Text::create($total)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
protected function assembleVisual(BaseHtmlElement $visual): void
|
||||
{
|
||||
$stateBall = new StateBall($this->state->getStateText(), $this->getStateBallSize());
|
||||
$stateBall->add($this->state->getIcon());
|
||||
if ($this->state->is_problem && ($this->state->is_handled || ! $this->state->is_reachable)) {
|
||||
$stateBall->getAttributes()->add('class', 'handled');
|
||||
}
|
||||
|
||||
$visual->addHtml($stateBall);
|
||||
if ($this->state->state_type === 'soft') {
|
||||
$visual->addHtml(
|
||||
new CheckAttempt((int) $this->state->check_attempt, (int) $this->item->max_check_attempts)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function createTimestamp(): ?BaseHtmlElement
|
||||
{
|
||||
$since = null;
|
||||
if ($this->state->is_overdue) {
|
||||
$since = new TimeSince($this->state->next_update->getTimestamp());
|
||||
$since->prepend($this->translate('Overdue') . ' ');
|
||||
$since->prependHtml(new Icon(Icons::WARNING));
|
||||
} elseif ($this->state->last_state_change !== null && $this->state->last_state_change->getTimestamp() > 0) {
|
||||
$since = new TimeSince($this->state->last_state_change->getTimestamp());
|
||||
}
|
||||
|
||||
return $since;
|
||||
}
|
||||
|
||||
protected function assemble(): void
|
||||
{
|
||||
if ($this->state->is_overdue) {
|
||||
$this->addAttributes(['class' => 'overdue']);
|
||||
}
|
||||
|
||||
$this->add([
|
||||
$this->createVisual(),
|
||||
$this->createIconImage(),
|
||||
$this->createMain()
|
||||
]);
|
||||
}
|
||||
}
|
||||
38
library/Icingadb/Widget/ItemList/TicketLinkObjectList.php
Normal file
38
library/Icingadb/Widget/ItemList/TicketLinkObjectList.php
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemList;
|
||||
|
||||
use Icinga\Exception\NotImplementedError;
|
||||
use Icinga\Module\Icingadb\Model\Comment;
|
||||
use Icinga\Module\Icingadb\Model\Downtime;
|
||||
use Icinga\Module\Icingadb\View\CommentRenderer;
|
||||
use Icinga\Module\Icingadb\View\DowntimeRenderer;
|
||||
use ipl\Orm\Model;
|
||||
use ipl\Web\Widget\ItemList;
|
||||
|
||||
/**
|
||||
* TicketLinkObjectList
|
||||
*
|
||||
* Create a list of icingadb objects with ticket links
|
||||
*
|
||||
* @template Result of Comment|Downtime
|
||||
*
|
||||
* @extends ObjectList<Result>
|
||||
*/
|
||||
class TicketLinkObjectList extends ObjectList
|
||||
{
|
||||
public function __construct($data)
|
||||
{
|
||||
ItemList::__construct($data, function (Model $item) {
|
||||
if ($item instanceof Comment) {
|
||||
return (new CommentRenderer())->setNoObjectLink();
|
||||
} elseif ($item instanceof Downtime) {
|
||||
return (new DowntimeRenderer())->setNoObjectLink();
|
||||
}
|
||||
|
||||
throw new NotImplementedError('Not implemented');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2023 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemTable;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Model\Hostgroupsummary;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\I18n\Translation;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Common\BaseTableRowItem;
|
||||
use ipl\Web\Widget\Link;
|
||||
|
||||
/**
|
||||
* Hostgroup item of a hostgroup list. Represents one database row.
|
||||
*
|
||||
* @property Hostgroupsummary $item
|
||||
* @property HostgroupTable $table
|
||||
*/
|
||||
abstract class BaseHostGroupItem extends BaseTableRowItem
|
||||
{
|
||||
use Translation;
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
if (isset($this->table)) {
|
||||
$this->table->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name));
|
||||
}
|
||||
}
|
||||
|
||||
protected function createSubject(): BaseHtmlElement
|
||||
{
|
||||
if (isset($this->table)) {
|
||||
$link = new Link(
|
||||
$this->item->display_name,
|
||||
Links::hostgroup($this->item),
|
||||
[
|
||||
'class' => 'subject',
|
||||
'title' => sprintf(
|
||||
$this->translate('List all hosts in the group "%s"'),
|
||||
$this->item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
if ($this->table->hasBaseFilter()) {
|
||||
$link->getUrl()->setFilter($this->table->getBaseFilter());
|
||||
}
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
return new HtmlElement(
|
||||
'span',
|
||||
Attributes::create(['class' => 'subject']),
|
||||
Text::create($this->item->display_name)
|
||||
);
|
||||
}
|
||||
|
||||
protected function createCaption(): BaseHtmlElement
|
||||
{
|
||||
return new HtmlElement('span', null, Text::create($this->item->name));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2023 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemTable;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\Links;
|
||||
use Icinga\Module\Icingadb\Model\ServicegroupSummary;
|
||||
use ipl\Html\Attributes;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\Text;
|
||||
use ipl\I18n\Translation;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Common\BaseTableRowItem;
|
||||
use ipl\Web\Widget\Link;
|
||||
|
||||
/**
|
||||
* Servicegroup item of a servicegroup list. Represents one database row.
|
||||
*
|
||||
* @property ServicegroupSummary $item
|
||||
* @property ServicegroupTable $table
|
||||
*/
|
||||
abstract class BaseServiceGroupItem extends BaseTableRowItem
|
||||
{
|
||||
use Translation;
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
if (isset($this->table)) {
|
||||
$this->table->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name));
|
||||
}
|
||||
}
|
||||
|
||||
protected function createSubject(): BaseHtmlElement
|
||||
{
|
||||
if (isset($this->table)) {
|
||||
$link = new Link(
|
||||
$this->item->display_name,
|
||||
Links::servicegroup($this->item),
|
||||
[
|
||||
'class' => 'subject',
|
||||
'title' => sprintf(
|
||||
$this->translate('List all services in the group "%s"'),
|
||||
$this->item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
if ($this->table->hasBaseFilter()) {
|
||||
$link->getUrl()->setFilter($this->table->getBaseFilter());
|
||||
}
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
return new HtmlElement(
|
||||
'span',
|
||||
Attributes::create(['class' => 'subject']),
|
||||
Text::create($this->item->display_name)
|
||||
);
|
||||
}
|
||||
|
||||
protected function createCaption(): BaseHtmlElement
|
||||
{
|
||||
return new HtmlElement('span', null, Text::create($this->item->name));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2023 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemTable;
|
||||
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Web\Widget\Link;
|
||||
|
||||
trait GridCellLayout
|
||||
{
|
||||
/**
|
||||
* Creates a state badge for the Host / Service group with the highest severity that an object in the group has,
|
||||
* along with the count of the objects with this severity belonging to the corresponding group.
|
||||
*
|
||||
* @return Link
|
||||
*/
|
||||
abstract public function createGroupBadge(): Link;
|
||||
|
||||
protected function assembleVisual(BaseHtmlElement $visual): void
|
||||
{
|
||||
$visual->add($this->createGroupBadge());
|
||||
}
|
||||
|
||||
protected function assembleTitle(BaseHtmlElement $title): void
|
||||
{
|
||||
$title->addHtml(
|
||||
$this->createSubject(),
|
||||
$this->createCaption()
|
||||
);
|
||||
}
|
||||
|
||||
protected function assemble(): void
|
||||
{
|
||||
$this->add([
|
||||
$this->createTitle()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2023 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemTable;
|
||||
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Url;
|
||||
use ipl\Web\Widget\Link;
|
||||
use ipl\Web\Widget\StateBadge;
|
||||
|
||||
class HostgroupGridCell extends BaseHostGroupItem
|
||||
{
|
||||
use GridCellLayout;
|
||||
|
||||
protected $defaultAttributes = ['class' => ['group-grid-cell', 'hostgroup-grid-cell']];
|
||||
|
||||
protected function createGroupBadge(): Link
|
||||
{
|
||||
$url = Url::fromPath('icingadb/hosts');
|
||||
$urlFilter = Filter::all(Filter::equal('hostgroup.name', $this->item->name));
|
||||
|
||||
if ($this->item->hosts_down_unhandled > 0) {
|
||||
$urlFilter->add(Filter::equal('host.state.soft_state', 1))
|
||||
->add(Filter::equal('host.state.is_handled', 'n'))
|
||||
->add(Filter::equal('host.state.is_reachable', 'y'));
|
||||
|
||||
return new Link(
|
||||
new StateBadge($this->item->hosts_down_unhandled, 'down'),
|
||||
$url->setFilter($urlFilter),
|
||||
[
|
||||
'title' => sprintf(
|
||||
$this->translatePlural(
|
||||
'List %d host that is currently in DOWN state in host group "%s"',
|
||||
'List %d hosts which are currently in DOWN state in host group "%s"',
|
||||
$this->item->hosts_down_unhandled
|
||||
),
|
||||
$this->item->hosts_down_unhandled,
|
||||
$this->item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
} elseif ($this->item->hosts_down_handled > 0) {
|
||||
$urlFilter->add(Filter::equal('host.state.soft_state', 1))
|
||||
->add(Filter::any(
|
||||
Filter::equal('host.state.is_handled', 'y'),
|
||||
Filter::equal('host.state.is_reachable', 'n')
|
||||
));
|
||||
|
||||
return new Link(
|
||||
new StateBadge($this->item->hosts_down_handled, 'down', true),
|
||||
$url->setFilter($urlFilter),
|
||||
[
|
||||
'title' => sprintf(
|
||||
$this->translatePlural(
|
||||
'List %d host that is currently in DOWN (Acknowledged) state in host group "%s"',
|
||||
'List %d hosts which are currently in DOWN (Acknowledged) state in host group "%s"',
|
||||
$this->item->hosts_down_handled
|
||||
),
|
||||
$this->item->hosts_down_handled,
|
||||
$this->item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
} elseif ($this->item->hosts_pending > 0) {
|
||||
$urlFilter->add(Filter::equal('host.state.soft_state', 99));
|
||||
|
||||
return new Link(
|
||||
new StateBadge($this->item->hosts_pending, 'pending'),
|
||||
$url->setFilter($urlFilter),
|
||||
[
|
||||
'title' => sprintf(
|
||||
$this->translatePlural(
|
||||
'List %d host that is currently in PENDING state in host group "%s"',
|
||||
'List %d hosts which are currently in PENDING state in host group "%s"',
|
||||
$this->item->hosts_pending
|
||||
),
|
||||
$this->item->hosts_pending,
|
||||
$this->item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
} elseif ($this->item->hosts_up > 0) {
|
||||
$urlFilter->add(Filter::equal('host.state.soft_state', 0));
|
||||
|
||||
return new Link(
|
||||
new StateBadge($this->item->hosts_up, 'up'),
|
||||
$url->setFilter($urlFilter),
|
||||
[
|
||||
'title' => sprintf(
|
||||
$this->translatePlural(
|
||||
'List %d host that is currently in UP state in host group "%s"',
|
||||
'List %d hosts which are currently in UP state in host group "%s"',
|
||||
$this->item->hosts_up
|
||||
),
|
||||
$this->item->hosts_up,
|
||||
$this->item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return new Link(
|
||||
new StateBadge(0, 'none'),
|
||||
$url,
|
||||
[
|
||||
'title' => sprintf(
|
||||
$this->translate('There are no hosts in host group "%s"'),
|
||||
$this->item->display_name
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemTable;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\DetailActions;
|
||||
use Icinga\Module\Icingadb\Common\ViewMode;
|
||||
use ipl\Web\Common\BaseItemTable;
|
||||
use ipl\Web\Url;
|
||||
|
||||
class HostgroupTable extends BaseItemTable
|
||||
{
|
||||
use DetailActions;
|
||||
use ViewMode;
|
||||
|
||||
protected $defaultAttributes = ['class' => 'hostgroup-table'];
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
$this->initializeDetailActions();
|
||||
$this->setDetailUrl(Url::fromPath('icingadb/hostgroup'));
|
||||
}
|
||||
|
||||
protected function getLayout(): string
|
||||
{
|
||||
return $this->getViewMode() === 'grid'
|
||||
? 'group-grid'
|
||||
: parent::getLayout();
|
||||
}
|
||||
|
||||
protected function getItemClass(): string
|
||||
{
|
||||
return $this->getViewMode() === 'grid'
|
||||
? HostgroupGridCell::class
|
||||
: HostgroupTableRow::class;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemTable;
|
||||
|
||||
use Icinga\Module\Icingadb\Model\Hostgroupsummary;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\HostStatistics;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\ServiceStatistics;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Stdlib\Filter;
|
||||
|
||||
/**
|
||||
* Hostgroup table row of a hostgroup table. Represents one database row.
|
||||
*
|
||||
* @property Hostgroupsummary $item
|
||||
* @property HostgroupTable $table
|
||||
*/
|
||||
class HostgroupTableRow extends BaseHostGroupItem
|
||||
{
|
||||
use TableRowLayout;
|
||||
|
||||
protected $defaultAttributes = ['class' => 'hostgroup-table-row'];
|
||||
|
||||
/**
|
||||
* Create Host and service statistics columns
|
||||
*
|
||||
* @return BaseHtmlElement[]
|
||||
*/
|
||||
protected function createStatistics(): array
|
||||
{
|
||||
$hostStats = new HostStatistics($this->item);
|
||||
|
||||
$hostStats->setBaseFilter(Filter::equal('hostgroup.name', $this->item->name));
|
||||
if (isset($this->table) && $this->table->hasBaseFilter()) {
|
||||
$hostStats->setBaseFilter(
|
||||
Filter::all($hostStats->getBaseFilter(), $this->table->getBaseFilter())
|
||||
);
|
||||
}
|
||||
|
||||
$serviceStats = new ServiceStatistics($this->item);
|
||||
|
||||
$serviceStats->setBaseFilter(Filter::equal('hostgroup.name', $this->item->name));
|
||||
if (isset($this->table) && $this->table->hasBaseFilter()) {
|
||||
$serviceStats->setBaseFilter(
|
||||
Filter::all($serviceStats->getBaseFilter(), $this->table->getBaseFilter())
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
$this->createColumn($hostStats),
|
||||
$this->createColumn($serviceStats)
|
||||
];
|
||||
}
|
||||
}
|
||||
113
library/Icingadb/Widget/ItemTable/ObjectGrid.php
Normal file
113
library/Icingadb/Widget/ItemTable/ObjectGrid.php
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemTable;
|
||||
|
||||
use Icinga\Exception\NotImplementedError;
|
||||
use Icinga\Module\Icingadb\Common\DetailActions;
|
||||
use Icinga\Module\Icingadb\Model\Hostgroupsummary;
|
||||
use Icinga\Module\Icingadb\Model\ServicegroupSummary;
|
||||
use InvalidArgumentException;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\HtmlElement;
|
||||
use ipl\Html\ValidHtml;
|
||||
use ipl\Orm\ResultSet;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Common\ItemRenderer;
|
||||
use ipl\Web\Layout\ItemLayout;
|
||||
use ipl\Web\Url;
|
||||
use ipl\Web\Widget\EmptyStateBar;
|
||||
|
||||
/**
|
||||
* ObjectGrid
|
||||
*
|
||||
* @internal The only reason this class exists is due to the detail actions. In case those are part of the ipl
|
||||
* some time, this class is obsolete, and we must be able to safely drop it.
|
||||
*
|
||||
* @template Item of Hostgroupsummary|ServicegroupSummary
|
||||
*/
|
||||
class ObjectGrid extends BaseHtmlElement
|
||||
{
|
||||
use DetailActions;
|
||||
|
||||
protected $defaultAttributes = [
|
||||
'class' => 'object-grid',
|
||||
'data-base-target' => '_next'
|
||||
];
|
||||
|
||||
protected $tag = 'ul';
|
||||
|
||||
/** @var ItemRenderer<Item> */
|
||||
protected $itemRenderer;
|
||||
|
||||
/** @var ResultSet|iterable<Item> */
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* Create a new object grid
|
||||
*
|
||||
* @param ResultSet|iterable<Item> $data
|
||||
* @param ItemRenderer<Item> $renderer
|
||||
*/
|
||||
public function __construct($data, ItemRenderer $renderer)
|
||||
{
|
||||
if (! is_iterable($data)) {
|
||||
throw new InvalidArgumentException('Data must be an array or an instance of Traversable');
|
||||
}
|
||||
|
||||
$this->data = $data;
|
||||
$this->itemRenderer = $renderer;
|
||||
|
||||
$this->initializeDetailActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a list item for the given data
|
||||
*
|
||||
* @param Item $data
|
||||
*
|
||||
* @return ValidHtml
|
||||
*
|
||||
* @throws NotImplementedError When the data is not of the expected type
|
||||
*/
|
||||
protected function createListItem(object $data): ValidHtml
|
||||
{
|
||||
$layout = new ItemLayout($data, $this->itemRenderer);
|
||||
$item = new HtmlElement('li', $layout->getAttributes(), $layout);
|
||||
|
||||
if ($this->getDetailActionsDisabled()) {
|
||||
return $item;
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case $data instanceof Hostgroupsummary:
|
||||
$this->setDetailUrl(Url::fromPath('icingadb/hostgroup'));
|
||||
|
||||
break;
|
||||
case $data instanceof ServicegroupSummary:
|
||||
$this->setDetailUrl(Url::fromPath('icingadb/servicegroup'));
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedError('Not implemented');
|
||||
}
|
||||
|
||||
$this->addDetailFilterAttribute($item, Filter::equal('name', $data->name));
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
protected function assemble(): void
|
||||
{
|
||||
/** @var Item $data */
|
||||
foreach ($this->data as $data) {
|
||||
$this->addHtml($this->createListItem($data));
|
||||
}
|
||||
|
||||
if ($this->isEmpty()) {
|
||||
$this->setTag('div');
|
||||
$this->addHtml(new EmptyStateBar(t('No items found.')));
|
||||
}
|
||||
}
|
||||
}
|
||||
69
library/Icingadb/Widget/ItemTable/ObjectTable.php
Normal file
69
library/Icingadb/Widget/ItemTable/ObjectTable.php
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2025 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemTable;
|
||||
|
||||
use Icinga\Exception\NotImplementedError;
|
||||
use Icinga\Module\Icingadb\Common\DetailActions;
|
||||
use Icinga\Module\Icingadb\Model\Hostgroupsummary;
|
||||
use Icinga\Module\Icingadb\Model\ServicegroupSummary;
|
||||
use ipl\Html\ValidHtml;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Url;
|
||||
use ipl\Web\Widget\ItemTable;
|
||||
|
||||
/**
|
||||
* ObjectTable
|
||||
*
|
||||
* @internal The only reason this class exists is due to the detail actions. In case those are part of the ipl
|
||||
* some time, this class is obsolete, and we must be able to safely drop it.
|
||||
*
|
||||
* @template Item of Hostgroupsummary|ServicegroupSummary
|
||||
*
|
||||
* @extends ItemTable<Item>
|
||||
*/
|
||||
class ObjectTable extends ItemTable
|
||||
{
|
||||
use DetailActions;
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
parent::init();
|
||||
|
||||
$this->initializeDetailActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Item $data
|
||||
*
|
||||
* @return ValidHtml
|
||||
*
|
||||
* @throws NotImplementedError When the data is not of the expected type
|
||||
*/
|
||||
protected function createListItem(object $data): ValidHtml
|
||||
{
|
||||
$item = parent::createListItem($data);
|
||||
|
||||
if ($this->getDetailActionsDisabled()) {
|
||||
return $item;
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case $data instanceof Hostgroupsummary:
|
||||
$this->setDetailUrl(Url::fromPath('icingadb/hostgroup'));
|
||||
|
||||
break;
|
||||
case $data instanceof ServicegroupSummary:
|
||||
$this->setDetailUrl(Url::fromPath('icingadb/servicegroup'));
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedError('Not implemented');
|
||||
}
|
||||
|
||||
$this->addDetailFilterAttribute($item, Filter::equal('name', $data->name));
|
||||
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemTable;
|
||||
|
||||
use Icinga\Module\Icingadb\Common\DetailActions;
|
||||
use Icinga\Module\Icingadb\Common\ViewMode;
|
||||
use ipl\Web\Common\BaseItemTable;
|
||||
use ipl\Web\Url;
|
||||
|
||||
class ServicegroupTable extends BaseItemTable
|
||||
{
|
||||
use DetailActions;
|
||||
use ViewMode;
|
||||
|
||||
protected $defaultAttributes = ['class' => 'servicegroup-table'];
|
||||
|
||||
protected function init(): void
|
||||
{
|
||||
$this->initializeDetailActions();
|
||||
$this->setDetailUrl(Url::fromPath('icingadb/servicegroup'));
|
||||
}
|
||||
|
||||
protected function getLayout(): string
|
||||
{
|
||||
return $this->getViewMode() === 'grid'
|
||||
? 'group-grid'
|
||||
: parent::getLayout();
|
||||
}
|
||||
|
||||
protected function getItemClass(): string
|
||||
{
|
||||
return $this->getViewMode() === 'grid'
|
||||
? ServicegroupGridCell::class
|
||||
: ServicegroupTableRow::class;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Widget\ItemTable;
|
||||
|
||||
use Icinga\Module\Icingadb\Model\ServicegroupSummary;
|
||||
use Icinga\Module\Icingadb\Widget\Detail\ServiceStatistics;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Stdlib\Filter;
|
||||
|
||||
/**
|
||||
* Servicegroup item of a servicegroup list. Represents one database row.
|
||||
*
|
||||
* @property ServicegroupSummary $item
|
||||
* @property ServicegroupTable $table
|
||||
*/
|
||||
class ServicegroupTableRow extends BaseServiceGroupItem
|
||||
{
|
||||
use TableRowLayout;
|
||||
|
||||
protected $defaultAttributes = ['class' => 'servicegroup-table-row'];
|
||||
|
||||
/**
|
||||
* Create Service statistics cell
|
||||
*
|
||||
* @return BaseHtmlElement[]
|
||||
*/
|
||||
protected function createStatistics(): array
|
||||
{
|
||||
$serviceStats = new ServiceStatistics($this->item);
|
||||
|
||||
$serviceStats->setBaseFilter(Filter::equal('servicegroup.name', $this->item->name));
|
||||
if (isset($this->table) && $this->table->hasBaseFilter()) {
|
||||
$serviceStats->setBaseFilter(
|
||||
Filter::all($serviceStats->getBaseFilter(), $this->table->getBaseFilter())
|
||||
);
|
||||
}
|
||||
|
||||
return [$this->createColumn($serviceStats)];
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue