params->get('name'); return $this->tabs()->add('show', [ 'label' => $this->translate('Basket'), 'url' => 'director/basket', 'urlParams' => ['name' => $name] ])->add('snapshots', [ 'label' => $this->translate('Snapshots'), 'url' => 'director/basket/snapshots', 'urlParams' => ['name' => $name] ]); } /** * @throws \Icinga\Exception\NotFoundError * @throws \Icinga\Exception\MissingParameterException */ public function indexAction() { $this->actions()->add( Link::create( $this->translate('Back'), 'director/baskets', null, ['class' => 'icon-left-big'] ) ); $basket = $this->requireBasket(); $this->basketTabs()->activate('show'); $this->addTitle($basket->get('basket_name')); if ($basket->isEmpty()) { $this->content()->add(Hint::info($this->translate('This basket is empty'))); } $this->content()->add( (new BasketForm())->setObject($basket)->handleRequest() ); } /** * @throws \Icinga\Exception\MissingParameterException */ public function addAction() { $this->actions()->add( Link::create( $this->translate('Baskets'), 'director/baskets', null, ['class' => 'icon-tag'] ) ); $this->addSingleTab($this->translate('Add to Basket')); $this->addTitle($this->translate('Add chosen objects to a Configuration Basket')); $form = new AddToBasketForm(); $form->setDb($this->db()) ->setType($this->params->getRequired('type')) ->setNames($this->url()->getParams()->getValues('names')) ->handleRequest(); $this->content()->add($form); } public function createAction() { $this->actions()->add( Link::create( $this->translate('back'), 'director/baskets', null, ['class' => 'icon-left-big'] ) ); $this->addSingleTab($this->translate('Create Basket')); $this->addTitle($this->translate('Create a new Configuration Basket')); $form = (new BasketForm()) ->setDb($this->db()) ->handleRequest(); $this->content()->add($form); } public function uploadAction() { $this->actions()->add( Link::create( $this->translate('back'), 'director/baskets', null, ['class' => 'icon-left-big'] ) ); $this->addSingleTab($this->translate('Upload a Basket')); $this->addTitle($this->translate('Upload a Configuration Basket')); $form = (new BasketUploadForm()) ->setDb($this->db()) ->handleRequest(); $this->content()->add($form); } public function uploadSnapshotAction() { $basket = Basket::load($this->params->get('name'), $this->db()); $this->actions()->add( Link::create( $this->translate('back'), 'director/basket/snapshots', ['name' => $basket->get('basket_name')], ['class' => 'icon-left-big'] ) ); $this->basketTabs()->activate('snapshots'); $this->addTitle($this->translate('Upload a Configuration Basket Snapshot')); $form = (new BasketUploadForm()) ->setObject($basket) ->setDb($this->db()) ->handleRequest(); $this->content()->add($form); } /** * @throws \Icinga\Exception\NotFoundError */ public function snapshotsAction() { $name = $this->params->get('name'); if ($name === null || $name === '') { $basket = null; } else { $basket = Basket::load($name, $this->db()); } if ($basket === null) { $this->addTitle($this->translate('Basket Snapshots')); $this->addSingleTab($this->translate('Snapshots')); } else { $this->addTitle(sprintf( $this->translate('%s: Snapshots'), $basket->get('basket_name') )); $this->basketTabs()->activate('snapshots'); $this->actions()->add(Link::create( $this->translate('Upload'), 'director/basket/upload-snapshot', ['name' => $basket->get('basket_name')], ['class' => 'icon-upload'] )); } if ($basket !== null) { $this->content()->add( (new BasketCreateSnapshotForm()) ->setBasket($basket) ->handleRequest() ); } $table = new BasketSnapshotTable($this->db()); if ($basket !== null) { $table->setBasket($basket); } $table->renderTo($this); } /** * @throws \Icinga\Exception\MissingParameterException * @throws \Icinga\Exception\NotFoundError */ public function snapshotAction() { $basket = $this->requireBasket(); $snapshot = BasketSnapshot::load([ 'basket_uuid' => $basket->get('uuid'), 'ts_create' => $this->params->getRequired('ts'), ], $this->db()); $snapSum = bin2hex($snapshot->get('content_checksum')); if ($this->params->get('action') === 'download') { $this->getResponse()->setHeader('Content-Type', 'application/json', true); $this->getResponse()->setHeader('Content-Disposition', sprintf( 'attachment; filename=Director-Basket_%s_%s.json', str_replace([' ', '"'], ['_', '_'], iconv( 'UTF-8', 'ISO-8859-1//IGNORE', $basket->get('basket_name') )), substr($snapSum, 0, 7) )); echo $snapshot->getJsonDump(); return; } $this->addTitle( $this->translate('%s: %s (Snapshot)'), $basket->get('basket_name'), substr($snapSum, 0, 7) ); $this->actions()->add([ Link::create( $this->translate('Show Basket'), 'director/basket', ['name' => $basket->get('basket_name')], ['data-base-target' => '_next'] ), Link::create( $this->translate('Restore'), $this->url()->with('action', 'restore'), null, ['class' => 'icon-rewind'] ), Link::create( $this->translate('Download'), $this->url() ->with([ 'action' => 'download', 'dbResourceName' => $this->getDbResourceName() ]), null, [ 'class' => 'icon-download', 'target' => '_blank' ] ), ]); $properties = new NameValueTable(); $properties->addNameValuePairs([ $this->translate('Created') => DateFormatter::formatDateTime($snapshot->get('ts_create') / 1000), $this->translate('Content Checksum') => bin2hex($snapshot->get('content_checksum')), ]); $this->content()->add($properties); if ($this->params->get('action') === 'restore') { $form = new RestoreBasketForm(); $form ->setSnapshot($snapshot) ->handleRequest(); $this->content()->add($form); $targetDbName = $form->getValue('target_db'); $connection = $form->getDb(); } else { $targetDbName = null; $connection = $this->db(); } $this->addSingleTab($this->translate('Snapshot')); $diff = new BasketDiff($snapshot, $connection); foreach ($diff->getBasketObjects() as $type => $objects) { if ($type === 'Datafield') { // TODO: we should now be able to show all fields and link // to a "diff" for the ones that should be created // $this->content()->add(Html::tag('h2', sprintf('+%d Datafield(s)', count($objects)))); continue; } $table = new NameValueTable(); $table->addAttributes([ 'class' => ['table-basket-changes', 'table-row-selectable'], 'data-base-target' => '_next', ]); foreach ($objects as $key => $object) { $linkParams = [ 'name' => $basket->get('basket_name'), 'checksum' => $this->params->get('checksum'), 'ts' => $this->params->get('ts'), 'type' => $type, 'key' => $key, ]; if ($targetDbName !== null) { $linkParams['target_db'] = $targetDbName; } try { if ($uuid = $object->uuid ?? null) { $uuid = Uuid::fromString($uuid); } if ($diff->hasCurrentInstance($type, $key, $uuid)) { if ($diff->hasChangedFor($type, $key, $uuid)) { $link = Link::create( $this->translate('modified'), 'director/basket/snapshotobject', $linkParams, ['class' => 'basket-modified'] ); } else { $link = Html::tag( 'span', ['class' => 'basket-unchanged'], $this->translate('unchanged') ); } } else { $link = Link::create( $this->translate('new'), 'director/basket/snapshotobject', $linkParams, ['class' => 'basket-new'] ); } $table->addNameValueRow($key, $link); } catch (Exception $e) { $table->addNameValueRow( $key, Html::tag('a', sprintf( '%s (%s:%d)', $e->getMessage(), basename($e->getFile()), $e->getLine() )) ); } } $this->content()->add(Html::tag('h2', $type)); $this->content()->add($table); } } /** * @throws \Icinga\Exception\MissingParameterException * @throws \Icinga\Exception\NotFoundError */ public function snapshotobjectAction() { $basket = $this->requireBasket(); $snapshot = BasketSnapshot::load([ 'basket_uuid' => $basket->get('uuid'), 'ts_create' => $this->params->getRequired('ts'), ], $this->db()); $snapshotUrl = $this->url()->without('type')->without('key')->setPath('director/basket/snapshot'); $type = $this->params->get('type'); $key = $this->params->get('key'); $this->addTitle($this->translate('Single Object Diff')); $this->content()->add(Hint::info(Html::sprintf( $this->translate('Comparing %s "%s" from Snapshot "%s" to current config'), $type, $key, Link::create( substr(bin2hex($snapshot->get('content_checksum')), 0, 7), $snapshotUrl, null, ['data-base-target' => '_next'] ) ))); $this->actions()->add([ Link::create( $this->translate('back'), $snapshotUrl, null, ['class' => 'icon-left-big'] ), /* Link::create( $this->translate('Restore'), $this->url()->with('action', 'restore'), null, ['class' => 'icon-rewind'] ) */ ]); $this->addSingleTab($this->translate('Snapshot')); $targetDbName = $this->params->get('target_db'); if ($targetDbName === null) { $connection = $this->db(); } else { $connection = Db::fromResourceName($targetDbName); } $diff = new BasketDiff($snapshot, $connection); $object = $diff->getBasketObject($type, $key); if ($uuid = $object->uuid ?? null) { $uuid = Uuid::fromString($uuid); } $basketJson = $diff->getBasketString($type, $key); $currentJson = $diff->getCurrentString($type, $key, $uuid); if ($currentJson === $basketJson) { $this->content()->add([ Hint::ok('Basket equals current object'), Html::tag('pre', $currentJson) ]); } else { $this->content()->add(new InlineDiff(new PhpDiff($currentJson, $basketJson))); } } /** * @return Basket * @throws \Icinga\Exception\MissingParameterException * @throws \Icinga\Exception\NotFoundError */ protected function requireBasket() { return Basket::load($this->params->getRequired('name'), $this->db()); } }