summaryrefslogtreecommitdiffstats
path: root/library/Icingadb/Widget/ItemTable/StateItemTable.php
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--library/Icingadb/Widget/ItemTable/StateItemTable.php216
1 files changed, 216 insertions, 0 deletions
diff --git a/library/Icingadb/Widget/ItemTable/StateItemTable.php b/library/Icingadb/Widget/ItemTable/StateItemTable.php
new file mode 100644
index 0000000..f392322
--- /dev/null
+++ b/library/Icingadb/Widget/ItemTable/StateItemTable.php
@@ -0,0 +1,216 @@
+<?php
+
+/* Icinga DB Web | (c) 2022 Icinga GmbH | GPLv2 */
+
+namespace Icinga\Module\Icingadb\Widget\ItemTable;
+
+use ipl\Html\Attributes;
+use ipl\Html\BaseHtmlElement;
+use ipl\Html\Form;
+use ipl\Html\Html;
+use ipl\Html\HtmlElement;
+use ipl\Html\HtmlString;
+use ipl\Orm\Common\SortUtil;
+use ipl\Orm\Query;
+use ipl\Web\Control\SortControl;
+use ipl\Web\Widget\EmptyStateBar;
+use ipl\Web\Widget\Icon;
+
+/** @todo Figure out what this might (should) have in common with the new BaseItemTable implementation */
+abstract class StateItemTable extends BaseHtmlElement
+{
+ protected $baseAttributes = [
+ 'class' => 'state-item-table'
+ ];
+
+ /** @var array<string, string> The columns to render */
+ protected $columns;
+
+ /** @var iterable The datasource */
+ protected $data;
+
+ /** @var string The sort rules */
+ protected $sort;
+
+ protected $tag = 'table';
+
+ /**
+ * Create a new item table
+ *
+ * @param iterable $data Datasource of the table
+ * @param array<string, string> $columns The columns to render, keys are labels
+ */
+ public function __construct(iterable $data, array $columns)
+ {
+ $this->data = $data;
+ $this->columns = array_flip($columns);
+
+ $this->addAttributes($this->baseAttributes);
+
+ $this->init();
+ }
+
+ /**
+ * Initialize the item table
+ *
+ * If you want to adjust the item table after construction, override this method.
+ */
+ protected function init()
+ {
+ }
+
+ /**
+ * Get the columns being rendered
+ *
+ * @return array<string, string>
+ */
+ public function getColumns(): array
+ {
+ return $this->columns;
+ }
+
+ /**
+ * Set sort rules (as returned by {@see SortControl::getSort()})
+ *
+ * @param ?string $sort
+ *
+ * @return $this
+ */
+ public function setSort(?string $sort): self
+ {
+ $this->sort = $sort;
+
+ return $this;
+ }
+
+ abstract protected function getItemClass(): string;
+
+ abstract protected function getVisualColumn(): string;
+
+ protected function getVisualLabel()
+ {
+ return new Icon('heartbeat', ['title' => t('Severity')]);
+ }
+
+ protected function assembleColumnHeader(BaseHtmlElement $header, string $name, $label): void
+ {
+ $sortRules = [];
+ if ($this->sort !== null) {
+ $sortRules = SortUtil::createOrderBy($this->sort);
+ }
+
+ $active = false;
+ $sortDirection = null;
+ foreach ($sortRules as $rule) {
+ if ($rule[0] === $name) {
+ $sortDirection = $rule[1];
+ $active = true;
+ break;
+ }
+ }
+
+ if ($sortDirection === 'desc') {
+ $value = "$name asc";
+ } else {
+ $value = "$name desc";
+ }
+
+ $icon = 'sort';
+ if ($active) {
+ $icon = $sortDirection === 'desc' ? 'sort-up' : 'sort-down';
+ }
+
+ $form = new Form();
+ $form->setAttribute('method', 'GET');
+
+ $button = $form->createElement('button', 'sort', [
+ 'value' => $value,
+ 'type' => 'submit',
+ 'title' => is_string($label) ? $label : null,
+ 'class' => $active ? 'active' : null
+ ]);
+ $button->addHtml(
+ Html::tag(
+ 'span',
+ null,
+ // With &nbsp; to have the height sized the same as the others
+ $label ?? HtmlString::create('&nbsp;')
+ ),
+ new Icon($icon)
+ );
+ $form->addElement($button);
+
+ $header->add($form);
+
+ switch (true) {
+ case substr($name, -7) === '.output':
+ case substr($name, -12) === '.long_output':
+ $header->getAttributes()->add('class', 'has-plugin-output');
+ break;
+ case substr($name, -22) === '.icon_image.icon_image':
+ $header->getAttributes()->add('class', 'has-icon-images');
+ break;
+ case substr($name, -17) === '.performance_data':
+ case substr($name, -28) === '.normalized_performance_data':
+ $header->getAttributes()->add('class', 'has-performance-data');
+ break;
+ }
+ }
+
+ protected function assemble()
+ {
+ $itemClass = $this->getItemClass();
+
+ $headerRow = new HtmlElement('tr');
+
+ $visualCell = new HtmlElement('th', Attributes::create(['class' => 'has-visual']));
+ $this->assembleColumnHeader($visualCell, $this->getVisualColumn(), $this->getVisualLabel());
+ $headerRow->addHtml($visualCell);
+
+ foreach ($this->columns as $name => $label) {
+ $headerCell = new HtmlElement('th');
+ $this->assembleColumnHeader($headerCell, $name, is_int($label) ? $name : $label);
+ $headerRow->addHtml($headerCell);
+ }
+
+ $this->addHtml(new HtmlElement('thead', null, $headerRow));
+
+ $body = new HtmlElement('tbody', Attributes::create(['data-base-target' => '_next']));
+ foreach ($this->data as $item) {
+ $body->addHtml(new $itemClass($item, $this));
+ }
+
+ if ($body->isEmpty()) {
+ $body->addHtml(new HtmlElement(
+ 'tr',
+ null,
+ new HtmlElement(
+ 'td',
+ Attributes::create(['colspan' => count($this->columns)]),
+ new EmptyStateBar(t('No items found.'))
+ )
+ ));
+ }
+
+ $this->addHtml($body);
+ }
+
+ /**
+ * Enrich the given list of column names with appropriate labels
+ *
+ * @param Query $query
+ * @param array $columns
+ *
+ * @return array
+ */
+ public static function applyColumnMetaData(Query $query, array $columns): array
+ {
+ $newColumns = [];
+ foreach ($columns as $columnPath) {
+ $label = $query->getResolver()->getColumnDefinition($columnPath)->getLabel();
+ $newColumns[$label ?? $columnPath] = $columnPath;
+ }
+
+ return $newColumns;
+ }
+}