diff --git a/library/Director/Db/IcingaObjectFilterHelper.php b/library/Director/Db/IcingaObjectFilterHelper.php index 150699053..87f1cc5a0 100644 --- a/library/Director/Db/IcingaObjectFilterHelper.php +++ b/library/Director/Db/IcingaObjectFilterHelper.php @@ -5,6 +5,7 @@ use Icinga\Module\Director\Objects\IcingaObject; use Icinga\Module\Director\Resolver\TemplateTree; use InvalidArgumentException; +use Ramsey\Uuid\UuidInterface; use RuntimeException; use Zend_Db_Select as ZfSelect; @@ -46,13 +47,42 @@ public static function filterByTemplate( ZfSelect $query, $template, $tableAlias = 'o', - $inheritanceType = self::INHERIT_DIRECT + $inheritanceType = self::INHERIT_DIRECT, + UuidInterface $branchuuid = null ) { $i = $tableAlias . 'i'; $o = $tableAlias; $type = $template->getShortTableName(); $db = $template->getDb(); $id = static::wantId($template); + + if ($branchuuid) { + if ($inheritanceType === self::INHERIT_DIRECT) { + return $query->where('imports LIKE \'%"' . $template->getObjectName() . '"%\''); + } elseif ($inheritanceType === self::INHERIT_INDIRECT + || $inheritanceType === self::INHERIT_DIRECT_OR_INDIRECT + ) { + $tree = new TemplateTree($type, $template->getConnection()); + $templateNames = $tree->getDescendantsFor($template); + + if ($inheritanceType === self::INHERIT_DIRECT_OR_INDIRECT) { + $templateNames[] = $template->getObjectName(); + } + + if (empty($templateNames)) { + $condition = '(1 = 0)'; + } else { + $condition = 'imports LIKE \'%"' . array_pop($templateNames) . '"%\''; + + foreach ($templateNames as $templateName) { + $condition .= " OR imports LIKE '%\"$templateName\"%'"; + } + } + + return $query->where($condition); + } + } + $sub = $db->select()->from( array($i => "icinga_{$type}_inheritance"), array('e' => '(1)') diff --git a/library/Director/Web/Controller/ObjectsController.php b/library/Director/Web/Controller/ObjectsController.php index 8fffefb9b..cc003a716 100644 --- a/library/Director/Web/Controller/ObjectsController.php +++ b/library/Director/Web/Controller/ObjectsController.php @@ -157,7 +157,7 @@ protected function getTable() */ protected function getApplyRulesTable() { - $table = new ApplyRulesTable($this->db()); + $table = (new ApplyRulesTable($this->db()))->setBranch($this->getBranch()); $table->setType($this->getType()) ->setBaseObjectUrl($this->getBaseObjectUrl()); $this->eventuallyFilterCommand($table); diff --git a/library/Director/Web/Controller/TemplateController.php b/library/Director/Web/Controller/TemplateController.php index 1343437cf..3572c3ccd 100644 --- a/library/Director/Web/Controller/TemplateController.php +++ b/library/Director/Web/Controller/TemplateController.php @@ -11,6 +11,7 @@ use Icinga\Module\Director\Web\Controller\Extension\DirectorDb; use Icinga\Module\Director\Web\Table\ApplyRulesTable; use Icinga\Module\Director\Web\Table\ObjectsTable; +use Icinga\Module\Director\Web\Table\ObjectsTableSetMembers; use Icinga\Module\Director\Web\Table\TemplatesTable; use Icinga\Module\Director\Web\Table\TemplateUsageTable; use Icinga\Module\Director\Web\Tabs\ObjectTabs; @@ -24,6 +25,8 @@ abstract class TemplateController extends CompatController { use DirectorDb; + use BranchHelper; + /** @var IcingaObject */ protected $template; @@ -42,7 +45,28 @@ public function objectsAction() ObjectsTable::create($this->getType(), $this->db()) ->setAuth($this->Auth()) + ->setBranch($this->getBranch()) + ->setBaseObjectUrl($this->getBaseObjectUrl()) + ->filterTemplate($template, $this->getInheritance()) + ->renderTo($this); + } + + public function setmembersAction() + { + $template = $this->requireTemplate(); + $plural = $this->getTranslatedPluralType(); + $this + ->addSingleTab($plural) + ->setAutorefreshInterval(10) + ->addTitle( + $this->translate('%s in service sets based on %s'), + $plural, + $template->getObjectName() + )->addBackToUsageLink($template); + + ObjectsTableSetMembers::create($this->getType(), $this->db(), $this->Auth()) ->setBaseObjectUrl($this->getBaseObjectUrl()) + ->setBranch($this->getBranch()) ->filterTemplate($template, $this->getInheritance()) ->renderTo($this); } @@ -61,6 +85,7 @@ public function applyrulesAction() ApplyRulesTable::create($type, $this->db()) ->setBaseObjectUrl($this->getBaseObjectUrl()) + ->setBranch($this->getBranch()) ->filterTemplate($template, $this->params->get('inheritance', 'direct')) ->renderTo($this); } @@ -189,7 +214,7 @@ public function usageAction() try { $this->content()->add( - TemplateUsageTable::forTemplate($template) + TemplateUsageTable::forTemplate($template, $this->getBranch()) ); } catch (NestingError $e) { $this->content()->add(Hint::error($e->getMessage())); diff --git a/library/Director/Web/Table/ApplyRulesTable.php b/library/Director/Web/Table/ApplyRulesTable.php index 985b5f3b9..a5379b5da 100644 --- a/library/Director/Web/Table/ApplyRulesTable.php +++ b/library/Director/Web/Table/ApplyRulesTable.php @@ -6,6 +6,7 @@ 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 @@ class ApplyRulesTable extends ZfQueryBasedTable { + use TableWithBranchSupport; + protected $searchColumns = [ 'o.object_name', 'o.assign_filter', @@ -94,10 +97,14 @@ public function renderRow($row) // 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 @@ public function filterTemplate( $this->getQuery(), $template, 'o', - $inheritance + $inheritance, + $this->branchUuid ); return $this; @@ -196,6 +204,15 @@ protected function applyRestrictions(ZfSelect $query) 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 @@ public function prepareQuery() '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 @@ public function prepareQuery() 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/DependencyTemplateUsageTable.php b/library/Director/Web/Table/DependencyTemplateUsageTable.php index d7537c58a..2c1de501f 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 @@ public function getTypes() ]; } - 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/HostTemplateUsageTable.php b/library/Director/Web/Table/HostTemplateUsageTable.php index 2d1ee2f1a..672691fb6 100644 --- a/library/Director/Web/Table/HostTemplateUsageTable.php +++ b/library/Director/Web/Table/HostTemplateUsageTable.php @@ -11,12 +11,4 @@ public function getTypes() '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 ee8a3efe3..981cd5380 100644 --- a/library/Director/Web/Table/IcingaServiceSetServiceTable.php +++ b/library/Director/Web/Table/IcingaServiceSetServiceTable.php @@ -123,6 +123,10 @@ protected function getServiceLink($row) ]; $url = 'director/host/servicesetservice'; } else { + if (is_resource($row->uuid)) { + $row->uuid =stream_get_contents($row->uuid); + } + $params = [ 'uuid' => Uuid::fromBytes($row->uuid)->toString(), ]; diff --git a/library/Director/Web/Table/NotificationTemplateUsageTable.php b/library/Director/Web/Table/NotificationTemplateUsageTable.php index da411a3a0..d8cd3d86d 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 @@ public function getTypes() ]; } - 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 e9960af26..ad022ae41 100644 --- a/library/Director/Web/Table/ObjectSetTable.php +++ b/library/Director/Web/Table/ObjectSetTable.php @@ -154,12 +154,12 @@ protected function prepareQuery() ['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 @@ -185,8 +185,9 @@ protected function prepareQuery() ->group('object_type') ->group('assign_filter') ->group('description') + ->group('service_object_name') ->group('count_services'); - }; + } } else { // Disabled for now, check for correctness: // $query->joinLeft( diff --git a/library/Director/Web/Table/ObjectsTable.php b/library/Director/Web/Table/ObjectsTable.php index 1393649ab..7ed0bbb67 100644 --- a/library/Director/Web/Table/ObjectsTable.php +++ b/library/Director/Web/Table/ObjectsTable.php @@ -14,6 +14,7 @@ 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 @@ -124,11 +125,17 @@ public function filterTemplate( 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; @@ -279,7 +286,39 @@ protected function prepareQuery() $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"], diff --git a/library/Director/Web/Table/ObjectsTableService.php b/library/Director/Web/Table/ObjectsTableService.php index 2d4ad41b1..c9bce72ba 100644 --- a/library/Director/Web/Table/ObjectsTableService.php +++ b/library/Director/Web/Table/ObjectsTableService.php @@ -203,8 +203,14 @@ public function prepareQuery() '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 000000000..b3d455b0b --- /dev/null +++ b/library/Director/Web/Table/ObjectsTableSetMembers.php @@ -0,0 +1,254 @@ +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' => $row->uuid, + ]); + + 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/ServiceTemplateUsageTable.php b/library/Director/Web/Table/ServiceTemplateUsageTable.php index 82f9643e7..5829d3051 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,28 @@ public function getTypes() '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) + ->setAuth($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/TemplateUsageTable.php b/library/Director/Web/Table/TemplateUsageTable.php index c6c27d7eb..4a1f2e4b4 100644 --- a/library/Director/Web/Table/TemplateUsageTable.php +++ b/library/Director/Web/Table/TemplateUsageTable.php @@ -2,9 +2,11 @@ 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\Objects\IcingaObject; -use Icinga\Module\Director\Resolver\TemplateTree; use gipfl\IcingaWeb2\Link; use ipl\Html\Table; use gipfl\Translation\TranslationHelper; @@ -13,10 +15,14 @@ class TemplateUsageTable extends Table { use TranslationHelper; + use TableWithBranchSupport; + protected $defaultAttributes = ['class' => 'pivot']; protected $objectType; + protected $searchColumns = []; + public function getTypes() { return [ @@ -25,26 +31,22 @@ public function getTypes() ]; } - 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, Branch $branch = null) { $type = ucfirst($template->getShortTableName()); $class = __NAMESPACE__ . "\\{$type}TemplateUsageTable"; if (class_exists($class)) { - return new $class($template); + return new $class($template, $branch); } else { - return new static($template); + return new static($template, $branch); } } @@ -58,7 +60,7 @@ public function getColumnsToBeRendered() ]; } - protected function __construct(IcingaObject $template) + protected function __construct(IcingaObject $template, Branch $branch = null) { if ($template->get('object_type') !== 'template') { @@ -68,6 +70,7 @@ protected function __construct(IcingaObject $template) ); } + $this->setBranch($branch); $this->objectType = $objectType = $template->getShortTableName(); $types = $this->getTypes(); $usage = $this->getUsageSummary($template); @@ -107,52 +110,45 @@ protected function __construct(IcingaObject $template) 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) { + $direct[$type] = $db + ->query($summaryTable->filterTemplate($template, 'direct')->getQuery()) + ->rowCount(); + $indirect[$type] = $db + ->query($summaryTable->filterTemplate($template, '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) + ->setAuth(Auth::getInstance()) + ->setBranchUuid($this->branchUuid) + ]; } } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 6c3abbf76..e58130b5a 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -585,6 +585,16 @@ parameters: count: 1 path: library/Director/Web/Controller/ObjectsController.php + - + message: "#^Access to an undefined property Icinga\\\\Module\\\\Director\\\\Web\\\\Controller\\\\ActionController\\:\\:\\$branch\\.$#" + count: 1 + path: library/Director/Web/Controller/TemplateController.php + + - + message: "#^Call to an undefined method Icinga\\\\Module\\\\Director\\\\Web\\\\Controller\\\\ActionController\\:\\:getBranchStore\\(\\)\\.$#" + count: 1 + path: library/Director/Web/Controller/TemplateController.php + - message: "#^Undefined variable\\: \\$elementValue$#" count: 1