mirror of
https://github.com/Icinga/icingaweb2-module-director.git
synced 2025-07-27 07:44:05 +02:00
config: refactor controller, tables, get rid...
...of view scripts, add new Widget helper classes
This commit is contained in:
parent
b016b1954b
commit
04a78f734a
@ -10,13 +10,20 @@ use Icinga\Module\Director\IcingaConfig\IcingaConfig;
|
|||||||
use Icinga\Module\Director\Objects\DirectorDeploymentLog;
|
use Icinga\Module\Director\Objects\DirectorDeploymentLog;
|
||||||
use Icinga\Module\Director\Settings;
|
use Icinga\Module\Director\Settings;
|
||||||
use Icinga\Module\Director\Web\Table\ActivityLogTable;
|
use Icinga\Module\Director\Web\Table\ActivityLogTable;
|
||||||
|
use Icinga\Module\Director\Web\Table\ConfigFileDiffTable;
|
||||||
use Icinga\Module\Director\Web\Table\DeploymentLogTable;
|
use Icinga\Module\Director\Web\Table\DeploymentLogTable;
|
||||||
|
use Icinga\Module\Director\Web\Table\GeneratedConfigFileTable;
|
||||||
use Icinga\Module\Director\Util;
|
use Icinga\Module\Director\Util;
|
||||||
use Icinga\Module\Director\Web\Controller\ActionController;
|
use Icinga\Module\Director\Web\Controller\ActionController;
|
||||||
use Icinga\Module\Director\Web\Tabs\InfraTabs;
|
use Icinga\Module\Director\Web\Tabs\InfraTabs;
|
||||||
|
use Icinga\Module\Director\Web\Widget\DeployedConfigInfoHeader;
|
||||||
|
use Icinga\Module\Director\Web\Widget\ShowConfigFile;
|
||||||
use Icinga\Web\Notification;
|
use Icinga\Web\Notification;
|
||||||
use Icinga\Web\Url;
|
use Icinga\Web\Url;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use ipl\Html\Html;
|
||||||
|
use ipl\Html\HtmlString;
|
||||||
|
use ipl\Html\Icon;
|
||||||
use ipl\Html\Link;
|
use ipl\Html\Link;
|
||||||
|
|
||||||
class ConfigController extends ActionController
|
class ConfigController extends ActionController
|
||||||
@ -33,14 +40,16 @@ class ConfigController extends ActionController
|
|||||||
$this->addTitle($this->translate('Deployments'));
|
$this->addTitle($this->translate('Deployments'));
|
||||||
try {
|
try {
|
||||||
if (DirectorDeploymentLog::hasUncollected($this->db())) {
|
if (DirectorDeploymentLog::hasUncollected($this->db())) {
|
||||||
$this->setAutorefreshInterval(5);
|
$this->setAutorefreshInterval(3);
|
||||||
$this->api()->collectLogFiles($this->db());
|
$this->api()->collectLogFiles($this->db());
|
||||||
} else {
|
} else {
|
||||||
$this->setAutorefreshInterval(10);
|
$this->setAutorefreshInterval(20);
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
// No problem, Icinga might be reloading
|
// No problem, Icinga might be reloading
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: a form!
|
||||||
$this->actions()->add(Link::create(
|
$this->actions()->add(Link::create(
|
||||||
$this->translate('Render config'),
|
$this->translate('Render config'),
|
||||||
'director/config/store',
|
'director/config/store',
|
||||||
@ -88,36 +97,6 @@ class ConfigController extends ActionController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function deploymentSucceeded($checksum)
|
|
||||||
{
|
|
||||||
if ($this->getRequest()->isApiRequest()) {
|
|
||||||
$this->sendJson($this->getResponse(), (object) array('checksum' => $checksum));
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
$url = Url::fromPath('director/config/deployments');
|
|
||||||
Notification::success(
|
|
||||||
$this->translate('Config has been submitted, validation is going on')
|
|
||||||
);
|
|
||||||
$this->redirectNow($url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function deploymentFailed($checksum, $error = null)
|
|
||||||
{
|
|
||||||
$extra = $error ? ': ' . $error: '';
|
|
||||||
|
|
||||||
if ($this->getRequest()->isApiRequest()) {
|
|
||||||
$this->sendJsonError($this->getResponse(), 'Config deployment failed' . $extra);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
$url = Url::fromPath('director/config/files', array('checksum' => $checksum));
|
|
||||||
Notification::error(
|
|
||||||
$this->translate('Config deployment failed') . $extra
|
|
||||||
);
|
|
||||||
$this->redirectNow($url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function activitiesAction()
|
public function activitiesAction()
|
||||||
{
|
{
|
||||||
$this->assertPermission('director/audit');
|
$this->assertPermission('director/audit');
|
||||||
@ -173,105 +152,79 @@ class ConfigController extends ActionController
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show all files for a given config
|
/**
|
||||||
|
* Show all files for a given config
|
||||||
|
*/
|
||||||
public function filesAction()
|
public function filesAction()
|
||||||
{
|
{
|
||||||
$this->assertPermission('director/showconfig');
|
$this->assertPermission('director/showconfig');
|
||||||
|
$config = IcingaConfig::load(
|
||||||
$this->view->title = $this->translate('Generated config');
|
Util::hex2binary($this->params->getRequired('checksum')),
|
||||||
$tabs = $this->getTabs();
|
|
||||||
|
|
||||||
if ($deploymentId = $this->view->deploymentId = $this->params->get('deployment_id')) {
|
|
||||||
$tabs->add('deployment', array(
|
|
||||||
'label' => $this->translate('Deployment'),
|
|
||||||
'url' => 'director/deployment',
|
|
||||||
'urlParams' => array(
|
|
||||||
'id' => $deploymentId
|
|
||||||
)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
$tabs->add('config', array(
|
|
||||||
'label' => $this->translate('Config'),
|
|
||||||
'url' => $this->getRequest()->getUrl(),
|
|
||||||
))->activate('config');
|
|
||||||
|
|
||||||
$checksum = $this->params->get('checksum');
|
|
||||||
|
|
||||||
$this->view->deployForm = $this->loadForm('DeployConfig')
|
|
||||||
->setAttrib('class', 'inline')
|
|
||||||
->setDb($this->db())
|
|
||||||
->setApi($this->api())
|
|
||||||
->setChecksum($checksum)
|
|
||||||
->setDeploymentId($deploymentId)
|
|
||||||
->handleRequest();
|
|
||||||
|
|
||||||
$this->view->table = $this
|
|
||||||
->loadTable('GeneratedConfigFile')
|
|
||||||
->setActiveFilename($this->params->get('active_file'))
|
|
||||||
->setConnection($this->db())
|
|
||||||
->setConfigChecksum($checksum);
|
|
||||||
|
|
||||||
if ($deploymentId) {
|
|
||||||
$this->view->table->setDeploymentId($deploymentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->view->config = IcingaConfig::load(
|
|
||||||
Util::hex2binary($this->params->get('checksum')),
|
|
||||||
$this->db()
|
$this->db()
|
||||||
);
|
);
|
||||||
|
$deploymentId = $this->params->get('deployment_id');
|
||||||
|
|
||||||
|
$tabs = $this->tabs();
|
||||||
|
if ($deploymentId) {
|
||||||
|
$tabs->add('deployment', [
|
||||||
|
'label' => $this->translate('Deployment'),
|
||||||
|
'url' => 'director/deployment',
|
||||||
|
'urlParams' => ['id' => $deploymentId]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tabs->add('config', [
|
||||||
|
'label' => $this->translate('Config'),
|
||||||
|
'url' => $this->url(),
|
||||||
|
])->activate('config');
|
||||||
|
|
||||||
|
$this->addTitle($this->translate('Generated config'));
|
||||||
|
$this->content()->add(new DeployedConfigInfoHeader(
|
||||||
|
$config,
|
||||||
|
$this->db(),
|
||||||
|
$this->api(),
|
||||||
|
$deploymentId
|
||||||
|
));
|
||||||
|
|
||||||
|
GeneratedConfigFileTable::load($config, $this->db())
|
||||||
|
->setActiveFilename($this->params->get('active_file'))
|
||||||
|
->setDeploymentId($deploymentId)
|
||||||
|
->renderTo($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show a single file
|
/**
|
||||||
|
* Show a single file
|
||||||
|
*/
|
||||||
public function fileAction()
|
public function fileAction()
|
||||||
{
|
{
|
||||||
$this->assertPermission('director/showconfig');
|
$this->assertPermission('director/showconfig');
|
||||||
$filename = $this->view->filename = $this->params->get('file_path');
|
$filename = $this->params->getRequired('file_path');
|
||||||
$fileOnly = $this->params->get('fileOnly');
|
$this->configTabs()->add('file', array(
|
||||||
$this->view->highlight = $this->params->get('highlight');
|
'label' => $this->translate('Rendered file'),
|
||||||
$this->view->highlightSeverity = $this->params->get('highlightSeverity');
|
'url' => $this->url(),
|
||||||
$tabs = $this->configTabs()->add('file', array(
|
|
||||||
'label' => $this->translate('Rendered file'),
|
|
||||||
'url' => $this->getRequest()->getUrl(),
|
|
||||||
))->activate('file');
|
))->activate('file');
|
||||||
|
|
||||||
$params = $this->getConfigTabParams();
|
$params = $this->getConfigTabParams();
|
||||||
if ('deployment' === $this->params->get('backTo')) {
|
if ('deployment' === $this->params->get('backTo')) {
|
||||||
$this->view->addLink = $this->view->qlink(
|
$this->addBackLink('director/deployment', ['id' => $params['deployment_id']]);
|
||||||
$this->translate('back'),
|
|
||||||
'director/deployment',
|
|
||||||
array('id' => $params['deployment_id']),
|
|
||||||
array('class' => 'icon-left-big')
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
$params['active_file'] = $filename;
|
$params['active_file'] = $filename;
|
||||||
$this->view->addLink = $this->view->qlink(
|
$this->addBackLink('director/config/files', $params);
|
||||||
$this->translate('back'),
|
|
||||||
'director/config/files',
|
|
||||||
$params,
|
|
||||||
array('class' => 'icon-left-big')
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->view->config = IcingaConfig::load(Util::hex2binary($this->params->get('config_checksum')), $this->db());
|
$config = IcingaConfig::load(Util::hex2binary($this->params->get('config_checksum')), $this->db());
|
||||||
$this->view->title = sprintf(
|
$this->addTitle($this->translate('Config file "%s"'), $filename);
|
||||||
$this->translate('Config file "%s"'),
|
$this->content()->add(new ShowConfigFile(
|
||||||
$filename
|
$config->getFile($filename),
|
||||||
);
|
$this->params->get('highlight'),
|
||||||
$this->view->file = $this->view->config->getFile($filename);
|
$this->params->get('highlightSeverity')
|
||||||
}
|
));
|
||||||
|
|
||||||
public function showAction()
|
|
||||||
{
|
|
||||||
$this->assertPermission('director/showconfig');
|
|
||||||
|
|
||||||
$this->configTabs()->activate('config');
|
|
||||||
$this->view->config = IcingaConfig::load(Util::hex2binary($this->params->get('checksum')), $this->db());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Check if this can be removed
|
// TODO: Check if this can be removed
|
||||||
public function storeAction()
|
public function storeAction()
|
||||||
{
|
{
|
||||||
|
$this->assertPermission('director/deploy');
|
||||||
$config = IcingaConfig::generate($this->db());
|
$config = IcingaConfig::generate($this->db());
|
||||||
$this->redirectNow(
|
$this->redirectNow(
|
||||||
Url::fromPath(
|
Url::fromPath(
|
||||||
@ -286,16 +239,11 @@ class ConfigController extends ActionController
|
|||||||
$this->assertPermission('director/showconfig');
|
$this->assertPermission('director/showconfig');
|
||||||
|
|
||||||
$db = $this->db();
|
$db = $this->db();
|
||||||
$this->view->title = $this->translate('Config diff');
|
$this->addTitle($this->translate('Config diff'));
|
||||||
|
$this->addSingleTab($this->translate('Config diff'));
|
||||||
|
|
||||||
$tabs = $this->getTabs()->add('diff', array(
|
$leftSum = $this->params->get('left');
|
||||||
'label' => $this->translate('Config diff'),
|
$rightSum = $this->params->get('right');
|
||||||
'url' => $this->getRequest()->getUrl()
|
|
||||||
))->activate('diff');
|
|
||||||
|
|
||||||
$leftSum = $this->view->leftSum = $this->params->get('left');
|
|
||||||
$rightSum = $this->view->rightSum = $this->params->get('right');
|
|
||||||
$left = IcingaConfig::load(Util::hex2binary($leftSum), $db);
|
|
||||||
|
|
||||||
$configs = $db->enumDeployedConfigs();
|
$configs = $db->enumDeployedConfigs();
|
||||||
foreach (array($leftSum, $rightSum) as $sum) {
|
foreach (array($leftSum, $rightSum) as $sum) {
|
||||||
@ -304,64 +252,104 @@ class ConfigController extends ActionController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->view->configs = $configs;
|
$this->content()->add(Html::form(['action' => $this->url(), 'method' => 'GET'], [
|
||||||
if ($rightSum === null) {
|
new HtmlString($this->view->formSelect(
|
||||||
|
'left',
|
||||||
|
$leftSum,
|
||||||
|
['class' => 'autosubmit', 'style' => 'width: 37%'],
|
||||||
|
[null => $this->translate('- please choose -')] + $configs
|
||||||
|
)),
|
||||||
|
Link::create(
|
||||||
|
Icon::create('flapping'),
|
||||||
|
$this->url(),
|
||||||
|
['left' => $rightSum, 'right' => $leftSum]
|
||||||
|
),
|
||||||
|
new HtmlString($this->view->formSelect(
|
||||||
|
'right',
|
||||||
|
$rightSum,
|
||||||
|
['class' => 'autosubmit', 'style' => 'width: 37%'],
|
||||||
|
[null => $this->translate('- please choose -')] + $configs
|
||||||
|
)),
|
||||||
|
]));
|
||||||
|
|
||||||
|
if (! strlen($rightSum) || ! strlen($leftSum)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
ConfigFileDiffTable::load($leftSum, $rightSum, $this->db())->renderTo($this);
|
||||||
$right = IcingaConfig::load(Util::hex2binary($rightSum), $db);
|
|
||||||
$this->view->table = $this
|
|
||||||
->loadTable('ConfigFileDiff')
|
|
||||||
->setConnection($this->db())
|
|
||||||
->setLeftChecksum($leftSum)
|
|
||||||
->setRightChecksum($rightSum);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function filediffAction()
|
public function filediffAction()
|
||||||
{
|
{
|
||||||
$this->assertPermission('director/showconfig');
|
$this->assertPermission('director/showconfig');
|
||||||
|
|
||||||
|
$p = $this->params;
|
||||||
$db = $this->db();
|
$db = $this->db();
|
||||||
$leftSum = $this->params->get('left');
|
$leftSum = $p->getRequired('left');
|
||||||
$rightSum = $this->params->get('right');
|
$rightSum = $p->getRequired('right');
|
||||||
$filename = $this->view->filename = $this->params->get('file_path');
|
$filename = $p->getRequired('file_path');
|
||||||
|
|
||||||
$left = IcingaConfig::load(Util::hex2binary($leftSum), $db);
|
$left = IcingaConfig::load(Util::hex2binary($leftSum), $db);
|
||||||
$right = IcingaConfig::load(Util::hex2binary($rightSum), $db);
|
$right = IcingaConfig::load(Util::hex2binary($rightSum), $db);
|
||||||
|
|
||||||
$leftFile = $left->getFile($filename);
|
$this
|
||||||
$rightFile = $right->getFile($filename);
|
->addTitle($this->translate('Config file "%s"'), $filename)
|
||||||
|
->addSingleTab($this->translate('Diff'))
|
||||||
|
->content()->add(ConfigDiff::create(
|
||||||
|
$left->getFile($filename),
|
||||||
|
$right->getFile($filename)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
$d = ConfigDiff::create($leftFile, $rightFile);
|
protected function deploymentSucceeded($checksum)
|
||||||
|
{
|
||||||
|
if ($this->getRequest()->isApiRequest()) {
|
||||||
|
$this->sendJson($this->getResponse(), (object) array('checksum' => $checksum));
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
$url = Url::fromPath('director/config/deployments');
|
||||||
|
Notification::success(
|
||||||
|
$this->translate('Config has been submitted, validation is going on')
|
||||||
|
);
|
||||||
|
$this->redirectNow($url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->view->title = sprintf(
|
protected function deploymentFailed($checksum, $error = null)
|
||||||
$this->translate('Config file "%s"'),
|
{
|
||||||
$filename
|
$extra = $error ? ': ' . $error: '';
|
||||||
);
|
|
||||||
|
|
||||||
$this->view->output = $d->renderHtml();
|
if ($this->getRequest()->isApiRequest()) {
|
||||||
|
$this->sendJsonError($this->getResponse(), 'Config deployment failed' . $extra);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
$url = Url::fromPath('director/config/files', array('checksum' => $checksum));
|
||||||
|
Notification::error(
|
||||||
|
$this->translate('Config deployment failed') . $extra
|
||||||
|
);
|
||||||
|
$this->redirectNow($url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function configTabs()
|
protected function configTabs()
|
||||||
{
|
{
|
||||||
$tabs = $this->getTabs();
|
$tabs = $this->tabs();
|
||||||
|
|
||||||
if ($this->hasPermission('director/deploy') && $deploymentId = $this->params->get('deployment_id')) {
|
if ($this->hasPermission('director/deploy')
|
||||||
$tabs->add('deployment', array(
|
&& $deploymentId = $this->params->get('deployment_id')
|
||||||
|
) {
|
||||||
|
$tabs->add('deployment', [
|
||||||
'label' => $this->translate('Deployment'),
|
'label' => $this->translate('Deployment'),
|
||||||
'url' => 'director/deployment',
|
'url' => 'director/deployment',
|
||||||
'urlParams' => array(
|
'urlParams' => ['id' => $deploymentId]
|
||||||
'id' => $deploymentId
|
]);
|
||||||
)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->hasPermission('director/showconfig')) {
|
if ($this->hasPermission('director/showconfig')) {
|
||||||
$tabs->add('config', array(
|
$tabs->add('config', [
|
||||||
'label' => $this->translate('Config'),
|
'label' => $this->translate('Config'),
|
||||||
'url' => 'director/config/files',
|
'url' => 'director/config/files',
|
||||||
'urlParams' => $this->getConfigTabParams()
|
'urlParams' => $this->getConfigTabParams()
|
||||||
));
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $tabs;
|
return $tabs;
|
||||||
@ -369,12 +357,12 @@ class ConfigController extends ActionController
|
|||||||
|
|
||||||
protected function getConfigTabParams()
|
protected function getConfigTabParams()
|
||||||
{
|
{
|
||||||
$params = array(
|
$params = [
|
||||||
'checksum' => $this->params->get(
|
'checksum' => $this->params->get(
|
||||||
'config_checksum',
|
'config_checksum',
|
||||||
$this->params->get('checksum')
|
$this->params->get('checksum')
|
||||||
)
|
)
|
||||||
);
|
];
|
||||||
|
|
||||||
if ($deploymentId = $this->params->get('deployment_id')) {
|
if ($deploymentId = $this->params->get('deployment_id')) {
|
||||||
$params['deployment_id'] = $deploymentId;
|
$params['deployment_id'] = $deploymentId;
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
<div class="controls">
|
|
||||||
<?= $this->tabs ?>
|
|
||||||
<h1><?= $this->escape($this->title) ?></h1>
|
|
||||||
<span class="action-links" data-base-target="_next">
|
|
||||||
<?= $this->addLink ?>
|
|
||||||
</span>
|
|
||||||
<form action="<?= $this->url ?>" method="GET">
|
|
||||||
<?= $this->formSelect(
|
|
||||||
'left',
|
|
||||||
$this->leftSum,
|
|
||||||
array('class' => 'autosubmit', 'style' => 'width: 37%'),
|
|
||||||
array(null => $this->translate('- please choose -')) + $this->configs
|
|
||||||
)
|
|
||||||
?>
|
|
||||||
<?= $this->qlink(
|
|
||||||
'',
|
|
||||||
$this->url(),
|
|
||||||
array(
|
|
||||||
'left' => $this->rightSum,
|
|
||||||
'right' => $this->leftSum
|
|
||||||
),
|
|
||||||
array('class' => 'icon-flapping')
|
|
||||||
) ?>
|
|
||||||
<?= $this->formSelect(
|
|
||||||
'right',
|
|
||||||
$this->rightSum,
|
|
||||||
array('class' => 'autosubmit', 'style' => 'width: 37%'),
|
|
||||||
array(null => $this->translate('- please choose -')) + $this->configs
|
|
||||||
)
|
|
||||||
?>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content" data-base-target="_next">
|
|
||||||
<?php if (count($this->table)): ?>
|
|
||||||
<div>
|
|
||||||
<?= $this->table->render() ?>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
|
||||||
</div>
|
|
@ -1,66 +0,0 @@
|
|||||||
<div class="controls">
|
|
||||||
<?= $this->tabs ?>
|
|
||||||
<h1><?= $title ?></h1>
|
|
||||||
<span class="action-links" data-base-target="_self">
|
|
||||||
<?= $this->addLink ?>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<?php
|
|
||||||
|
|
||||||
if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
|
|
||||||
$decode = function($str) { return htmlspecialchars_decode($str, ENT_COMPAT | ENT_SUBSTITUTE | ENT_HTML5); };
|
|
||||||
} else {
|
|
||||||
$decode = function($str) { return htmlspecialchars_decode($str, ENT_COMPAT | ENT_IGNORE); };
|
|
||||||
}
|
|
||||||
|
|
||||||
$view = $this;
|
|
||||||
$linkObject = function ($match) use ($view, $decode) {
|
|
||||||
|
|
||||||
if ($match[2] === 'Service') {
|
|
||||||
return $match[0];
|
|
||||||
}
|
|
||||||
if ($match[2] === 'CheckCommand') {
|
|
||||||
$match[2] = 'command';
|
|
||||||
}
|
|
||||||
|
|
||||||
$name = $decode($match[3]);
|
|
||||||
return sprintf(
|
|
||||||
'%s %s "%s" {',
|
|
||||||
$match[1],
|
|
||||||
$match[2],
|
|
||||||
$view->qlink(
|
|
||||||
$name,
|
|
||||||
'director/' . $match[2],
|
|
||||||
array('name' => $name),
|
|
||||||
array('data-base-target' => '_next')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
function linkObjects($config, $linkCallback) {
|
|
||||||
$pattern = '/^(object|template)\s([A-Z][A-Za-z]*?)\s"(.+?)"\s{/m';
|
|
||||||
|
|
||||||
return preg_replace_callback(
|
|
||||||
$pattern,
|
|
||||||
$linkCallback,
|
|
||||||
$config
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function highlight($what, $line, $severity) {
|
|
||||||
$lines = explode("\n", $what);
|
|
||||||
$lines[$line - 1] = '<span class="highlight ' . $severity . '">' . $lines[$line - 1] . '</span>';
|
|
||||||
return implode("\n", $lines);
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<pre class="generated-config">
|
|
||||||
<?php if ($this->highlight): ?>
|
|
||||||
<?= highlight(linkObjects($this->escape($file->getContent()), $linkObject), $highlight, $highlightSeverity) ?>
|
|
||||||
<?php else: ?>
|
|
||||||
<?= linkObjects($this->escape($file->getContent()), $linkObject) ?>
|
|
||||||
<?php endif ?>
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
@ -1,11 +0,0 @@
|
|||||||
<div class="controls">
|
|
||||||
<?= $this->tabs ?>
|
|
||||||
<h1><?= $this->escape($this->title) ?></h1>
|
|
||||||
<span class="action-links" data-base-target="_next">
|
|
||||||
<?= $this->addLink ?>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content" data-base-target="_next">
|
|
||||||
<?= $this->output ?>
|
|
||||||
</div>
|
|
@ -1,44 +0,0 @@
|
|||||||
<div class="controls">
|
|
||||||
<?= $this->tabs ?>
|
|
||||||
<h1><?= $this->escape($this->title) ?></h1>
|
|
||||||
<span class="action-links" data-base-target="_next">
|
|
||||||
<?= $this->addLink ?>
|
|
||||||
</span>
|
|
||||||
<?php if (count($table) || ($this->filterEditor && ! $this->filterEditor->getFilter()->isEmpty())): ?>
|
|
||||||
<?= $this->filterEditor ?>
|
|
||||||
<?php endif ?>
|
|
||||||
<?= $this->table->getPaginator() ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content" data-base-target="_next">
|
|
||||||
<table class="name-value-table">
|
|
||||||
<tr>
|
|
||||||
<th><?= $this->translate('Actions') ?></th>
|
|
||||||
<td data-base-target="_self"><?= $this->deployForm ?><br /><?= $this->qlink(
|
|
||||||
$this->translate('Last related activity'),
|
|
||||||
'director/show/activitylog',
|
|
||||||
array('checksum' => $this->config->getLastActivityHexChecksum()),
|
|
||||||
array('class' => 'icon-clock', 'data-base-target' => '_next')
|
|
||||||
) ?><br /><?= $this->qlink(
|
|
||||||
$this->translate('Diff with other config'),
|
|
||||||
'director/config/diff',
|
|
||||||
array('left' => $this->config->getHexChecksum()),
|
|
||||||
array('class' => 'icon-flapping', 'data-base-target' => '_self')
|
|
||||||
) ?></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><?= $this->translate('Statistics') ?></th>
|
|
||||||
<td><?= sprintf(
|
|
||||||
$this->translate('%d files rendered in %0.2fs'),
|
|
||||||
count($this->config->getFiles()),
|
|
||||||
$config->getDuration() / 1000
|
|
||||||
) ?></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<?php if (count($table)): ?>
|
|
||||||
<div data-base-target="_self">
|
|
||||||
<?= $this->table->render() ?>
|
|
||||||
</div>
|
|
||||||
<?php endif ?>
|
|
||||||
</div>
|
|
@ -1,38 +0,0 @@
|
|||||||
<div class="controls">
|
|
||||||
<?= $this->tabs ?>
|
|
||||||
<h1>Config: <?= $this->config->getHexChecksum() ?></h1>
|
|
||||||
|
|
||||||
<table class="name-value-table">
|
|
||||||
<tr>
|
|
||||||
<th><?= $this->translate('Deploy to master') ?></th>
|
|
||||||
<td data-base-target="_main"><?= $this->qlink(
|
|
||||||
$this->config->getHexChecksum(),
|
|
||||||
'director/config/deploy',
|
|
||||||
array('checksum' => $this->config->getHexChecksum())
|
|
||||||
) ?></td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><?= $this->translate('Last related activity') ?></th>
|
|
||||||
<td><?= $this->qlink(
|
|
||||||
$this->config->getLastActivityHexChecksum(),
|
|
||||||
'director/show/activitylog',
|
|
||||||
array('checksum' => $this->config->getLastActivityHexChecksum())
|
|
||||||
) ?></td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><?= $this->translate('Generated files') ?></th>
|
|
||||||
<td><?= count($this->config->getFiles()) ?></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<?php foreach ($this->config->getFiles() as $filename => $file): ?>
|
|
||||||
<h2><?= $this->escape($filename) ?></h2>
|
|
||||||
<pre>
|
|
||||||
<?= $this->escape($file->getContent()) ?>
|
|
||||||
</pre>
|
|
||||||
<?php endforeach ?>
|
|
||||||
</div>
|
|
@ -1,7 +0,0 @@
|
|||||||
<div class="controls">
|
|
||||||
<?= $this->tabs ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<?= $this->id ?>
|
|
||||||
</div>
|
|
@ -1,33 +1,46 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Icinga\Module\Director\Tables;
|
namespace Icinga\Module\Director\Web\Table;
|
||||||
|
|
||||||
use Icinga\Exception\ProgrammingError;
|
use Icinga\Module\Director\Db;
|
||||||
use Icinga\Module\Director\Web\Table\QuickTable;
|
|
||||||
use Icinga\Module\Director\Util;
|
use Icinga\Module\Director\Util;
|
||||||
|
use ipl\Html\Link;
|
||||||
|
use ipl\Web\Table\ZfQueryBasedTable;
|
||||||
|
|
||||||
class ConfigFileDiffTable extends QuickTable
|
class ConfigFileDiffTable extends ZfQueryBasedTable
|
||||||
{
|
{
|
||||||
|
use DbHelper;
|
||||||
|
|
||||||
protected $leftChecksum;
|
protected $leftChecksum;
|
||||||
|
|
||||||
protected $rightChecksum;
|
protected $rightChecksum;
|
||||||
|
|
||||||
public function getColumns()
|
/**
|
||||||
|
* @param $leftSum
|
||||||
|
* @param $rightSum
|
||||||
|
* @param Db $connection
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public static function load($leftSum, $rightSum, Db $connection)
|
||||||
{
|
{
|
||||||
throw new ProgrammingError('Accessing getColumns() is not supported');
|
$table = new static($connection);
|
||||||
|
$table->attributes()->add('class', 'config-diff');
|
||||||
|
return $table->setLeftChecksum($leftSum)
|
||||||
|
->setRightChecksum($rightSum);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function listTableClasses()
|
public function renderRow($row)
|
||||||
{
|
{
|
||||||
return array_merge(array('config-diff'), parent::listTableClasses());
|
$tr = $this::row([
|
||||||
|
$this->getFileFiffLink($row),
|
||||||
|
$row->file_path,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$tr->attributes()->add('class', 'file-' . $row->file_action);
|
||||||
|
return $tr;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getRowClasses($row)
|
protected function getFileFiffLink($row)
|
||||||
{
|
|
||||||
return 'file-' . $row->file_action;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getActionUrl($row)
|
|
||||||
{
|
{
|
||||||
$params = array('file_path' => $row->file_path);
|
$params = array('file_path' => $row->file_path);
|
||||||
|
|
||||||
@ -40,10 +53,14 @@ class ConfigFileDiffTable extends QuickTable
|
|||||||
} else {
|
} else {
|
||||||
$params['left'] = $row->config_checksum_left;
|
$params['left'] = $row->config_checksum_left;
|
||||||
$params['right'] = $row->config_checksum_right;
|
$params['right'] = $row->config_checksum_right;
|
||||||
return $this->url('director/config/filediff', $params);
|
return Link::create(
|
||||||
|
$row->file_action,
|
||||||
|
'director/config/filediff',
|
||||||
|
$params
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->url('director/config/file', $params);
|
return Link::create($row->file_action, 'director/config/file', $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setLeftChecksum($checksum)
|
public function setLeftChecksum($checksum)
|
||||||
@ -60,42 +77,14 @@ class ConfigFileDiffTable extends QuickTable
|
|||||||
|
|
||||||
public function getTitles()
|
public function getTitles()
|
||||||
{
|
{
|
||||||
$view = $this->view();
|
|
||||||
return array(
|
return array(
|
||||||
'file_action' => $view->translate('Action'),
|
$this->translate('Action'),
|
||||||
'file_path' => $view->translate('File'),
|
$this->translate('File'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function count()
|
public function prepareQuery()
|
||||||
{
|
{
|
||||||
$db = $this->connection()->getConnection();
|
|
||||||
$query = clone($this->getBaseQuery());
|
|
||||||
$query->reset('order');
|
|
||||||
$this->applyFiltersToQuery($query);
|
|
||||||
return $db->fetchOne($db->select()->from(
|
|
||||||
array('cntsub' => $query),
|
|
||||||
array('cnt' => 'COUNT(*)')
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function fetchData()
|
|
||||||
{
|
|
||||||
$db = $this->connection()->getConnection();
|
|
||||||
$query = $this->getBaseQuery();
|
|
||||||
|
|
||||||
if ($this->hasLimit() || $this->hasOffset()) {
|
|
||||||
$query->limit($this->getLimit(), $this->getOffset());
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->applyFiltersToQuery($query);
|
|
||||||
|
|
||||||
return $db->fetchAll($query);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getBaseQuery()
|
|
||||||
{
|
|
||||||
$conn = $this->connection();
|
|
||||||
$db = $this->db();
|
$db = $this->db();
|
||||||
|
|
||||||
$left = $db->select()
|
$left = $db->select()
|
||||||
@ -103,10 +92,10 @@ class ConfigFileDiffTable extends QuickTable
|
|||||||
array('cfl' => 'director_generated_config_file'),
|
array('cfl' => 'director_generated_config_file'),
|
||||||
array(
|
array(
|
||||||
'file_path' => 'COALESCE(cfl.file_path, cfr.file_path)',
|
'file_path' => 'COALESCE(cfl.file_path, cfr.file_path)',
|
||||||
'config_checksum_left' => $conn->dbHexFunc('cfl.config_checksum'),
|
'config_checksum_left' => $this->dbHexFunc('cfl.config_checksum'),
|
||||||
'config_checksum_right' => $conn->dbHexFunc('cfr.config_checksum'),
|
'config_checksum_right' => $this->dbHexFunc('cfr.config_checksum'),
|
||||||
'file_checksum_left' => $conn->dbHexFunc('cfl.file_checksum'),
|
'file_checksum_left' => $this->dbHexFunc('cfl.file_checksum'),
|
||||||
'file_checksum_right' => $conn->dbHexFunc('cfr.file_checksum'),
|
'file_checksum_right' => $this->dbHexFunc('cfr.file_checksum'),
|
||||||
'file_action' => '(CASE WHEN cfr.config_checksum IS NULL'
|
'file_action' => '(CASE WHEN cfr.config_checksum IS NULL'
|
||||||
. " THEN 'removed' WHEN cfl.file_checksum = cfr.file_checksum"
|
. " THEN 'removed' WHEN cfl.file_checksum = cfr.file_checksum"
|
||||||
. " THEN 'unmodified' ELSE 'modified' END)",
|
. " THEN 'unmodified' ELSE 'modified' END)",
|
||||||
@ -115,12 +104,12 @@ class ConfigFileDiffTable extends QuickTable
|
|||||||
array('cfr' => 'director_generated_config_file'),
|
array('cfr' => 'director_generated_config_file'),
|
||||||
$db->quoteInto(
|
$db->quoteInto(
|
||||||
'cfl.file_path = cfr.file_path AND cfr.config_checksum = ?',
|
'cfl.file_path = cfr.file_path AND cfr.config_checksum = ?',
|
||||||
$conn->quoteBinary(Util::hex2binary($this->rightChecksum))
|
$this->quoteBinary(Util::hex2binary($this->rightChecksum))
|
||||||
),
|
),
|
||||||
array()
|
array()
|
||||||
)->where(
|
)->where(
|
||||||
'cfl.config_checksum = ?',
|
'cfl.config_checksum = ?',
|
||||||
$conn->quoteBinary(Util::hex2binary($this->leftChecksum))
|
$this->quoteBinary(Util::hex2binary($this->leftChecksum))
|
||||||
);
|
);
|
||||||
|
|
||||||
$right = $db->select()
|
$right = $db->select()
|
||||||
@ -128,22 +117,22 @@ class ConfigFileDiffTable extends QuickTable
|
|||||||
array('cfl' => 'director_generated_config_file'),
|
array('cfl' => 'director_generated_config_file'),
|
||||||
array(
|
array(
|
||||||
'file_path' => 'COALESCE(cfr.file_path, cfl.file_path)',
|
'file_path' => 'COALESCE(cfr.file_path, cfl.file_path)',
|
||||||
'config_checksum_left' => $conn->dbHexFunc('cfl.config_checksum'),
|
'config_checksum_left' => $this->dbHexFunc('cfl.config_checksum'),
|
||||||
'config_checksum_right' => $conn->dbHexFunc('cfr.config_checksum'),
|
'config_checksum_right' => $this->dbHexFunc('cfr.config_checksum'),
|
||||||
'file_checksum_left' => $conn->dbHexFunc('cfl.file_checksum'),
|
'file_checksum_left' => $this->dbHexFunc('cfl.file_checksum'),
|
||||||
'file_checksum_right' => $conn->dbHexFunc('cfr.file_checksum'),
|
'file_checksum_right' => $this->dbHexFunc('cfr.file_checksum'),
|
||||||
'file_action' => "('created')",
|
'file_action' => "('created')",
|
||||||
)
|
)
|
||||||
)->joinRight(
|
)->joinRight(
|
||||||
array('cfr' => 'director_generated_config_file'),
|
array('cfr' => 'director_generated_config_file'),
|
||||||
$db->quoteInto(
|
$db->quoteInto(
|
||||||
'cfl.file_path = cfr.file_path AND cfl.config_checksum = ?',
|
'cfl.file_path = cfr.file_path AND cfl.config_checksum = ?',
|
||||||
$conn->quoteBinary(Util::hex2binary($this->leftChecksum))
|
$this->quoteBinary(Util::hex2binary($this->leftChecksum))
|
||||||
),
|
),
|
||||||
array()
|
array()
|
||||||
)->where(
|
)->where(
|
||||||
'cfr.config_checksum = ?',
|
'cfr.config_checksum = ?',
|
||||||
$conn->quoteBinary(Util::hex2binary($this->rightChecksum))
|
$this->quoteBinary(Util::hex2binary($this->rightChecksum))
|
||||||
)->where('cfl.file_checksum IS NULL');
|
)->where('cfl.file_checksum IS NULL');
|
||||||
|
|
||||||
return $db->select()->union(array($left, $right))->order('file_path');
|
return $db->select()->union(array($left, $right))->order('file_path');
|
88
library/Director/Web/Widget/DeployedConfigInfoHeader.php
Normal file
88
library/Director/Web/Widget/DeployedConfigInfoHeader.php
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Module\Director\Web\Widget;
|
||||||
|
|
||||||
|
use Icinga\Module\Director\Core\CoreApi;
|
||||||
|
use Icinga\Module\Director\Db;
|
||||||
|
use Icinga\Module\Director\Forms\DeployConfigForm;
|
||||||
|
use Icinga\Module\Director\IcingaConfig\IcingaConfig;
|
||||||
|
use ipl\Html\Html;
|
||||||
|
use ipl\Html\Link;
|
||||||
|
use ipl\Translation\TranslationHelper;
|
||||||
|
use ipl\Web\Widget\NameValueTable;
|
||||||
|
|
||||||
|
class DeployedConfigInfoHeader extends Html
|
||||||
|
{
|
||||||
|
use TranslationHelper;
|
||||||
|
|
||||||
|
/** @var IcingaConfig */
|
||||||
|
protected $config;
|
||||||
|
|
||||||
|
/** @var int */
|
||||||
|
protected $deploymentId;
|
||||||
|
|
||||||
|
/** @var Db */
|
||||||
|
protected $db;
|
||||||
|
|
||||||
|
/** @var CoreApi */
|
||||||
|
protected $api;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
IcingaConfig $config,
|
||||||
|
Db $db,
|
||||||
|
CoreApi $api,
|
||||||
|
$deploymentId = null
|
||||||
|
) {
|
||||||
|
$this->config = $config;
|
||||||
|
$this->db = $db;
|
||||||
|
$this->api = $api;
|
||||||
|
if ($deploymentId) {
|
||||||
|
$this->deploymentId = (int) $deploymentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->prepareContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function prepareContent()
|
||||||
|
{
|
||||||
|
$config = $this->config;
|
||||||
|
$deployForm = DeployConfigForm::load()
|
||||||
|
->setDb($this->db)
|
||||||
|
->setApi($this->api)
|
||||||
|
->setChecksum($config->getHexChecksum())
|
||||||
|
->setDeploymentId($this->deploymentId)
|
||||||
|
->setAttrib('class', 'inline')
|
||||||
|
->handleRequest();
|
||||||
|
|
||||||
|
$links = new NameValueTable();
|
||||||
|
$links->addNameValueRow(
|
||||||
|
$this->translate('Actions'),
|
||||||
|
[
|
||||||
|
$deployForm,
|
||||||
|
Html::br(),
|
||||||
|
Link::create(
|
||||||
|
$this->translate('Last related activity'),
|
||||||
|
'director/show/activitylog',
|
||||||
|
['checksum' => $config->getLastActivityHexChecksum()],
|
||||||
|
['class' => 'icon-clock', 'data-base-target' => '_next']
|
||||||
|
),
|
||||||
|
Html::br(),
|
||||||
|
Link::create(
|
||||||
|
$this->translate('Diff with other config'),
|
||||||
|
'director/config/diff',
|
||||||
|
['left' => $config->getHexChecksum()],
|
||||||
|
['class' => 'icon-flapping', 'data-base-target' => '_self']
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)->addNameValueRow(
|
||||||
|
$this->translate('Statistics'),
|
||||||
|
sprintf(
|
||||||
|
$this->translate('%d files rendered in %0.2fs'),
|
||||||
|
count($config->getFiles()),
|
||||||
|
$config->getDuration() / 1000
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->add($links);
|
||||||
|
}
|
||||||
|
}
|
95
library/Director/Web/Widget/ShowConfigFile.php
Normal file
95
library/Director/Web/Widget/ShowConfigFile.php
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Module\Director\Web\Widget;
|
||||||
|
|
||||||
|
use Icinga\Module\Director\IcingaConfig\IcingaConfigFile;
|
||||||
|
use ipl\Html\Html;
|
||||||
|
use ipl\Html\HtmlString;
|
||||||
|
use ipl\Html\Link;
|
||||||
|
use ipl\Html\Util;
|
||||||
|
use ipl\Translation\TranslationHelper;
|
||||||
|
|
||||||
|
class ShowConfigFile extends Html
|
||||||
|
{
|
||||||
|
use TranslationHelper;
|
||||||
|
|
||||||
|
protected $file;
|
||||||
|
|
||||||
|
protected $highlight;
|
||||||
|
|
||||||
|
protected $highlightSeverity;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
IcingaConfigFile $file,
|
||||||
|
$highlight = null,
|
||||||
|
$highlightSeverity = null
|
||||||
|
) {
|
||||||
|
$this->file = $file;
|
||||||
|
$this->highlight = $highlight;
|
||||||
|
$this->highlightSeverity = $highlightSeverity;
|
||||||
|
$this->prepareContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function prepareContent()
|
||||||
|
{
|
||||||
|
$source = $this->linkObjects(Util::escapeForHtml($this->file->getContent()));
|
||||||
|
if ($this->highlight) {
|
||||||
|
$source = $this->highlight(
|
||||||
|
$source,
|
||||||
|
$this->highlight,
|
||||||
|
$this->highlightSeverity
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->add(Html::pre(
|
||||||
|
['class' => 'generated-config'],
|
||||||
|
new HtmlString($source)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function linkObject($match)
|
||||||
|
{
|
||||||
|
if ($match[2] === 'Service') {
|
||||||
|
return $match[0];
|
||||||
|
}
|
||||||
|
if ($match[2] === 'CheckCommand') {
|
||||||
|
$match[2] = 'command';
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = $this->decode($match[3]);
|
||||||
|
return sprintf(
|
||||||
|
'%s %s "%s" {',
|
||||||
|
$match[1],
|
||||||
|
$match[2],
|
||||||
|
Link::create(
|
||||||
|
$name,
|
||||||
|
'director/' . $match[2],
|
||||||
|
['name' => $name],
|
||||||
|
['data-base-target' => '_next']
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function decode($str)
|
||||||
|
{
|
||||||
|
return htmlspecialchars_decode($str, ENT_COMPAT | ENT_SUBSTITUTE | ENT_HTML5);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function linkObjects($config)
|
||||||
|
{
|
||||||
|
$pattern = '/^(object|template)\s([A-Z][A-Za-z]*?)\s"(.+?)"\s{/m';
|
||||||
|
|
||||||
|
return preg_replace_callback(
|
||||||
|
$pattern,
|
||||||
|
[$this, 'linkObject'],
|
||||||
|
$config
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function highlight($what, $line, $severity)
|
||||||
|
{
|
||||||
|
$lines = explode("\n", $what);
|
||||||
|
$lines[$line - 1] = '<span class="highlight ' . $severity . '">' . $lines[$line - 1] . '</span>';
|
||||||
|
return implode("\n", $lines);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user