From d76efea9837ed99118eea6421d6fa857d780fdca Mon Sep 17 00:00:00 2001 From: raviks789 <33730024+raviks789@users.noreply.github.com> Date: Fri, 9 Feb 2024 16:40:05 +0100 Subject: [PATCH 1/4] Regulate object links based on user permissions and backend used --- application/clicommands/SendCommand.php | 2 +- library/Jira/IcingaCommandPipe.php | 25 +--- library/Jira/Web/Form/NewIssueForm.php | 2 +- library/Jira/Web/RenderingHelper.php | 179 +++++++++++++++++++----- library/Jira/Web/Table/IssueDetails.php | 64 ++++----- library/Jira/Web/Table/IssuesTable.php | 3 +- public/css/module.less | 22 ++- 7 files changed, 195 insertions(+), 102 deletions(-) diff --git a/application/clicommands/SendCommand.php b/application/clicommands/SendCommand.php index 0b06bcc..114c5ec 100644 --- a/application/clicommands/SendCommand.php +++ b/application/clicommands/SendCommand.php @@ -150,7 +150,7 @@ public function problemAction() if ($ackPipe) { $cmd = new LegacyCommandPipe($ackPipe); } else { - $cmd = (new IcingaCommandPipe())->setMonitoringInfo($info); + $cmd = new IcingaCommandPipe($info); } if ($cmd->acknowledge($ackAuthor, $ackMessage, $host, $service)) { Logger::info("Problem has been acknowledged for $key"); diff --git a/library/Jira/IcingaCommandPipe.php b/library/Jira/IcingaCommandPipe.php index 1e6d626..5ec223c 100644 --- a/library/Jira/IcingaCommandPipe.php +++ b/library/Jira/IcingaCommandPipe.php @@ -2,8 +2,6 @@ namespace Icinga\Module\Jira; -use Icinga\Application\Modules\Module; -use Icinga\Module\Jira\ProvidedHook\Icingadb\IcingadbSupport; use Icinga\Module\Monitoring\Command\Object\AcknowledgeProblemCommand; use Icinga\Module\Monitoring\Command\Transport\CommandTransport; use Icinga\Module\Icingadb\Command\Transport\CommandTransport as IcingadbCommandTransport; @@ -13,20 +11,16 @@ class IcingaCommandPipe { + /** @var MonitoringInfo */ private $monitoringInfo; - public function acknowledge($author, $message, $host, $service = null) + public function __construct(MonitoringInfo $monitoringInfo) { - if ($this->monitoringInfo === null) { - if (Module::exists('icingadb') && IcingadbSupport::useIcingaDbAsBackend()) { - $backend = new IcingadbBackend(); - } else { - $backend = new IdoBackend(); - } - - $this->setMonitoringInfo($backend->getMonitoringInfo($host, $service)); - } + $this->monitoringInfo = $monitoringInfo; + } + public function acknowledge($author, $message, $host, $service = null) + { if (! $this->monitoringInfo->hasObject()) { if ($service !== null) { throw new IcingaException( @@ -57,13 +51,6 @@ public function acknowledge($author, $message, $host, $service = null) return true; } - public function setMonitoringInfo(MonitoringInfo $info) - { - $this->monitoringInfo = $info; - - return $this; - } - protected function getAcknowledgeProblemCommand() { if ($this->monitoringInfo->getObject() instanceof MonitoredObject) { diff --git a/library/Jira/Web/Form/NewIssueForm.php b/library/Jira/Web/Form/NewIssueForm.php index b85271e..26c22c7 100644 --- a/library/Jira/Web/Form/NewIssueForm.php +++ b/library/Jira/Web/Form/NewIssueForm.php @@ -275,7 +275,7 @@ protected function eventuallyAcknowledge($key, $host, $service) $ackMessage = "Jira issue $key has been created"; try { - $cmd = new IcingaCommandPipe(); + $cmd = new IcingaCommandPipe($this->monitoringInfo); if ($cmd->acknowledge('Jira', $ackMessage, $host, $service)) { Logger::info("Problem has been acknowledged for $key"); } diff --git a/library/Jira/Web/RenderingHelper.php b/library/Jira/Web/RenderingHelper.php index 4af30e6..499c54e 100644 --- a/library/Jira/Web/RenderingHelper.php +++ b/library/Jira/Web/RenderingHelper.php @@ -7,7 +7,10 @@ use Icinga\Date\DateFormatter; use Icinga\Module\Jira\ProvidedHook\Icingadb\IcingadbSupport; use Icinga\Module\Jira\RestApi; +use ipl\Html\Attributes; use ipl\Html\Html; +use ipl\Html\HtmlElement; +use ipl\Html\HtmlString; use ipl\Web\Url; use ipl\Web\Widget\Icon; use ipl\Web\Widget\Link; @@ -17,49 +20,157 @@ class RenderingHelper { protected $api; - public function linkToMonitoring($host, $service) + /** @var ?Link Host link */ + protected $hostLink; + + /** @var ?Link Service link */ + protected $serviceLink; + + /** + * Set the link of monitored host + * + * @param Link $hostLink + * + * @return $this + */ + public function setHostLink(Link $hostLink): self { - if ($service === null) { - return $this->linkToMonitoringHost($host); - } else { - return $this->linkToMonitoringService($host, $service); - } + $this->hostLink = $hostLink; + + return $this; } - public function linkToMonitoringHost($host) + /** + * Set the link of monitored service + * + * @param Link $serviceLink + * + * @return $this + */ + public function setServiceLink(Link $serviceLink): self { - if (Module::exists('icingadb') && IcingadbSupport::useIcingaDbAsBackend()) { - return new Link([new Icon('server'), $host], Url::fromPath('icingadb/host', [ - 'name' => $host - ]), [ - 'title' => t('Show Icinga Host State'), - ]); - } + $this->serviceLink = $serviceLink; - return new Link([new Icon('laptop'), $host], Url::fromPath('monitoring/host/show', [ - 'host' => $host - ]), [ - 'title' => t('Show Icinga Host State'), - ]); + return $this; } - public function linkToMonitoringService($host, $service) + /** + * Get the link of monitored host + * + * @return ?Link + */ + public function getHostLink(): ?Link { - if (Module::exists('icingadb') && IcingadbSupport::useIcingaDbAsBackend()) { - return new Link([new Icon('cog'), $service], Url::fromPath('icingadb/service', [ - 'name' => $service, - 'host.name' => $host, - ]), [ - 'title' => t('Show Icinga Service State'), - ]); - } + return $this->hostLink; + } - return new Link([new Icon('cog'), $service], Url::fromPath('monitoring/service/show', [ - 'host' => $host, - 'service' => $service, - ]), [ - 'title' => t('Show Icinga Service State'), - ]); + /** + * Get the link of monitored service + * + * @return ?Link + */ + public function getServiceLink(): ?Link + { + return $this->serviceLink; + } + + + /** + * Get the formatted issue comment using author, time and description or comment body + * + * @param string|HtmlElement[] $author + * @param string $time + * @param string $body + * + * @return HtmlElement[] + */ + public function getIssueComment($author, string $time, string $body): array + { + return [ + new HtmlElement('h3', null, Html::sprintf('%s: %s', $this->shortTimeSince($time), $author)), + new HtmlElement('pre', new Attributes(['class' => 'comment']), $this->formatBody($body)), + ]; + } + + /** + * Format the given issue description or comment body + * + * @param string $body + * + * @return HtmlString + */ + public function formatBody(string $body): HtmlString + { + $html = Html::wantHtml($body)->render(); + + // This is safe. + return new HtmlString($this->replaceLinks($html) ?? ''); + } + + /** + * Replace object urls in the given string with link elements + * + * @param string $string + * + * @return ?string + */ + protected function replaceLinks(string $string): ?string + { + return preg_replace_callback('/\[([^|]+)\|([^]]+)]/', function ($match) { + $url = Url::fromPath(htmlspecialchars_decode($match[2])); + $link = new Link( + $match[1], + $url, + ['target' => '_blank'] + ); + + if ($url->hasParam('service') || $url->hasParam('host.name')) { + if ( + strpos($match[2], 'icingaweb2/monitoring') !== false + && (Module::exists('icingadb') && IcingadbSupport::useIcingaDbAsBackend()) + ) { + $link = new Link( + $match[1], + Url::fromPath( + 'icingadb/service', + [ + 'name' => $url->getParam('service'), + 'host.name' => $url->getParam('host'), + ] + ), + ['target' => '_blank'] + ); + } + + $serviceLink = clone $link; + $serviceLink->setContent([new Icon('cog'), $match[1]]) + ->addAttributes(['title' => t('Show Icinga Service State')]); + $this->setServiceLink($serviceLink); + } else { + $icon = new Icon('server'); + if (strpos($match[2], 'icingaweb2/monitoring') !== false) { + if (Module::exists('icingadb') && IcingadbSupport::useIcingaDbAsBackend()) { + $link = new Link( + $match[1], + Url::fromPath( + 'icingadb/host', + ['name' => $url->getParam('host')] + ), + ['target' => '_blank'] + ); + } else { + $icon = new Icon('laptop'); + } + } + + $hostLink = clone $link; + $hostLink->setContent([$icon, $match[1]]) + ->addAttributes(['title' => t('Show Icinga Host State')]); + $this->setHostLink($hostLink); + } + + return $link->render(); + }, $string); } public function linkToJira($caption, $url, $attributes = []) diff --git a/library/Jira/Web/Table/IssueDetails.php b/library/Jira/Web/Table/IssueDetails.php index 4589ad0..334a64f 100644 --- a/library/Jira/Web/Table/IssueDetails.php +++ b/library/Jira/Web/Table/IssueDetails.php @@ -4,12 +4,11 @@ use Icinga\Module\Jira\Web\RenderingHelper; use ipl\Html\Html; -use ipl\Html\HtmlString; +use ipl\Html\HtmlElement; use Icinga\Application\Config; use ipl\Html\Table; use ipl\I18n\Translation; use ipl\Web\Widget\Icon; -use stdClass; class IssueDetails extends Table { @@ -74,16 +73,22 @@ protected function assemble() $this->translate('Created') => $helper->shortTimeSince($fields->created, false), ]); + $summary = $helper->getIssueComment( + $fields->summary, + $fields->created, + $fields->description + ); + if ($host !== null) { $this->addNameValueRow( $this->translate('Host'), - $helper->linkToMonitoringHost($host) + $helper->getHostLink() ); } if ($service !== null) { $this->addNameValueRow( $this->translate('Service'), - $helper->linkToMonitoringService($host, $service) + $helper->getServiceLink() ); } if ($user !== null) { @@ -94,11 +99,7 @@ protected function assemble() } $this->addComments(array_reverse($fields->comment->comments)); - $this->addComment( - $fields->summary, - $fields->created, - $fields->description - ); + $this->addComment($summary); } protected function addWideRow($content) @@ -113,9 +114,11 @@ protected function addComments($comments) foreach ($comments as $comment) { if (property_exists($comment, 'author')) { $this->addComment( - $this->formatAuthor($comment->author), - $comment->created, - $comment->body + $this->helper->getIssueComment( + $this->formatAuthor($comment->author), + $comment->created, + $comment->body + ) ); } } @@ -145,35 +148,16 @@ protected function formatAuthor($author) } } - protected function formatBody($body) + /** + * Add comment to the issue detail + * + * @param HtmlElement[] $comment + * + * @return $this + */ + protected function addComment(array $comment) { - $html = Html::wantHtml($body)->render(); - - // This is safe. - return new HtmlString($this->replaceLinks($html)); - } - - protected function replaceLinks($string) - { - return \preg_replace_callback('/\[([^|]+)\|([^]]+)]/', function ($match) { - return Html::tag('a', ['href' => $match[2], 'target' => '_blank'], $match[1]); - }, $string); - } - - protected function addComment($author, $time, $body) - { - return $this->addWideRow([ - Html::tag('h3', null, Html::sprintf( - '%s: %s', - $this->helper->shortTimeSince($time), - $author - )), - Html::tag( - 'pre', - ['class' => 'comment'], - $this->formatBody($body) - ), - ]); + return $this->addWideRow($comment); } protected function createNameValueRow($name, $value) diff --git a/library/Jira/Web/Table/IssuesTable.php b/library/Jira/Web/Table/IssuesTable.php index eb40dc5..7fa7a1f 100644 --- a/library/Jira/Web/Table/IssuesTable.php +++ b/library/Jira/Web/Table/IssuesTable.php @@ -33,6 +33,7 @@ protected function assemble() ], null, 'th')); foreach ($this->issues as $issue) { + $issueDescription = $helper->formatBody($issue->fields->description); $this->add(static::tr([ static::td([ $helper->renderAvatar($issue->fields->project), @@ -46,7 +47,7 @@ protected function assemble() $issue->fields->summary, Html::tag( 'p', - $issue->fields->description + $issueDescription ), ])->setSeparator(' '), static::td($helper->shortTimeSince($issue->fields->created)), diff --git a/public/css/module.less b/public/css/module.less index 0f93702..d202384 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -1,10 +1,20 @@ .issue-table { - td:first-of-type { - white-space: nowrap; - } - img { - vertical-align: middle; - } + td:first-of-type { + white-space: nowrap; + } + + img { + vertical-align: middle; + } + + td > p > a { + border-bottom: 1px dotted @text-color-light; + + &:hover { + border-bottom: 1px solid @text-color-light; + text-decoration: none; + } + } } .status-badge { From 35411834fb58caa2085b79e41ff88cd75150523e Mon Sep 17 00:00:00 2001 From: raviks789 <33730024+raviks789@users.noreply.github.com> Date: Wed, 21 Feb 2024 16:11:45 +0100 Subject: [PATCH 2/4] RenderingHelper: Replace object links before html injection --- library/Jira/Web/RenderingHelper.php | 35 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/library/Jira/Web/RenderingHelper.php b/library/Jira/Web/RenderingHelper.php index 499c54e..3322f96 100644 --- a/library/Jira/Web/RenderingHelper.php +++ b/library/Jira/Web/RenderingHelper.php @@ -101,23 +101,11 @@ public function getIssueComment($author, string $time, string $body): array */ public function formatBody(string $body): HtmlString { - $html = Html::wantHtml($body)->render(); - - // This is safe. - return new HtmlString($this->replaceLinks($html) ?? ''); - } + $urls = []; - /** - * Replace object urls in the given string with link elements - * - * @param string $string - * - * @return ?string - */ - protected function replaceLinks(string $string): ?string - { - return preg_replace_callback('/\[([^|]+)\|([^]]+)]/', function ($match) { - $url = Url::fromPath(htmlspecialchars_decode($match[2])); + // Replace object urls in the given string with link elements + $body = preg_replace_callback('/\[([^|]+)\|([^]]+)]/', function ($match) use (&$urls) { + $url = Url::fromPath($match[2]); $link = new Link( $match[1], $url, @@ -169,8 +157,19 @@ protected function replaceLinks(string $string): ?string $this->setHostLink($hostLink); } - return $link->render(); - }, $string); + $urls[] = $link->render(); + + return '$objectLink' . (count($urls) - 1) . '$'; + }, $body); + + $html = Html::wantHtml($body)->render(); + + foreach ($urls as $i => $url) { + $html = str_replace('$objectLink' . $i . '$', $url, $html); + } + + // This is safe. + return new HtmlString($html); } public function linkToJira($caption, $url, $attributes = []) From 3dde64e6b7eb25eda61376845f4517b048ff637a Mon Sep 17 00:00:00 2001 From: raviks789 <33730024+raviks789@users.noreply.github.com> Date: Tue, 13 Feb 2024 09:44:43 +0100 Subject: [PATCH 3/4] phpstan: Update baseline --- phpstan-baseline-standard.neon | 95 ---------------------------------- 1 file changed, 95 deletions(-) diff --git a/phpstan-baseline-standard.neon b/phpstan-baseline-standard.neon index 431da3c..14eb4ee 100644 --- a/phpstan-baseline-standard.neon +++ b/phpstan-baseline-standard.neon @@ -425,16 +425,6 @@ parameters: count: 1 path: library/Jira/IcingaCommandPipe.php - - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\IcingaCommandPipe\\:\\:setMonitoringInfo\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Jira/IcingaCommandPipe.php - - - - message: "#^Property Icinga\\\\Module\\\\Jira\\\\IcingaCommandPipe\\:\\:\\$monitoringInfo has no type specified\\.$#" - count: 1 - path: library/Jira/IcingaCommandPipe.php - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\IcingadbBackend\\:\\:getObject\\(\\) should return Icinga\\\\Module\\\\Icingadb\\\\Model\\\\Host\\|Icinga\\\\Module\\\\Icingadb\\\\Model\\\\Service but returns ipl\\\\Orm\\\\Model\\.$#" count: 1 @@ -1615,46 +1605,6 @@ parameters: count: 1 path: library/Jira/Web/RenderingHelper.php - - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\Web\\\\RenderingHelper\\:\\:linkToMonitoring\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Jira/Web/RenderingHelper.php - - - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\Web\\\\RenderingHelper\\:\\:linkToMonitoring\\(\\) has parameter \\$host with no type specified\\.$#" - count: 1 - path: library/Jira/Web/RenderingHelper.php - - - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\Web\\\\RenderingHelper\\:\\:linkToMonitoring\\(\\) has parameter \\$service with no type specified\\.$#" - count: 1 - path: library/Jira/Web/RenderingHelper.php - - - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\Web\\\\RenderingHelper\\:\\:linkToMonitoringHost\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Jira/Web/RenderingHelper.php - - - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\Web\\\\RenderingHelper\\:\\:linkToMonitoringHost\\(\\) has parameter \\$host with no type specified\\.$#" - count: 1 - path: library/Jira/Web/RenderingHelper.php - - - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\Web\\\\RenderingHelper\\:\\:linkToMonitoringService\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Jira/Web/RenderingHelper.php - - - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\Web\\\\RenderingHelper\\:\\:linkToMonitoringService\\(\\) has parameter \\$host with no type specified\\.$#" - count: 1 - path: library/Jira/Web/RenderingHelper.php - - - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\Web\\\\RenderingHelper\\:\\:linkToMonitoringService\\(\\) has parameter \\$service with no type specified\\.$#" - count: 1 - path: library/Jira/Web/RenderingHelper.php - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\Web\\\\RenderingHelper\\:\\:renderAvatar\\(\\) has no return type specified\\.$#" count: 1 @@ -1755,26 +1705,6 @@ parameters: count: 1 path: library/Jira/Web/Table/IssueDetails.php - - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\Web\\\\Table\\\\IssueDetails\\:\\:addComment\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Jira/Web/Table/IssueDetails.php - - - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\Web\\\\Table\\\\IssueDetails\\:\\:addComment\\(\\) has parameter \\$author with no type specified\\.$#" - count: 1 - path: library/Jira/Web/Table/IssueDetails.php - - - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\Web\\\\Table\\\\IssueDetails\\:\\:addComment\\(\\) has parameter \\$body with no type specified\\.$#" - count: 1 - path: library/Jira/Web/Table/IssueDetails.php - - - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\Web\\\\Table\\\\IssueDetails\\:\\:addComment\\(\\) has parameter \\$time with no type specified\\.$#" - count: 1 - path: library/Jira/Web/Table/IssueDetails.php - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\Web\\\\Table\\\\IssueDetails\\:\\:addComments\\(\\) has no return type specified\\.$#" count: 1 @@ -1850,31 +1780,6 @@ parameters: count: 1 path: library/Jira/Web/Table/IssueDetails.php - - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\Web\\\\Table\\\\IssueDetails\\:\\:formatBody\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Jira/Web/Table/IssueDetails.php - - - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\Web\\\\Table\\\\IssueDetails\\:\\:formatBody\\(\\) has parameter \\$body with no type specified\\.$#" - count: 1 - path: library/Jira/Web/Table/IssueDetails.php - - - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\Web\\\\Table\\\\IssueDetails\\:\\:replaceLinks\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Jira/Web/Table/IssueDetails.php - - - - message: "#^Method Icinga\\\\Module\\\\Jira\\\\Web\\\\Table\\\\IssueDetails\\:\\:replaceLinks\\(\\) has parameter \\$string with no type specified\\.$#" - count: 1 - path: library/Jira/Web/Table/IssueDetails.php - - - - message: "#^Parameter \\#2 \\$callback of function preg_replace_callback expects callable\\(array\\\\)\\: string, Closure\\(mixed\\)\\: ipl\\\\Html\\\\HtmlElement given\\.$#" - count: 1 - path: library/Jira/Web/Table/IssueDetails.php - - message: "#^Property Icinga\\\\Module\\\\Jira\\\\Web\\\\Table\\\\IssueDetails\\:\\:\\$helper has no type specified\\.$#" count: 1 From f63a73f51d1bb0e395f6abd6a2442ba71aa4213a Mon Sep 17 00:00:00 2001 From: raviks789 Date: Wed, 17 Apr 2024 17:41:02 +0200 Subject: [PATCH 4/4] Fix incorrect object link rendering --- library/Jira/Web/RenderingHelper.php | 153 +++++++++++++++--------- library/Jira/Web/Table/IssueDetails.php | 2 + 2 files changed, 96 insertions(+), 59 deletions(-) diff --git a/library/Jira/Web/RenderingHelper.php b/library/Jira/Web/RenderingHelper.php index 3322f96..8ffa1f6 100644 --- a/library/Jira/Web/RenderingHelper.php +++ b/library/Jira/Web/RenderingHelper.php @@ -20,12 +20,47 @@ class RenderingHelper { protected $api; + /** @var ?string Host name from the icingaKey JIRA ticket field */ + protected $hostName; + + /** @var ?string Service name from the icingaKey JIRA ticket field */ + protected $serviceName; + /** @var ?Link Host link */ protected $hostLink; /** @var ?Link Service link */ protected $serviceLink; + /** + * Set the name of monitored host + * + * @param string $hostName + * + * @return $this + */ + public function setHostName(string $hostName): self + { + $this->hostName = $hostName; + + return $this; + } + + + /** + * Set the name of monitored service + * + * @param string $serviceName + * + * @return $this + */ + public function setServiceName(string $serviceName): self + { + $this->serviceName = $serviceName; + + return $this; + } + /** * Set the link of monitored host * @@ -101,75 +136,75 @@ public function getIssueComment($author, string $time, string $body): array */ public function formatBody(string $body): HtmlString { - $urls = []; - // Replace object urls in the given string with link elements - $body = preg_replace_callback('/\[([^|]+)\|([^]]+)]/', function ($match) use (&$urls) { + $body = preg_replace_callback('/\[([^|]+)\|([^]]+)]/', function ($match) { $url = Url::fromPath($match[2]); - $link = new Link( - $match[1], - $url, - ['target' => '_blank'] - ); - - if ($url->hasParam('service') || $url->hasParam('host.name')) { - if ( - strpos($match[2], 'icingaweb2/monitoring') !== false - && (Module::exists('icingadb') && IcingadbSupport::useIcingaDbAsBackend()) - ) { - $link = new Link( - $match[1], - Url::fromPath( - 'icingadb/service', - [ - 'name' => $url->getParam('service'), - 'host.name' => $url->getParam('host'), - ] - ), - ['target' => '_blank'] - ); - } + $link = new Link($match[1], $url); + $urlPath = $url->getPath(); + + $monitoringObjectLink = in_array($urlPath, ['monitoring/host/show', 'monitoring/service/show']); + $icingadbObjectLink = in_array($urlPath, ['icingadb/host', 'icingadb/service']); + + if ($monitoringObjectLink || $icingadbObjectLink) { + $transformToIcingadbLink = $monitoringObjectLink + && Module::exists('icingadb') + && IcingadbSupport::useIcingaDbAsBackend(); + if (strpos($urlPath, '/service') !== false) { + if ($monitoringObjectLink) { + $urlServiceParam = $url->getParam('service'); + $urlHostParam = $url->getParam('host'); + if ($transformToIcingadbLink) { + $url->setPath('icingadb/service') + ->remove(['service', 'host']) + ->overwriteParams(['name' => $urlServiceParam, 'host.name' => $urlHostParam]); + $link->setUrl($url); + } + } else { + $urlServiceParam = $url->getParam('name'); + $urlHostParam = $url->getParam('host.name'); + } - $serviceLink = clone $link; - $serviceLink->setContent([new Icon('cog'), $match[1]]) - ->addAttributes(['title' => t('Show Icinga Service State')]); - $this->setServiceLink($serviceLink); - } else { - $icon = new Icon('server'); - if (strpos($match[2], 'icingaweb2/monitoring') !== false) { - if (Module::exists('icingadb') && IcingadbSupport::useIcingaDbAsBackend()) { - $link = new Link( - $match[1], - Url::fromPath( - 'icingadb/host', - ['name' => $url->getParam('host')] - ), - ['target' => '_blank'] - ); + if ( + ! $this->serviceLink + && $urlServiceParam === $this->serviceName + && $urlHostParam === $this->hostName + ) { + $serviceLink = clone $link; + $serviceLink->setContent([new Icon('cog'), $match[1]]) + ->addAttributes(['title' => t('Show Icinga Service State')]); + $this->setServiceLink($serviceLink); + } + } else { + $icon = new Icon('server'); + if ($monitoringObjectLink) { + $urlHostParam = $url->getParam('host'); + if ($transformToIcingadbLink) { + $url->setPath('icingadb/host') + ->remove('host') + ->setParam('name', $urlHostParam); + $link->setUrl($url); + } else { + $icon = new Icon('laptop'); + } } else { - $icon = new Icon('laptop'); + $urlHostParam = $url->getParam('name'); } - } - $hostLink = clone $link; - $hostLink->setContent([$icon, $match[1]]) - ->addAttributes(['title' => t('Show Icinga Host State')]); - $this->setHostLink($hostLink); + if (! $this-> hostLink && $urlHostParam === $this->hostName) { + $hostLink = clone $link; + $hostLink->setContent([$icon, $match[1]]) + ->addAttributes(['title' => t('Show Icinga Host State')]); + $this->setHostLink($hostLink); + } + } + } elseif ($url->isExternal()) { + $link->addAttributes(['target' => '_blank']); } - $urls[] = $link->render(); - - return '$objectLink' . (count($urls) - 1) . '$'; + return $link->render(); }, $body); - $html = Html::wantHtml($body)->render(); - - foreach ($urls as $i => $url) { - $html = str_replace('$objectLink' . $i . '$', $url, $html); - } - - // This is safe. - return new HtmlString($html); + return new HtmlString($body); } public function linkToJira($caption, $url, $attributes = []) diff --git a/library/Jira/Web/Table/IssueDetails.php b/library/Jira/Web/Table/IssueDetails.php index 334a64f..da898c4 100644 --- a/library/Jira/Web/Table/IssueDetails.php +++ b/library/Jira/Web/Table/IssueDetails.php @@ -40,10 +40,12 @@ protected function assemble() $icingaKey = preg_replace('/^BEGIN(.+)END$/', '$1', $fields->$keyField); $parts = explode('!', $icingaKey); $host = array_shift($parts); + $helper->setHostName($host); if (empty($parts)) { $service = null; } else { $service = array_shift($parts); + $helper->setServiceName($service); } if (isset($fields->icingaUser)) { $user = $fields->icingaUser;