diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 13:17:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 13:17:48 +0000 |
commit | e6d4dfc040bbe3cb80a2ce65b82493b557f751fc (patch) | |
tree | 40bd6366b01b06f4d96fc8638f23a772263cb5e9 /library/Director/Web/Table | |
parent | Releasing progress-linux version 1.10.2-2~progress7.99u1. (diff) | |
download | icingaweb2-module-director-e6d4dfc040bbe3cb80a2ce65b82493b557f751fc.tar.xz icingaweb2-module-director-e6d4dfc040bbe3cb80a2ce65b82493b557f751fc.zip |
Merging upstream version 1.11.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/Director/Web/Table')
25 files changed, 703 insertions, 292 deletions
diff --git a/library/Director/Web/Table/ActivityLogTable.php b/library/Director/Web/Table/ActivityLogTable.php index 5460bc2..a57b86e 100644 --- a/library/Director/Web/Table/ActivityLogTable.php +++ b/library/Director/Web/Table/ActivityLogTable.php @@ -2,14 +2,15 @@ namespace Icinga\Module\Director\Web\Table; -use gipfl\Format\LocalTimeFormat; +use DateTime; use gipfl\IcingaWeb2\Link; -use gipfl\IcingaWeb2\Table\ZfQueryBasedTable; +use Icinga\Module\Director\Auth\Permission; use Icinga\Module\Director\Util; +use IntlDateFormatter; use ipl\Html\Html; use ipl\Html\HtmlElement; -class ActivityLogTable extends ZfQueryBasedTable +class ActivityLogTable extends IntlZfQueryBasedTable { protected $filters = []; @@ -27,9 +28,6 @@ class ActivityLogTable extends ZfQueryBasedTable 'object_type', ]; - /** @var LocalTimeFormat */ - protected $timeFormat; - protected $ranges = []; /** @var ?object */ @@ -44,7 +42,6 @@ class ActivityLogTable extends ZfQueryBasedTable public function __construct($db) { parent::__construct($db); - $this->timeFormat = new LocalTimeFormat(); } public function assemble() @@ -96,7 +93,9 @@ class ActivityLogTable extends ZfQueryBasedTable if (! $this->hasObjectFilter) { $columns[] = $this->makeRangeInfo($row->id); } - $columns[] = $this::td($this->timeFormat->getTime($row->ts_change_time)); + + + $columns[] = $this::td($this->getTime($row->ts_change_time)); return $this::tr($columns)->addAttributes(['class' => $action]); } @@ -107,7 +106,7 @@ class ActivityLogTable extends ZfQueryBasedTable */ protected function renderDayIfNew($timestamp) { - $day = $this->getDateFormatter()->getFullDay($timestamp); + $day = $this->getDateFormatter()->format((new DateTime())->setTimestamp($timestamp)); if ($this->lastDay !== $day) { $this->nextHeader()->add( @@ -206,7 +205,7 @@ class ActivityLogTable extends ZfQueryBasedTable $type = substr($type, 7); } - if (Util::hasPermission('director/showconfig')) { + if (Util::hasPermission(Permission::SHOW_CONFIG)) { // Later on replacing, service_set -> serviceset // multi column key :( diff --git a/library/Director/Web/Table/ApplyRulesTable.php b/library/Director/Web/Table/ApplyRulesTable.php index a861bac..a5379b5 100644 --- a/library/Director/Web/Table/ApplyRulesTable.php +++ b/library/Director/Web/Table/ApplyRulesTable.php @@ -6,6 +6,7 @@ use Icinga\Authentication\Auth; use Icinga\Data\Filter\Filter; use Icinga\Exception\IcingaException; use Icinga\Module\Director\Db; +use Icinga\Module\Director\Db\DbSelectParenthesis; use Icinga\Module\Director\Db\DbUtil; use Icinga\Module\Director\Db\IcingaObjectFilterHelper; use Icinga\Module\Director\IcingaConfig\AssignRenderer; @@ -20,6 +21,8 @@ use Zend_Db_Select as ZfSelect; class ApplyRulesTable extends ZfQueryBasedTable { + use TableWithBranchSupport; + protected $searchColumns = [ 'o.object_name', 'o.assign_filter', @@ -94,10 +97,14 @@ class ApplyRulesTable extends ZfQueryBasedTable // NOT (YET) static::td($this->createActionLinks($row))->setSeparator(' ') ]); + $classes = $this->getRowClasses($row); + if ($row->disabled === 'y') { - $tr->getAttributes()->add('class', 'disabled'); + $classes[] = 'disabled'; } + $tr->getAttributes()->add('class', $classes); + return $tr; } @@ -117,7 +124,8 @@ class ApplyRulesTable extends ZfQueryBasedTable $this->getQuery(), $template, 'o', - $inheritance + $inheritance, + $this->branchUuid ); return $this; @@ -146,7 +154,7 @@ class ApplyRulesTable extends ZfQueryBasedTable $links = []; $links[] = Link::create( Icon::create('sitemap'), - "${baseUrl}template/applytargets", + "{$baseUrl}template/applytargets", ['id' => $row->id], ['title' => $this->translate('Show affected Objects')] ); @@ -196,6 +204,15 @@ class ApplyRulesTable extends ZfQueryBasedTable return FilterRenderer::applyToQuery($filter, $query); } + protected function getRowClasses($row) + { + // TODO: remove isset, to figure out where it is missing + if (isset($row->branch_uuid) && $row->branch_uuid !== null) { + return ['branch_modified']; + } + return []; + } + /** * @return IcingaObject @@ -216,6 +233,7 @@ class ApplyRulesTable extends ZfQueryBasedTable 'id' => 'o.id', 'uuid' => 'o.uuid', 'object_name' => 'o.object_name', + 'object_type' => 'o.object_type', 'disabled' => 'o.disabled', 'assign_filter' => 'o.assign_filter', 'apply_for' => '(NULL)', @@ -224,17 +242,92 @@ class ApplyRulesTable extends ZfQueryBasedTable if ($table === 'icinga_service') { $columns['apply_for'] = 'o.apply_for'; } + + $conn = $this->connection(); $query = $this->db()->select()->from( ['o' => $table], $columns - )->where( - "object_type = 'apply'" )->order('o.object_name'); - if ($this->type === 'service') { - $query->where('service_set_id IS NULL'); + if ($this->branchUuid) { + $columns = $this->branchifyColumns($columns); + $columns['branch_uuid'] = 'bo.branch_uuid'; + if ($conn->isPgsql()) { + $columns['imports'] = 'CONCAT(\'[\', ARRAY_TO_STRING(ARRAY_AGG' + . '(CONCAT(\'"\', sub_o.object_name, \'"\')), \',\'), \']\')'; + } else { + $columns['imports'] = 'CONCAT(\'[\', ' + . 'GROUP_CONCAT(CONCAT(\'"\', sub_o.object_name, \'"\')), \']\')'; + } + + $this->stripSearchColumnAliases(); + + $query->reset('columns'); + $right = clone($query); + + $query->columns($columns) + ->joinLeft( + ['oi' => $table . '_inheritance'], + 'o.id = oi.' . $this->getType() . '_id', + [] + )->joinLeft( + ['sub_o' => $table], + 'sub_o.id = oi.parent_' . $this->getType() . '_id', + [] + )->group(['o.id', 'bo.uuid', 'bo.branch_uuid']); + + $query->joinLeft( + ['bo' => "branched_$table"], + // TODO: PgHexFunc + $this->db()->quoteInto( + 'bo.uuid = o.uuid AND bo.branch_uuid = ?', + DbUtil::quoteBinaryLegacy($this->branchUuid->getBytes(), $this->db()) + ), + [] + )->where("(bo.branch_deleted IS NULL OR bo.branch_deleted = 'n')"); + + if ($this->type === 'service') { + $query->where('o.service_set_id IS NULL AND bo.service_set IS NULL'); + } + + $columns['imports'] = 'bo.imports'; + + $right->columns($columns) + ->joinRight( + ['bo' => "branched_$table"], + 'bo.uuid = o.uuid', + [] + ) + ->where('o.uuid IS NULL') + ->where('bo.branch_uuid = ?', $conn->quoteBinary($this->branchUuid->getBytes())); + + $query = $this->db()->select()->union([ + 'l' => new DbSelectParenthesis($query), + 'r' => new DbSelectParenthesis($right), + ]); + + $query = $this->db()->select()->from(['u' => $query]); + $query->order('object_name')->limit(100); + } else { + if ($this->type === 'service') { + $query->where('service_set_id IS NULL'); + } } + $query->where( + "object_type = 'apply'" + ); + + $this->applyRestrictions($query); + return $this->applyRestrictions($query); } + + /** + * @return Db + */ + public function connection() + { + return parent::connection(); + } } diff --git a/library/Director/Web/Table/BasketSnapshotTable.php b/library/Director/Web/Table/BasketSnapshotTable.php index 08f808a..6fe0e16 100644 --- a/library/Director/Web/Table/BasketSnapshotTable.php +++ b/library/Director/Web/Table/BasketSnapshotTable.php @@ -4,13 +4,12 @@ namespace Icinga\Module\Director\Web\Table; use ipl\Html\Html; use gipfl\IcingaWeb2\Link; -use gipfl\IcingaWeb2\Table\ZfQueryBasedTable; use Icinga\Date\DateFormatter; use Icinga\Module\Director\Core\Json; use Icinga\Module\Director\DirectorObject\Automation\Basket; use RuntimeException; -class BasketSnapshotTable extends ZfQueryBasedTable +class BasketSnapshotTable extends IntlZfQueryBasedTable { use DbHelper; @@ -67,7 +66,7 @@ class BasketSnapshotTable extends ZfQueryBasedTable if (! is_object($summary) && ! is_array($summary)) { throw new RuntimeException(sprintf( 'Got invalid basket summary: %s ', - var_export($summary, 1) + var_export($summary, true) )); } diff --git a/library/Director/Web/Table/BranchActivityTable.php b/library/Director/Web/Table/BranchActivityTable.php index e7131ef..cbf940d 100644 --- a/library/Director/Web/Table/BranchActivityTable.php +++ b/library/Director/Web/Table/BranchActivityTable.php @@ -2,15 +2,14 @@ namespace Icinga\Module\Director\Web\Table; -use gipfl\Format\LocalTimeFormat; +use Icinga\Module\Director\Auth\Permission; use Icinga\Module\Director\Db; use Icinga\Module\Director\Db\Branch\BranchActivity; use Icinga\Module\Director\Util; use gipfl\IcingaWeb2\Link; -use gipfl\IcingaWeb2\Table\ZfQueryBasedTable; use Ramsey\Uuid\UuidInterface; -class BranchActivityTable extends ZfQueryBasedTable +class BranchActivityTable extends IntlZfQueryBasedTable { protected $extraParams = []; @@ -20,16 +19,12 @@ class BranchActivityTable extends ZfQueryBasedTable /** @var ?UuidInterface */ protected $objectUuid; - /** @var LocalTimeFormat */ - protected $timeFormat; - protected $linkToObject = true; public function __construct(UuidInterface $branchUuid, $db, UuidInterface $objectUuid = null) { $this->branchUuid = $branchUuid; $this->objectUuid = $objectUuid; - $this->timeFormat = new LocalTimeFormat(); parent::__construct($db); } @@ -45,7 +40,7 @@ class BranchActivityTable extends ZfQueryBasedTable $activity = BranchActivity::fromDbRow($row); return $this::tr([ $this::td($this->makeBranchLink($activity))->setSeparator(' '), - $this::td($this->timeFormat->getTime($ts)) + $this::td($this->getTime($ts)) ])->addAttributes(['class' => ['action-' . $activity->getAction(), 'branched']]); } @@ -75,7 +70,7 @@ class BranchActivityTable extends ZfQueryBasedTable { $type = preg_replace('/^icinga_/', '', $activity->getObjectTable()); - if (Util::hasPermission('director/showconfig')) { + if (Util::hasPermission(Permission::SHOW_CONFIG)) { // Later on replacing, service_set -> serviceset return [ '[' . $activity->getAuthor() . ']', diff --git a/library/Director/Web/Table/ChoicesTable.php b/library/Director/Web/Table/ChoicesTable.php index 4ba2460..9574520 100644 --- a/library/Director/Web/Table/ChoicesTable.php +++ b/library/Director/Web/Table/ChoicesTable.php @@ -44,7 +44,7 @@ class ChoicesTable extends ZfQueryBasedTable public function renderRow($row) { $type = $this->getType(); - $url = Url::fromPath("director/templatechoice/${type}", [ + $url = Url::fromPath("director/templatechoice/{$type}", [ 'name' => $row->object_name ]); @@ -56,7 +56,7 @@ class ChoicesTable extends ZfQueryBasedTable protected function prepareQuery() { $type = $this->getType(); - $table = "icinga_${type}_template_choice"; + $table = "icinga_{$type}_template_choice"; return $this->db() ->select() ->from(['o' => $table], 'object_name') diff --git a/library/Director/Web/Table/CustomvarTable.php b/library/Director/Web/Table/CustomvarTable.php index f9a3844..1aead19 100644 --- a/library/Director/Web/Table/CustomvarTable.php +++ b/library/Director/Web/Table/CustomvarTable.php @@ -91,11 +91,11 @@ class CustomvarTable extends ZfQueryBasedTable $columns["cnt_$type"] = 'COUNT(*)'; $columns["distinct_$type"] = 'COUNT(DISTINCT varvalue)'; return $db->select()->from( - ['v' => "icinga_${type}_var"], + ['v' => "icinga_{$type}_var"], $columns )->join( - ['o' => "icinga_${type}"], - "o.id = v.${type}_id", + ['o' => "icinga_{$type}"], + "o.id = v.{$type}_id", [] )->where('o.object_type != ?', 'external_object')->group('varname'); } diff --git a/library/Director/Web/Table/CustomvarVariantsTable.php b/library/Director/Web/Table/CustomvarVariantsTable.php index 80fca70..8447e36 100644 --- a/library/Director/Web/Table/CustomvarVariantsTable.php +++ b/library/Director/Web/Table/CustomvarVariantsTable.php @@ -108,11 +108,11 @@ class CustomvarVariantsTable extends ZfQueryBasedTable $columns["cnt_$type"] = 'COUNT(*)'; $columns['format'] = 'v.format'; return $db->select()->from( - ['v' => "icinga_${type}_var"], + ['v' => "icinga_{$type}_var"], $columns )->join( - ['o' => "icinga_${type}"], - "o.id = v.${type}_id", + ['o' => "icinga_{$type}"], + "o.id = v.{$type}_id", [] )->where( 'v.varname = ?', diff --git a/library/Director/Web/Table/DatafieldTable.php b/library/Director/Web/Table/DatafieldTable.php index 4b321d7..0062626 100644 --- a/library/Director/Web/Table/DatafieldTable.php +++ b/library/Director/Web/Table/DatafieldTable.php @@ -5,12 +5,13 @@ namespace Icinga\Module\Director\Web\Table; use gipfl\IcingaWeb2\Link; use gipfl\IcingaWeb2\Table\ZfQueryBasedTable; use Zend_Db_Adapter_Abstract as ZfDbAdapter; +use Zend_Db_Expr as DbExpr; use Zend_Db_Select as ZfDbSelect; class DatafieldTable extends ZfQueryBasedTable { protected $searchColumns = [ - 'df.varname', + 'lc_varname', 'df.caption', ]; @@ -19,6 +20,7 @@ class DatafieldTable extends ZfQueryBasedTable return [ 'id' => 'df.id', 'varname' => 'df.varname', + 'lc_varname' => new DbExpr('LOWER(df.varname)'), 'caption' => 'df.caption', 'description' => 'df.description', 'datatype' => 'df.datatype', @@ -88,6 +90,15 @@ class DatafieldTable extends ZfQueryBasedTable )->group('df.id')->group('df.varname')->group('dfc.category_name')->order('caption ASC'); } + public function search($search) + { + if ($search !== null) { + $search = strtolower($search); + } + + return parent::search($search); + } + /** * @param $type * @param ZfDbAdapter $db @@ -96,7 +107,7 @@ class DatafieldTable extends ZfQueryBasedTable */ protected function makeDatafieldSub($type, ZfDbAdapter $db) { - return $db->select()->from("icinga_${type}_field", [ + return $db->select()->from("icinga_{$type}_field", [ 'cnt' => 'COUNT(*)', 'datafield_id' ])->group('datafield_id'); @@ -110,7 +121,7 @@ class DatafieldTable extends ZfQueryBasedTable */ protected function makeVarSub($type, ZfDbAdapter $db) { - return $db->select()->from("icinga_${type}_var", [ + return $db->select()->from("icinga_{$type}_var", [ 'cnt' => 'COUNT(*)', 'varname' ])->group('varname'); diff --git a/library/Director/Web/Table/DependencyTemplateUsageTable.php b/library/Director/Web/Table/DependencyTemplateUsageTable.php index d7537c5..2c1de50 100644 --- a/library/Director/Web/Table/DependencyTemplateUsageTable.php +++ b/library/Director/Web/Table/DependencyTemplateUsageTable.php @@ -2,6 +2,8 @@ namespace Icinga\Module\Director\Web\Table; +use Icinga\Module\Director\Db; + class DependencyTemplateUsageTable extends TemplateUsageTable { public function getTypes() @@ -12,11 +14,15 @@ class DependencyTemplateUsageTable extends TemplateUsageTable ]; } - protected function getTypeSummaryDefinitions() + protected function getSummaryTables(string $templateType, Db $connection) { return [ - 'templates' => $this->getSummaryLine('template'), - 'applyrules' => $this->getSummaryLine('apply'), + 'templates' => TemplatesTable::create( + $templateType, + $connection + ), + 'applyrules' => ApplyRulesTable::create($templateType, $connection) + ->setBranchUuid($this->branchUuid) ]; } } diff --git a/library/Director/Web/Table/DeploymentLogTable.php b/library/Director/Web/Table/DeploymentLogTable.php index 2d5cb94..4849218 100644 --- a/library/Director/Web/Table/DeploymentLogTable.php +++ b/library/Director/Web/Table/DeploymentLogTable.php @@ -3,10 +3,9 @@ namespace Icinga\Module\Director\Web\Table; use gipfl\IcingaWeb2\Link; -use gipfl\IcingaWeb2\Table\ZfQueryBasedTable; use Icinga\Date\DateFormatter; -class DeploymentLogTable extends ZfQueryBasedTable +class DeploymentLogTable extends IntlZfQueryBasedTable { use DbHelper; diff --git a/library/Director/Web/Table/GroupMemberTable.php b/library/Director/Web/Table/GroupMemberTable.php index b0814ad..c24c6af 100644 --- a/library/Director/Web/Table/GroupMemberTable.php +++ b/library/Director/Web/Table/GroupMemberTable.php @@ -109,7 +109,7 @@ class GroupMemberTable extends ZfQueryBasedTable ]; } - $url = Url::fromPath("director/${type}", $params); + $url = Url::fromPath("director/{$type}", $params); $tr = $this::tr(); @@ -163,7 +163,7 @@ class GroupMemberTable extends ZfQueryBasedTable 'o.id', 'o.object_type', 'o.object_name', - 'membership_type' => "CASE WHEN go.${type}_id IS NULL THEN 'apply' ELSE 'direct' END" + 'membership_type' => "CASE WHEN go.{$type}_id IS NULL THEN 'apply' ELSE 'direct' END" ]; if ($this->group === null) { @@ -176,19 +176,19 @@ class GroupMemberTable extends ZfQueryBasedTable } $query = $this->db()->select()->from( - ['gro' => "icinga_${type}group_${type}_resolved"], + ['gro' => "icinga_{$type}group_{$type}_resolved"], $columns )->join( - ['o' => "icinga_${type}"], - "o.id = gro.${type}_id", + ['o' => "icinga_{$type}"], + "o.id = gro.{$type}_id", [] )->join( - ['g' => "icinga_${type}group"], - "gro.${type}group_id = g.id", + ['g' => "icinga_{$type}group"], + "gro.{$type}group_id = g.id", [] )->joinLeft( - ['go' => "icinga_${type}group_${type}"], - "go.${type}_id = o.id AND go.${type}group_id = g.id", + ['go' => "icinga_{$type}group_{$type}"], + "go.{$type}_id = o.id AND go.{$type}group_id = g.id", [] )->order('o.object_name'); diff --git a/library/Director/Web/Table/HostTemplateUsageTable.php b/library/Director/Web/Table/HostTemplateUsageTable.php index 2d1ee2f..672691f 100644 --- a/library/Director/Web/Table/HostTemplateUsageTable.php +++ b/library/Director/Web/Table/HostTemplateUsageTable.php @@ -11,12 +11,4 @@ class HostTemplateUsageTable extends TemplateUsageTable 'objects' => $this->translate('Objects'), ]; } - - protected function getTypeSummaryDefinitions() - { - return [ - 'templates' => $this->getSummaryLine('template'), - 'objects' => $this->getSummaryLine('object'), - ]; - } } diff --git a/library/Director/Web/Table/IcingaServiceSetServiceTable.php b/library/Director/Web/Table/IcingaServiceSetServiceTable.php index c205e66..2c3dbc4 100644 --- a/library/Director/Web/Table/IcingaServiceSetServiceTable.php +++ b/library/Director/Web/Table/IcingaServiceSetServiceTable.php @@ -12,6 +12,7 @@ use Icinga\Module\Director\Objects\IcingaServiceSet; use gipfl\IcingaWeb2\Link; use gipfl\IcingaWeb2\Table\ZfQueryBasedTable; use gipfl\IcingaWeb2\Url; +use Ramsey\Uuid\Uuid; class IcingaServiceSetServiceTable extends ZfQueryBasedTable { @@ -122,9 +123,12 @@ class IcingaServiceSetServiceTable extends ZfQueryBasedTable ]; $url = 'director/host/servicesetservice'; } else { + if (is_resource($row->uuid)) { + $row->uuid =stream_get_contents($row->uuid); + } + $params = [ - 'name' => $row->service, - 'set' => $row->service_set + 'uuid' => Uuid::fromBytes($row->uuid)->toString(), ]; $url = 'director/service'; } @@ -194,14 +198,28 @@ class IcingaServiceSetServiceTable extends ZfQueryBasedTable $connection = $this->connection(); assert($connection instanceof Db); $builder = new ServiceSetQueryBuilder($connection, $this->branchUuid); - return $builder->selectServicesForSet($this->set)->limit(100); + $query = $builder->selectServicesForSet($this->set); + $alias = $this->branchUuid ? 'u' : 'o'; + + if ($this->affectedHost) { + if ($hostId = $this->affectedHost->get('id')) { + $query->joinLeft( + ['hsb' => 'icinga_host_service_blacklist'], + $this->db()->quoteInto("$alias.id = hsb.service_id AND hsb.host_id = ?", $hostId), + [] + )->columns([ + 'blacklisted' => "CASE WHEN hsb.service_id IS NULL THEN 'n' ELSE 'y' END" + ]); + } + } + + return $query->limit(100); } protected function createFakeRemoveLinkForReadonlyView() { return Html::tag('span', [ - 'class' => 'icon-paste', - 'style' => 'float: right; font-weight: normal', + 'class' => ['icon-paste', 'seviceset-obj-link'], ], $this->host->getObjectName()); } @@ -209,8 +227,7 @@ class IcingaServiceSetServiceTable extends ZfQueryBasedTable { $hostname = $host->getObjectName(); return Link::create($hostname, 'director/host/services', ['name' => $hostname], [ - 'class' => 'icon-paste', - 'style' => 'float: right; font-weight: normal', + 'class' => ['icon-paste', 'seviceset-obj-link'], 'data-base-target' => '_next', 'title' => sprintf( $this->translate('This set has been inherited from %s'), diff --git a/library/Director/Web/Table/IntlZfQueryBasedTable.php b/library/Director/Web/Table/IntlZfQueryBasedTable.php new file mode 100644 index 0000000..81ef14c --- /dev/null +++ b/library/Director/Web/Table/IntlZfQueryBasedTable.php @@ -0,0 +1,51 @@ +<?php + +namespace Icinga\Module\Director\Web\Table; + +use DateTime; +use gipfl\IcingaWeb2\Table\ZfQueryBasedTable; +use IntlDateFormatter; +use Locale; + +abstract class IntlZfQueryBasedTable extends ZfQueryBasedTable +{ + protected function getDateFormatter() + { + return (new IntlDateFormatter( + Locale::getDefault(), + IntlDateFormatter::FULL, + IntlDateFormatter::NONE + )); + } + + /** + * @param int $timestamp + */ + protected function renderDayIfNew($timestamp) + { + $day = $this->getDateFormatter()->format((new DateTime())->setTimestamp($timestamp)); + + if ($this->lastDay !== $day) { + $this->nextHeader()->add( + $this::th($day, [ + 'colspan' => 2, + 'class' => 'table-header-day' + ]) + ); + + $this->lastDay = $day; + $this->nextBody(); + } + } + + protected function getTime(int $timeStamp) + { + $timeFormatter = $this->getDateFormatter(); + + $timeFormatter->setPattern( + in_array(Locale::getDefault(), ['en_US', 'en_US.UTF-8']) ? 'h:mm:ss a': 'H:mm:ss' + ); + + return $timeFormatter->format((new DateTime())->setTimestamp($timeStamp)); + } +} diff --git a/library/Director/Web/Table/NotificationTemplateUsageTable.php b/library/Director/Web/Table/NotificationTemplateUsageTable.php index da411a3..d8cd3d8 100644 --- a/library/Director/Web/Table/NotificationTemplateUsageTable.php +++ b/library/Director/Web/Table/NotificationTemplateUsageTable.php @@ -2,6 +2,8 @@ namespace Icinga\Module\Director\Web\Table; +use Icinga\Module\Director\Db; + class NotificationTemplateUsageTable extends TemplateUsageTable { public function getTypes() @@ -12,11 +14,15 @@ class NotificationTemplateUsageTable extends TemplateUsageTable ]; } - protected function getTypeSummaryDefinitions() + protected function getSummaryTables(string $templateType, Db $connection) { return [ - 'templates' => $this->getSummaryLine('template'), - 'applyrules' => $this->getSummaryLine('apply', 'o.host_id IS NULL'), + 'templates' => TemplatesTable::create( + $templateType, + $connection + ), + 'applyrules' => ApplyRulesTable::create($templateType, $connection) + ->setBranchUuid($this->branchUuid) ]; } } diff --git a/library/Director/Web/Table/ObjectSetTable.php b/library/Director/Web/Table/ObjectSetTable.php index 2773841..4df9bdd 100644 --- a/library/Director/Web/Table/ObjectSetTable.php +++ b/library/Director/Web/Table/ObjectSetTable.php @@ -2,7 +2,9 @@ namespace Icinga\Module\Director\Web\Table; +use gipfl\IcingaWeb2\Zf1\Db\FilterRenderer; use Icinga\Authentication\Auth; +use Icinga\Data\Filter\Filter; use Icinga\Module\Director\Db; use gipfl\IcingaWeb2\Link; use gipfl\IcingaWeb2\Table\ZfQueryBasedTable; @@ -28,6 +30,8 @@ class ObjectSetTable extends ZfQueryBasedTable /** @var Auth */ private $auth; + protected $queries = []; + public static function create($type, Db $db, Auth $auth) { $table = new static($db); @@ -53,7 +57,7 @@ class ObjectSetTable extends ZfQueryBasedTable 'uuid' => Uuid::fromBytes(Db\DbUtil::binaryResult($row->uuid))->toString(), ]; - $url = Url::fromPath("director/${type}set", $params); + $url = Url::fromPath("director/{$type}set", $params); $classes = $this->getRowClasses($row); $tr = static::tr([ @@ -85,7 +89,7 @@ class ObjectSetTable extends ZfQueryBasedTable { $type = $this->getType(); - $table = "icinga_${type}_set"; + $table = "icinga_{$type}_set"; $columns = [ 'id' => 'os.id', 'uuid' => 'os.uuid', @@ -106,15 +110,15 @@ class ObjectSetTable extends ZfQueryBasedTable ['os' => $table], $columns )->joinLeft( - ['o' => "icinga_${type}"], - "o.${type}_set_id = os.id", + ['o' => "icinga_{$type}"], + "o.{$type}_set_id = os.id", [] ); $nameFilter = new FilterByNameRestriction( $this->connection(), $this->auth, - "${type}_set" + "{$type}_set" ); $nameFilter->applyToQuery($query, 'os'); /** @var Db $conn */ @@ -145,7 +149,20 @@ class ObjectSetTable extends ZfQueryBasedTable $query->group('bos.uuid')->group('os.uuid')->group('os.id')->group('bos.branch_uuid'); $right->group('bos.uuid')->group('os.uuid')->group('os.id')->group('bos.branch_uuid'); } - + $right->joinLeft( + ['bo' => "branched_icinga_{$type}"], + "bo.{$type}_set = bos.object_name", + [] + )->group(['bo.object_name', 'o.object_name']); + $query->joinLeft( + ['bo' => "branched_icinga_{$type}"], + "bo.{$type}_set = bos.object_name", + [] + )->group(['bo.object_name', 'o.object_name']); + $this->queries = [ + $query, + $right + ]; $query = $this->db()->select()->union([ 'l' => new DbSelectParenthesis($query), 'r' => new DbSelectParenthesis($right), @@ -168,16 +185,16 @@ class ObjectSetTable extends ZfQueryBasedTable ->group('assign_filter') ->group('description') ->group('count_services'); - }; + } } else { // Disabled for now, check for correctness: // $query->joinLeft( - // ['osi' => "icinga_${type}_set_inheritance"], - // "osi.parent_${type}_set_id = os.id", + // ['osi' => "icinga_{$type}_set_inheritance"], + // "osi.parent_{$type}_set_id = os.id", // [] // )->joinLeft( - // ['oso' => "icinga_${type}_set"], - // "oso.id = oso.${type}_set_id", + // ['oso' => "icinga_{$type}_set"], + // "oso.id = oso.{$type}_set_id", // [] // ); // 'count_hosts' => 'COUNT(DISTINCT oso.id)', @@ -196,11 +213,40 @@ class ObjectSetTable extends ZfQueryBasedTable ->group('os.assign_filter') ->group('os.description'); }; + $this->queries = [$query]; } return $query; } + public function search($search) + { + if (! empty($search)) { + $columns = $this->getSearchColumns(); + if (strpos($search, ' ') === false) { + $filter = Filter::matchAny(); + foreach ($columns as $column) { + $filter->addFilter(Filter::expression($column, '=', "*$search*")); + } + } else { + $filter = Filter::matchAll(); + foreach (explode(' ', $search) as $s) { + $sub = Filter::matchAny(); + foreach ($columns as $column) { + $sub->addFilter(Filter::expression($column, '=', "*$s*")); + } + $filter->addFilter($sub); + } + } + + foreach ($this->queries as $query) { + FilterRenderer::applyToQuery($filter, $query); + } + } + + return $this; + } + /** * @return Db */ diff --git a/library/Director/Web/Table/ObjectsTable.php b/library/Director/Web/Table/ObjectsTable.php index 792cb6d..4ad1166 100644 --- a/library/Director/Web/Table/ObjectsTable.php +++ b/library/Director/Web/Table/ObjectsTable.php @@ -14,6 +14,7 @@ use gipfl\IcingaWeb2\Link; use gipfl\IcingaWeb2\Table\ZfQueryBasedTable; use gipfl\IcingaWeb2\Url; use Ramsey\Uuid\Uuid; +use Zend_Db_Adapter_Pdo_Pgsql; use Zend_Db_Select as ZfSelect; class ObjectsTable extends ZfQueryBasedTable @@ -50,12 +51,18 @@ class ObjectsTable extends ZfQueryBasedTable /** @var Auth */ private $auth; + public function __construct($db, Auth $auth) + { + $this->auth = $auth; + parent::__construct($db); + } + /** * @param $type * @param Db $db * @return static */ - public static function create($type, Db $db) + public static function create($type, Db $db, Auth $auth) { $class = __NAMESPACE__ . '\\ObjectsTable' . ucfirst($type); if (! class_exists($class)) { @@ -63,7 +70,7 @@ class ObjectsTable extends ZfQueryBasedTable } /** @var static $table */ - $table = new $class($db); + $table = new $class($db, $auth); $table->type = $type; return $table; } @@ -84,20 +91,6 @@ class ObjectsTable extends ZfQueryBasedTable return $this; } - /** - * @return Auth - */ - public function getAuth() - { - return $this->auth; - } - - public function setAuth(Auth $auth) - { - $this->auth = $auth; - return $this; - } - public function filterObjectType($type) { $this->filterObjectType = $type; @@ -124,11 +117,17 @@ class ObjectsTable extends ZfQueryBasedTable IcingaObject $template, $inheritance = Db\IcingaObjectFilterHelper::INHERIT_DIRECT ) { + if ($this->branchUuid) { + $tableAlias = 'u'; + } else { + $tableAlias = 'o'; + } IcingaObjectFilterHelper::filterByTemplate( $this->getQuery(), $template, - 'o', - $inheritance + $tableAlias, + $inheritance, + $this->branchUuid ); return $this; @@ -142,7 +141,7 @@ class ObjectsTable extends ZfQueryBasedTable protected function renderObjectNameColumn($row) { $type = $this->baseObjectUrl; - $url = Url::fromPath("director/${type}", [ + $url = Url::fromPath("director/{$type}", [ 'uuid' => Uuid::fromBytes($row->uuid)->toString() ]); @@ -227,11 +226,10 @@ class ObjectsTable extends ZfQueryBasedTable { /** @var Db $db */ $db = $this->connection(); - $auth = $this->getAuth(); return [ - new HostgroupRestriction($db, $auth), - new FilterByNameRestriction($db, $auth, $this->getDummyObject()->getShortTableName()) + new HostgroupRestriction($db, $this->auth), + new FilterByNameRestriction($db, $this->auth, $this->getDummyObject()->getShortTableName()) ]; } @@ -279,7 +277,39 @@ class ObjectsTable extends ZfQueryBasedTable $conn->quoteBinary($this->branchUuid->getBytes()) ), [] - )->where("(bo.branch_deleted IS NULL OR bo.branch_deleted = 'n')"); + ); + + // keep the imported templates as columns + $leftColumns = $columns; + $rightColumns = $columns; + + if ($this->db() instanceof Zend_Db_Adapter_Pdo_Pgsql) { + $leftColumns['imports'] = 'CONCAT(\'[\', ARRAY_TO_STRING(ARRAY_AGG' + . '(CONCAT(\'"\', sub_o.object_name, \'"\')), \',\'), \']\')'; + } else { + $leftColumns['imports'] = 'CONCAT(\'[\', ' + . 'GROUP_CONCAT(CONCAT(\'"\', sub_o.object_name, \'"\')), \']\')'; + } + + $query->reset('columns'); + + $query->columns($leftColumns) + ->joinLeft( + ['oi' => $table . '_inheritance'], + 'o.id = oi.' . $this->getType() . '_id', + [] + )->joinLeft( + ['sub_o' => $table], + 'sub_o.id = oi.parent_' . $this->getType() . '_id', + [] + )->group(['o.id', 'bo.uuid', 'bo.branch_uuid']); + + $rightColumns['imports'] = 'bo.imports'; + + $right->reset('columns'); + $right->columns($rightColumns); + + $query->where("(bo.branch_deleted IS NULL OR bo.branch_deleted = 'n')"); $this->applyObjectTypeFilter($query, $right); $right->joinRight( ['bo' => "branched_$table"], @@ -298,6 +328,7 @@ class ObjectsTable extends ZfQueryBasedTable $query->order('object_name')->limit(100); } else { $this->applyObjectTypeFilter($query); + $query = $this->applyRestrictions($query); $query->order('o.object_name')->limit(100); } diff --git a/library/Director/Web/Table/ObjectsTableService.php b/library/Director/Web/Table/ObjectsTableService.php index 2d4ad41..c9bce72 100644 --- a/library/Director/Web/Table/ObjectsTableService.php +++ b/library/Director/Web/Table/ObjectsTableService.php @@ -203,8 +203,14 @@ class ObjectsTableService extends ObjectsTable 'hsb.service_id = o.id AND hsb.host_id = o.host_id', [] )->where('o.service_set_id IS NULL') + ->group(['o.id', 'h.id','hsb.service_id', 'hsb.host_id']) ->order('o.object_name')->order('h.object_name'); + if ($this->branchUuid) { + $subQuery->where('bo.service_set IS NULL') + ->group(['bo.uuid', 'bo.branch_uuid']); + } + if ($this->host) { if ($this->branchUuid) { $subQuery->where('COALESCE(h.object_name, bo.host) = ?', $this->host->getObjectName()); diff --git a/library/Director/Web/Table/ObjectsTableSetMembers.php b/library/Director/Web/Table/ObjectsTableSetMembers.php new file mode 100644 index 0000000..6b18ac9 --- /dev/null +++ b/library/Director/Web/Table/ObjectsTableSetMembers.php @@ -0,0 +1,255 @@ +<?php + +namespace Icinga\Module\Director\Web\Table; + +use Icinga\Authentication\Auth; +use Icinga\Module\Director\Db; +use gipfl\IcingaWeb2\Link; +use gipfl\IcingaWeb2\Table\ZfQueryBasedTable; +use gipfl\IcingaWeb2\Url; +use Icinga\Module\Director\Db\DbSelectParenthesis; +use Icinga\Module\Director\Db\IcingaObjectFilterHelper; +use Icinga\Module\Director\Objects\IcingaObject; +use Icinga\Module\Director\Restriction\FilterByNameRestriction; +use Ramsey\Uuid\Uuid; + +class ObjectsTableSetMembers extends ZfQueryBasedTable +{ + use TableWithBranchSupport; + + protected $searchColumns = [ + 'os.object_name', + 'o.object_name', + ]; + + private $type; + + /** @var IcingaObject */ + protected $dummyObject; + + protected $baseObjectUrl; + + /** @var Auth */ + private $auth; + + public static function create($type, Db $db, Auth $auth) + { + $table = new static($db); + $table->type = $type; + $table->auth = $auth; + return $table; + } + + public function getType() + { + return $this->type; + } + + public function getColumnsToBeRendered() + { + return [ + 'os.object_name' => 'Service Set', + 'o.object_name' => 'Service Name' + ]; + } + + public function setBaseObjectUrl($url) + { + $this->baseObjectUrl = $url; + + return $this; + } + + protected function getRowClasses($row) + { + // TODO: remove isset, to figure out where it is missing + if (isset($row->branch_uuid) && $row->branch_uuid !== null) { + return ['branch_modified']; + } + return []; + } + + /** + * Should be triggered from renderRow, still unused. + * + * @param IcingaObject $template + * @param string $inheritance + * @return $this + * @throws \Icinga\Exception\ProgrammingError + */ + public function filterTemplate( + IcingaObject $template, + $inheritance = IcingaObjectFilterHelper::INHERIT_DIRECT + ) { + IcingaObjectFilterHelper::filterByTemplate( + $this->getQuery(), + $template, + 'o', + $inheritance, + $this->branchUuid + ); + + return $this; + } + + + public function renderRow($row) + { + $url = Url::fromPath('director/service/edit', [ + 'name' => $row->object_name, + 'uuid' => Uuid::fromBytes($row->uuid)->toString(), + ]); + + return static::tr([ + static::td([ + Link::create($row->service_set, $url), + ]), + static::td($row->object_name), + ])->addAttributes(['class' => $this->getRowClasses($row)]); + } + + /** + * @return IcingaObject + */ + protected function getDummyObject() + { + if ($this->dummyObject === null) { + $type = $this->type; + $this->dummyObject = IcingaObject::createByType($type); + } + return $this->dummyObject; + } + + protected function prepareQuery() + { + $table = $this->getDummyObject()->getTableName(); + $type = $this->getType(); + + $columns = [ + 'id' => 'o.id', + 'uuid' => 'o.uuid', + 'service_set' => 'os.object_name', + 'object_name' => 'o.object_name', + 'object_type' => 'os.object_type', + 'assign_filter' => 'os.assign_filter', + 'description' => 'os.description', + ]; + + $query = $this->db()->select()->from( + ['o' => $table], + $columns + )->joinLeft( + ['os' => "icinga_{$type}_set"], + "o.{$type}_set_id = os.id", + [] + )->where('o.host_id IS NULL'); + + $nameFilter = new FilterByNameRestriction( + $this->connection(), + $this->auth, + "{$type}_set" + ); + $nameFilter->applyToQuery($query, 'os'); + + if ($this->branchUuid) { + $columns['branch_uuid'] = 'bos.branch_uuid'; + $conn = $this->connection(); + if ($conn->isPgsql()) { + $columns['imports'] = 'CONCAT(\'[\', ARRAY_TO_STRING(ARRAY_AGG' + . '(CONCAT(\'"\', sub_o.object_name, \'"\')), \',\'), \']\')'; + } else { + $columns['imports'] = 'CONCAT(\'[\', ' + . 'GROUP_CONCAT(CONCAT(\'"\', sub_o.object_name, \'"\')), \']\')'; + } + + $columns = $this->branchifyColumns($columns); + $this->stripSearchColumnAliases(); + + $query->reset('columns'); + $right = clone($query); + $conn = $this->connection(); + + $query->columns($columns)->joinLeft( + ['bos' => "branched_icinga_{$type}_set"], + // TODO: PgHexFunc + $this->db()->quoteInto( + 'bos.uuid = os.uuid AND bos.branch_uuid = ?', + $conn->quoteBinary($this->branchUuid->getBytes()) + ), + [] + )->joinLeft( + ['oi' => $table . '_inheritance'], + 'o.id = oi.' . $this->getType() . '_id', + [] + )->joinLeft( + ['sub_o' => $table], + 'sub_o.id = oi.parent_' . $this->getType() . '_id', + [] + )->where("(bos.branch_deleted IS NULL OR bos.branch_deleted = 'n')"); + + $columns['imports'] = 'bo.imports'; + $right->columns($columns)->joinRight( + ['bos' => "branched_icinga_{$type}_set"], + 'bos.uuid = os.uuid', + [] + ) + ->where('os.uuid IS NULL') + ->where('bos.branch_uuid = ?', $conn->quoteBinary($this->branchUuid->getBytes())); + $query->group('COALESCE(os.uuid, bos.uuid)'); + $right->group('COALESCE(os.uuid, bos.uuid)'); + if ($conn->isPgsql()) { + // This is ugly, might want to modify the query - even a subselect looks better + $query->group('bos.uuid')->group('os.uuid')->group('os.id')->group('bos.branch_uuid')->group('o.id'); + $right->group('bos.uuid')->group('os.uuid')->group('os.id')->group('bos.branch_uuid')->group('o.id'); + } + $right->joinLeft( + ['bo' => "branched_icinga_{$type}"], + "bo.{$type}_set = bos.object_name", + [] + )->group(['bo.object_name', 'o.object_name', 'bo.uuid', 'bo.imports']); + $query->joinLeft( + ['bo' => "branched_icinga_{$type}"], + "bo.{$type}_set = bos.object_name", + [] + )->group(['bo.object_name', 'o.object_name', 'bo.uuid']); + + $query = $this->db()->select()->union([ + 'l' => new DbSelectParenthesis($query), + 'r' => new DbSelectParenthesis($right), + ]); + $query = $this->db()->select()->from(['u' => $query]); + $query->order('object_name')->limit(100); + + $query + ->group('uuid') + ->where('object_type = ?', 'template') + ->order('object_name'); + if ($conn->isPgsql()) { + $query + ->group('uuid') + ->group('id') + ->group('imports') + ->group('branch_uuid') + ->group('object_name') + ->group('object_type') + ->group('assign_filter') + ->group('description') + ->group('service_set'); + } + } else { + $query + ->where('o.object_type = ?', 'object') + ->order('os.object_name'); + } + + return $query; + } + + /** + * @return Db + */ + public function connection() + { + return parent::connection(); + } +} diff --git a/library/Director/Web/Table/ReadOnlyFormAvpTable.php b/library/Director/Web/Table/ReadOnlyFormAvpTable.php deleted file mode 100644 index c3b44f3..0000000 --- a/library/Director/Web/Table/ReadOnlyFormAvpTable.php +++ /dev/null @@ -1,113 +0,0 @@ -<?php - -namespace Icinga\Module\Director\Web\Table; - -use Icinga\Module\Director\PlainObjectRenderer; -use Icinga\Module\Director\Web\Form\QuickForm; -use Zend_Form_Element as ZfElement; -use Zend_Form_DisplayGroup as ZfDisplayGroup; - -class ReadOnlyFormAvpTable -{ - protected $form; - - public function __construct(QuickForm $form) - { - $this->form = $form; - } - - protected function renderDisplayGroups(QuickForm $form) - { - $html = ''; - - foreach ($form->getDisplayGroups() as $group) { - $elements = $this->filterGroupElements($group); - - if (empty($elements)) { - continue; - } - - $html .= '<tr><th colspan="2" style="text-align: right">' . $group->getLegend() . '</th></tr>'; - $html .= $this->renderElements($elements); - } - - return $html; - } - - /** - * @param ZfDisplayGroup $group - * @return ZfElement[] - */ - protected function filterGroupElements(ZfDisplayGroup $group) - { - $blacklist = array('disabled', 'assign_filter'); - $elements = array(); - /** @var ZfElement $element */ - foreach ($group->getElements() as $element) { - if ($element->getValue() === null) { - continue; - } - - if ($element->getType() === 'Zend_Form_Element_Hidden') { - continue; - } - - if (in_array($element->getName(), $blacklist)) { - continue; - } - - - $elements[] = $element; - } - - return $elements; - } - - protected function renderElements($elements) - { - $html = ''; - foreach ($elements as $element) { - $html .= $this->renderElement($element); - } - - return $html; - } - - /** - * @param ZfElement $element - * - * @return string - */ - protected function renderElement(ZfElement $element) - { - $value = $element->getValue(); - return '<tr><th>' - . $this->escape($element->getLabel()) - . '</th><td>' - . $this->renderValue($value) - . '</td></tr>'; - } - - protected function renderValue($value) - { - if (is_string($value)) { - return $this->escape($value); - } elseif (is_array($value)) { - return $this->escape(implode(', ', $value)); - } - return $this->escape(PlainObjectRenderer::render($value)); - } - - protected function escape($string) - { - return htmlspecialchars($string); - } - - public function render() - { - $this->form->initializeForObject(); - return '<table class="name-value-table">' . "\n" - . $this->renderDisplayGroups($this->form) - . '</table>'; - } -} diff --git a/library/Director/Web/Table/ServiceTemplateUsageTable.php b/library/Director/Web/Table/ServiceTemplateUsageTable.php index 82f9643..c2806f6 100644 --- a/library/Director/Web/Table/ServiceTemplateUsageTable.php +++ b/library/Director/Web/Table/ServiceTemplateUsageTable.php @@ -2,6 +2,9 @@ namespace Icinga\Module\Director\Web\Table; +use Icinga\Authentication\Auth; +use Icinga\Module\Director\Db; + class ServiceTemplateUsageTable extends TemplateUsageTable { public function getTypes() @@ -10,18 +13,27 @@ class ServiceTemplateUsageTable extends TemplateUsageTable 'templates' => $this->translate('Templates'), 'objects' => $this->translate('Objects'), 'applyrules' => $this->translate('Apply Rules'), - // 'setmembers' => $this->translate('Set Members'), + 'setmembers' => $this->translate('Set Members'), ]; } - protected function getTypeSummaryDefinitions() + protected function getSummaryTables(string $templateType, Db $connection) { + $auth = Auth::getInstance(); return [ - 'templates' => $this->getSummaryLine('template'), - 'objects' => $this->getSummaryLine('object'), - 'applyrules' => $this->getSummaryLine('apply', 'o.service_set_id IS NULL'), - // TODO: re-enable - // 'setmembers' => $this->getSummaryLine('apply', 'o.service_set_id IS NOT NULL'), + 'templates' => TemplatesTable::create( + $templateType, + $connection + ), + 'objects' => ObjectsTable::create($templateType, $connection, $this->auth) + ->setBranchUuid($this->branchUuid), + 'applyrules' => ApplyRulesTable::create($templateType, $connection) + ->setBranchUuid($this->branchUuid), + 'setmembers' => ObjectsTableSetMembers::create( + $templateType, + $connection, + $auth + ) ]; } } diff --git a/library/Director/Web/Table/SyncRunTable.php b/library/Director/Web/Table/SyncRunTable.php index e08aad7..19f2678 100644 --- a/library/Director/Web/Table/SyncRunTable.php +++ b/library/Director/Web/Table/SyncRunTable.php @@ -2,22 +2,17 @@ namespace Icinga\Module\Director\Web\Table; -use gipfl\Format\LocalTimeFormat; use Icinga\Module\Director\Objects\SyncRule; use gipfl\IcingaWeb2\Link; -use gipfl\IcingaWeb2\Table\ZfQueryBasedTable; -class SyncRunTable extends ZfQueryBasedTable +class SyncRunTable extends IntlZfQueryBasedTable { /** @var SyncRule */ protected $rule; - protected $timeFormat; - public function __construct(SyncRule $rule) { parent::__construct($rule->getConnection()); - $this->timeFormat = new LocalTimeFormat(); $this->getAttributes() ->set('data-base-target', '_self') ->add('class', 'history'); @@ -31,7 +26,7 @@ class SyncRunTable extends ZfQueryBasedTable return $this::tr([ $this::td($this->makeSummary($row)), $this::td(new Link( - $this->timeFormat->getTime($time), + $this->getTime($time), 'director/syncrule/history', [ 'id' => $row->rule_id, diff --git a/library/Director/Web/Table/TableWithBranchSupport.php b/library/Director/Web/Table/TableWithBranchSupport.php index 7c5b15c..9e412c3 100644 --- a/library/Director/Web/Table/TableWithBranchSupport.php +++ b/library/Director/Web/Table/TableWithBranchSupport.php @@ -56,6 +56,9 @@ trait TableWithBranchSupport $result[$alias] = $column; } + if (isset($result['count_services'])) { + $result['count_services'] = 'COUNT(DISTINCT COALESCE(o.uuid, bo.uuid))'; + } return $result; } diff --git a/library/Director/Web/Table/TemplateUsageTable.php b/library/Director/Web/Table/TemplateUsageTable.php index 66e56ea..376a499 100644 --- a/library/Director/Web/Table/TemplateUsageTable.php +++ b/library/Director/Web/Table/TemplateUsageTable.php @@ -2,9 +2,12 @@ namespace Icinga\Module\Director\Web\Table; +use Icinga\Authentication\Auth; use Icinga\Exception\ProgrammingError; +use Icinga\Module\Director\Db; +use Icinga\Module\Director\Db\Branch\Branch; +use Icinga\Module\Director\Db\IcingaObjectFilterHelper; use Icinga\Module\Director\Objects\IcingaObject; -use Icinga\Module\Director\Resolver\TemplateTree; use gipfl\IcingaWeb2\Link; use ipl\Html\Table; use gipfl\Translation\TranslationHelper; @@ -13,10 +16,17 @@ class TemplateUsageTable extends Table { use TranslationHelper; + use TableWithBranchSupport; + + /** @var Auth */ + protected $auth; + protected $defaultAttributes = ['class' => 'pivot']; protected $objectType; + protected $searchColumns = []; + public function getTypes() { return [ @@ -25,26 +35,22 @@ class TemplateUsageTable extends Table ]; } - protected function getTypeSummaryDefinitions() - { - return [ - 'templates' => $this->getSummaryLine('template'), - 'objects' => $this->getSummaryLine('object'), - ]; - } - /** * @param IcingaObject $template + * @param Branch|null $branch + * * @return TemplateUsageTable + * + * @throws ProgrammingError */ - public static function forTemplate(IcingaObject $template) + public static function forTemplate(IcingaObject $template, Auth $auth, Branch $branch = null) { $type = ucfirst($template->getShortTableName()); - $class = __NAMESPACE__ . "\\${type}TemplateUsageTable"; + $class = __NAMESPACE__ . "\\{$type}TemplateUsageTable"; if (class_exists($class)) { - return new $class($template); + return new $class($template, $auth, $branch); } else { - return new static($template); + return new static($template, $auth, $branch); } } @@ -58,8 +64,9 @@ class TemplateUsageTable extends Table ]; } - protected function __construct(IcingaObject $template) + protected function __construct(IcingaObject $template, Auth $auth, Branch $branch = null) { + $this->auth = $auth; if ($template->get('object_type') !== 'template') { throw new ProgrammingError( @@ -68,6 +75,7 @@ class TemplateUsageTable extends Table ); } + $this->setBranch($branch); $this->objectType = $objectType = $template->getShortTableName(); $types = $this->getTypes(); $usage = $this->getUsageSummary($template); @@ -85,7 +93,7 @@ class TemplateUsageTable extends Table Table::td( Link::create( $count, - "director/${objectType}template/$type", + "director/{$objectType}template/$type", [ 'name' => $template->getObjectName(), 'inheritance' => $inheritance @@ -98,6 +106,7 @@ class TemplateUsageTable extends Table } if ($used) { + $this->getHeader()->add(Table::row($this->getColumnsToBeRendered(), null, 'th')); $this->add($rows); } else { $this->add($this->translate('This template is not in use')); @@ -106,52 +115,51 @@ class TemplateUsageTable extends Table protected function getUsageSummary(IcingaObject $template) { - $id = $template->getAutoincId(); $connection = $template->getConnection(); $db = $connection->getDbAdapter(); - $oType = $this->objectType; - $tree = new TemplateTree($oType, $connection); - $ids = $tree->listDescendantIdsFor($template); - if (empty($ids)) { - $ids = [0]; + + $types = array_keys($this->getTypes()); + $direct = []; + $indirect = []; + $templateType = $template->getShortTableName(); + + foreach ($this->getSummaryTables($templateType, $connection) as $type => $summaryTable) { + $directTable = clone $summaryTable; + $inDirectTable = clone $summaryTable; + + $direct[$type] = $db->query( + $directTable + ->filterTemplate($template, IcingaObjectFilterHelper::INHERIT_DIRECT) + ->getQuery() + )->rowCount(); + $indirect[$type] = $db->query( + $inDirectTable + ->filterTemplate($template, IcingaObjectFilterHelper::INHERIT_INDIRECT) + ->getQuery() + )->rowCount(); } - $baseQuery = $db->select()->from( - ['o' => 'icinga_' . $oType], - $this->getTypeSummaryDefinitions() - )->joinLeft( - ['oi' => "icinga_${oType}_inheritance"], - "oi.${oType}_id = o.id", - [] - ); - - $query = clone($baseQuery); - $direct = $db->fetchRow( - $query->where("oi.parent_${oType}_id = ?", $id) - ); - $query = clone($baseQuery); - $indirect = $db->fetchRow( - $query->where("oi.parent_${oType}_id IN (?)", $ids) - ); - //$indirect->templates = count($ids) - 1; $total = []; - $types = array_keys($this->getTypes()); foreach ($types as $type) { - $total[$type] = $direct->$type + $indirect->$type; + $total[$type] = $direct[$type] + $indirect[$type]; } return (object) [ - 'direct' => $direct, - 'indirect' => $indirect, + 'direct' => (object) $direct, + 'indirect' => (object) $indirect, 'total' => (object) $total ]; } - protected function getSummaryLine($type, $extra = null) + protected function getSummaryTables(string $templateType, Db $connection) { - if ($extra !== null) { - $extra = " AND $extra"; - } - return "COALESCE(SUM(CASE WHEN o.object_type = '${type}'${extra} THEN 1 ELSE 0 END), 0)"; + return [ + 'templates' => TemplatesTable::create( + $templateType, + $connection + ), + 'objects' => ObjectsTable::create($templateType, $connection, $this->auth) + ->setBranchUuid($this->branchUuid) + ]; } } diff --git a/library/Director/Web/Table/TemplatesTable.php b/library/Director/Web/Table/TemplatesTable.php index be195b2..d6582fd 100644 --- a/library/Director/Web/Table/TemplatesTable.php +++ b/library/Director/Web/Table/TemplatesTable.php @@ -36,8 +36,8 @@ class TemplatesTable extends ZfQueryBasedTable implements FilterableByUsage { $type = $this->type; $this->enableMultiSelect( - "director/${type}s/edittemplates", - "director/${type}template", + "director/{$type}s/edittemplates", + "director/{$type}template", ['name'] ); } @@ -60,12 +60,12 @@ class TemplatesTable extends ZfQueryBasedTable implements FilterableByUsage $name, Html::tag( 'span', - ['style' => 'font-style: italic'], + ['class' => 'font-italic'], $this->translate(' - not in use -') ) ]; - $url = Url::fromPath("director/${type}template/usage", [ + $url = Url::fromPath("director/{$type}template/usage", [ 'name' => $name ]); @@ -101,8 +101,8 @@ class TemplatesTable extends ZfQueryBasedTable implements FilterableByUsage { $type = $this->getType(); $this->getQuery()->where( - "(EXISTS (SELECT ${type}_id FROM icinga_${type}_inheritance" - . " WHERE parent_${type}_id = o.id))" + "(EXISTS (SELECT {$type}_id FROM icinga_{$type}_inheritance" + . " WHERE parent_{$type}_id = o.id))" ); } @@ -110,8 +110,8 @@ class TemplatesTable extends ZfQueryBasedTable implements FilterableByUsage { $type = $this->getType(); $this->getQuery()->where( - "(NOT EXISTS (SELECT ${type}_id FROM icinga_${type}_inheritance" - . " WHERE parent_${type}_id = o.id))" + "(NOT EXISTS (SELECT {$type}_id FROM icinga_{$type}_inheritance" + . " WHERE parent_{$type}_id = o.id))" ); } @@ -135,8 +135,8 @@ class TemplatesTable extends ZfQueryBasedTable implements FilterableByUsage protected function prepareQuery() { $type = $this->getType(); - $used = "CASE WHEN EXISTS(SELECT 1 FROM icinga_${type}_inheritance oi" - . " WHERE oi.parent_${type}_id = o.id) THEN 'y' ELSE 'n' END"; + $used = "CASE WHEN EXISTS(SELECT 1 FROM icinga_{$type}_inheritance oi" + . " WHERE oi.parent_{$type}_id = o.id) THEN 'y' ELSE 'n' END"; $columns = [ 'object_name' => 'o.object_name', @@ -145,7 +145,7 @@ class TemplatesTable extends ZfQueryBasedTable implements FilterableByUsage 'is_used' => $used, ]; $query = $this->db()->select()->from( - ['o' => "icinga_${type}"], + ['o' => "icinga_{$type}"], $columns )->where( "o.object_type = 'template'" |