mirror of https://github.com/MISP/MISP
345 lines
15 KiB
PHP
345 lines
15 KiB
PHP
<?php
|
|
$clusteredTags = $trendAnalysis['clustered_tags'];
|
|
$clusteredEvents = $trendAnalysis['clustered_events'];
|
|
$allTags = $trendAnalysis['all_tags'];
|
|
$allTimestamps = $trendAnalysis['all_timestamps'];
|
|
$currentPeriod = $allTimestamps[0];
|
|
$previousPeriod = $allTimestamps[1];
|
|
$previousPeriod2 = $allTimestamps[2];
|
|
|
|
$allUniqueTagsPerPeriod = array_map(function ($tags) {
|
|
return array_keys($tags);
|
|
}, $clusteredTags);
|
|
$allUniqueTags = array_unique(array_merge($allUniqueTagsPerPeriod[$currentPeriod], $allUniqueTagsPerPeriod[$previousPeriod], $allUniqueTagsPerPeriod[$previousPeriod2]));
|
|
App::uses('ColourPaletteTool', 'Tools');
|
|
$paletteTool = new ColourPaletteTool();
|
|
$COLOR_PALETTE = $paletteTool->createColourPalette(max(count($allUniqueTags), 1));
|
|
|
|
$trendIconMapping = [
|
|
1 => '▲',
|
|
-1 => '▼',
|
|
0 => '⮞',
|
|
'?' => '',
|
|
];
|
|
$trendColorMapping = [
|
|
1 => '#b94a48',
|
|
-1 => '#468847',
|
|
0 => '#3a87ad',
|
|
'?' => '#999999',
|
|
];
|
|
$now = new DateTime();
|
|
$currentPeriodDate = DateTime::createFromFormat('U', $currentPeriod);
|
|
$previousPeriodDate = DateTime::createFromFormat('U', $previousPeriod);
|
|
$previousPeriod2Date = DateTime::createFromFormat('U', $previousPeriod2);
|
|
|
|
$colorForTags = [];
|
|
$chartData = [];
|
|
$maxValue = 0;
|
|
foreach ($allUniqueTags as $i => $tag) {
|
|
$colorForTags[$tag] = $COLOR_PALETTE[$i];
|
|
$chartData[$tag] = [
|
|
$clusteredTags[$previousPeriod2][$tag]['occurence'] ?? 0,
|
|
$clusteredTags[$previousPeriod][$tag]['occurence'] ?? 0,
|
|
$clusteredTags[$currentPeriod][$tag]['occurence'] ?? 0,
|
|
];
|
|
$maxValue = max($maxValue, max($chartData[$tag]));
|
|
}
|
|
$canvasWidth = 600;
|
|
$canvasHeight = 150;
|
|
foreach (array_keys($chartData) as $tag) {
|
|
$chartData[$tag][0] = [0, $canvasHeight - ($chartData[$tag][0] / $maxValue) * $canvasHeight];
|
|
$chartData[$tag][1] = [$canvasWidth / 2, $canvasHeight - ($chartData[$tag][1] / $maxValue) * $canvasHeight];
|
|
$chartData[$tag][2] = [$canvasWidth, $canvasHeight - ($chartData[$tag][2] / $maxValue) * $canvasHeight];
|
|
}
|
|
|
|
if (!function_exists('reduceTag')) {
|
|
function reduceTag(string $tagname, int $reductionLength = 1): string
|
|
{
|
|
$re = '/^(?<namespace>[a-z0-9_-]+)(:(?<predicate>[a-z0-9_-]+)="(?<value>[^"]+)"$)?(:(?<predicate2>[a-z0-9_-]+))?/';
|
|
$matches = [];
|
|
preg_match($re, $tagname, $matches);
|
|
if (!empty($matches['predicate2'])) {
|
|
return $reductionLength == 0 ? $tagname : $matches['predicate2'];
|
|
} else if (!empty($matches['value'])) {
|
|
return $reductionLength == 0 ? $tagname : ($reductionLength == 1 ? sprintf('%s="%s"', $matches['predicate'], $matches['value']) : $matches['value']
|
|
);
|
|
} else if (!empty($matches['namespace'])) {
|
|
return $matches['namespace'];
|
|
} else {
|
|
return $tagname;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!function_exists('computeLinePositions')) {
|
|
function computeLinePositions(float $x1, float $y1, float $x2, float $y2): array
|
|
{
|
|
$x_offset = 3.5;
|
|
$y_offset = 1;
|
|
$conf = [
|
|
'left' => $x1 + $x_offset,
|
|
'top' => $y1 + $y_offset,
|
|
'width' => sqrt(pow($y2 - $y1, 2) + pow($x2 - $x1, 2)),
|
|
'angle' => atan(($y2 - $y1) / ($x2 - $x1)),
|
|
];
|
|
return $conf;
|
|
}
|
|
}
|
|
|
|
?>
|
|
|
|
<div style="display: flex; column-gap: 20px; justify-content: space-around; margin-bottom: 40px;">
|
|
<div style="display: flex; align-items: center;">
|
|
<table class="table table-condensed" style="min-width: 300px; max-width: 400px; margin: 0;">
|
|
<tbody>
|
|
<tr>
|
|
<td><?= __('Period duration') ?></td>
|
|
<td><?= __('%s days', $currentPeriodDate->diff($now)->format('%a')); ?></td>
|
|
</tr>
|
|
<tr>
|
|
<td><?= __('Starting period') ?></td>
|
|
<td><?= sprintf('%s', $currentPeriodDate->format('M d, o. (\W\e\e\k W)')); ?></td>
|
|
</tr>
|
|
<tr>
|
|
<td><?= __('Previous period') ?></td>
|
|
<td><?= sprintf('%s', $previousPeriodDate->format('M d, o. (\W\e\e\k W)')); ?></td>
|
|
</tr>
|
|
<tr>
|
|
<td><?= __('Period-2') ?></td>
|
|
<td><?= sprintf('%s', $previousPeriod2Date->format('M d, o. (\W\e\e\k W)')); ?></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php if (!empty($allUniqueTags)) : ?>
|
|
<div style="padding: 0 40px;">
|
|
<div class="chart-container">
|
|
<div class="y-axis-container">
|
|
<div>
|
|
<span class="y-axis-label" style="<?= sprintf('left: %spx; top: %spx; transform: translate(-100%%, %s%%)', 0, 0, -25) ?>"><?= h($maxValue) ?></span>
|
|
<span class="y-axis-label" style="<?= sprintf('left: %spx; top: %spx; transform: translate(-100%%, %s%%)', 0, ($canvasHeight - 20)/2, 0) ?>"><?= h(round($maxValue / 2, 2)) ?></span>
|
|
<span class="y-axis-label" style="<?= sprintf('left: %spx; top: %spx; transform: translate(-100%%, %s%%)', 0, ($canvasHeight - 20), 25) ?>"><?= 0 ?></span>
|
|
</div>
|
|
</div>
|
|
<div class="canvas">
|
|
<?php foreach ($chartData as $tag => $coords) : ?>
|
|
<?php for ($i = 0; $i < 3; $i++) : ?>
|
|
<?php
|
|
$coord = $coords[$i];
|
|
$previousCoord = isset($coords[$i - 1]) ? $coords[$i - 1] : false;
|
|
?>
|
|
<span class="dot" style="<?= sprintf('left: %spx; top: %spx; background-color: %s;', $coord[0], $coord[1], $colorForTags[$tag]) ?>" title="<?= h($tag) ?>"></span>
|
|
<?php
|
|
if (!empty($previousCoord)) {
|
|
$linePosition = computeLinePositions($previousCoord[0], $previousCoord[1], $coord[0], $coord[1]);
|
|
echo sprintf(
|
|
'<span class="line" style="left: %spx; top: %spx; width: %spx; transform: rotate(%srad); background-color: %s;" title="%s"></span>',
|
|
$linePosition['left'],
|
|
$linePosition['top'],
|
|
$linePosition['width'],
|
|
$linePosition['angle'],
|
|
$colorForTags[$tag],
|
|
h($tag)
|
|
);
|
|
}
|
|
?>
|
|
<?php endfor ?>
|
|
<?php endforeach ?>
|
|
</div>
|
|
<div class="x-axis-container">
|
|
<span class="x-axis-label" style="<?= sprintf('left: %spx; top: %spx;', 0, 0) ?>"><?= __('Period-2') ?></span>
|
|
<span class="x-axis-label" style="<?= sprintf('left: %spx; top: %spx;', $canvasWidth / 2, 0) ?>"><?= __('Previous period') ?></span>
|
|
<span class="x-axis-label" style="<?= sprintf('left: %spx; top: %spx;', $canvasWidth, 0) ?>"><?= __('Starting period') ?></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php else : ?>
|
|
<p><?= __('- No tag for the selected tag namespace -') ?></p>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<?php if (!empty($allTags)) : ?>
|
|
<table class="table table-condensed no-border">
|
|
<thead>
|
|
<tr>
|
|
<th></th>
|
|
<th>
|
|
<span>
|
|
<div><?= __('Period-2') ?></div>
|
|
<div style="font-weight: normal;"><?= __('%s events', h($clusteredEvents[$previousPeriod2])) ?></div>
|
|
</span>
|
|
<table>
|
|
<thead style="font-size: small;">
|
|
<tr>
|
|
<td style="min-width: 20px;">#</td>
|
|
<td style="min-width: 15px;">⥮</td>
|
|
<td>%</td>
|
|
<td></td>
|
|
</tr>
|
|
</thead>
|
|
</table>
|
|
</th>
|
|
<th>
|
|
<span>
|
|
<div><?= __('Previous period') ?></div>
|
|
<div style="font-weight: normal;"><?= __('%s events', h($clusteredEvents[$previousPeriod])) ?></div>
|
|
</span>
|
|
<table>
|
|
<thead style="font-size: small;">
|
|
<tr>
|
|
<td style="min-width: 20px;">#</td>
|
|
<td style="min-width: 15px;">⥮</td>
|
|
<td>%</td>
|
|
<td></td>
|
|
</tr>
|
|
</thead>
|
|
</table>
|
|
</th>
|
|
<th>
|
|
<span>
|
|
<div><?= __('Starting period') ?></div>
|
|
<div style="font-weight: normal;"><?= __('%s events', h($clusteredEvents[$currentPeriod])) ?></div>
|
|
</span>
|
|
<table>
|
|
<thead style="font-size: small;">
|
|
<tr>
|
|
<td style="min-width: 20px;">#</td>
|
|
<td style="min-width: 15px;">⥮</td>
|
|
<td>%</td>
|
|
<td></td>
|
|
</tr>
|
|
</thead>
|
|
</table>
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<?php foreach ($tagFilterPrefixes as $tagPrefix) : ?>
|
|
<?php
|
|
if (empty($allTags[$tagPrefix])) {
|
|
continue;
|
|
}
|
|
?>
|
|
<tbody>
|
|
<tr>
|
|
<td colspan="4">
|
|
<h4><?= __('Tag namespace: %s', sprintf('<code>%s</code>', h($tagPrefix))) ?></h4>
|
|
</td>
|
|
</tr>
|
|
<?php foreach ($allTags[$tagPrefix] as $tagName) : ?>
|
|
<tr>
|
|
<td style="padding-left: 15px;">
|
|
<span class="tag-legend" style="background-color: <?= $colorForTags[$tagName] ?>;"></span>
|
|
<code><?= h(reduceTag($tagName, count(explode(':', $tagPrefix)))) ?></code>
|
|
</td>
|
|
<td>
|
|
<table class="table-condensed no-border">
|
|
<tbody>
|
|
<tr>
|
|
<td><?= h($clusteredTags[$previousPeriod2][$tagName]['occurence'] ?? '-') ?></td>
|
|
<td><?= h($clusteredTags[$previousPeriod2][$tagName]['raw_change'] ?? '-') ?></td>
|
|
<td><?= h($clusteredTags[$previousPeriod2][$tagName]['percent_change'] ?? '-') ?>%</td>
|
|
<td style="font-size: large; color: <?= $trendColorMapping[$clusteredTags[$previousPeriod2][$tagName]['change_sign'] ?? '?'] ?>"><?= $trendIconMapping[$clusteredTags[$previousPeriod2][$tagName]['change_sign'] ?? '?'] ?></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</td>
|
|
<td>
|
|
<table class="table-condensed no-border">
|
|
<tbody>
|
|
<tr>
|
|
<td><?= h($clusteredTags[$previousPeriod][$tagName]['occurence'] ?? '-') ?></td>
|
|
<td><?= h($clusteredTags[$previousPeriod][$tagName]['raw_change'] ?? '-') ?></td>
|
|
<td><?= h($clusteredTags[$previousPeriod][$tagName]['percent_change'] ?? '-') ?>%</td>
|
|
<td style="font-size: large; color: <?= $trendColorMapping[$clusteredTags[$previousPeriod][$tagName]['change_sign'] ?? '?'] ?>"><?= $trendIconMapping[$clusteredTags[$previousPeriod][$tagName]['change_sign'] ?? '?'] ?></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</td>
|
|
<td>
|
|
<table class="table-condensed no-border">
|
|
<tbody>
|
|
<tr>
|
|
<td><?= h($clusteredTags[$currentPeriod][$tagName]['occurence'] ?? '-') ?></td>
|
|
<td><?= h($clusteredTags[$currentPeriod][$tagName]['raw_change'] ?? '-') ?></td>
|
|
<td><?= h($clusteredTags[$currentPeriod][$tagName]['percent_change'] ?? '-') ?>%</td>
|
|
<td style="font-size: large; color: <?= $trendColorMapping[$clusteredTags[$currentPeriod][$tagName]['change_sign'] ?? '?'] ?>"><?= $trendIconMapping[$clusteredTags[$currentPeriod][$tagName]['change_sign'] ?? '?'] ?></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
<?php endforeach; ?>
|
|
</table>
|
|
<?php endif; ?>
|
|
|
|
<style>
|
|
.dot {
|
|
position: absolute;
|
|
height: 7px;
|
|
width: 7px;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.line {
|
|
position: absolute;
|
|
background: blue;
|
|
height: 3px;
|
|
transform-origin: left center;
|
|
box-shadow: 1px 1px 2px 0px #00000066;
|
|
}
|
|
|
|
.chart-container {
|
|
position: relative;
|
|
background-color: #dddddd33;
|
|
padding: 5px 35px 5px 45px;
|
|
border-radius: 5px;
|
|
border: 1px solid #ddd;
|
|
}
|
|
|
|
.canvas {
|
|
width: 610px;
|
|
height: 160px;
|
|
position: relative;
|
|
}
|
|
|
|
.x-axis-container {
|
|
position: relative;
|
|
height: 20px;
|
|
}
|
|
|
|
.x-axis-label {
|
|
position: absolute;
|
|
white-space: nowrap;
|
|
translate: -50%;
|
|
}
|
|
|
|
.y-axis-container {
|
|
height: 150px;
|
|
border-right: 1px solid #000;
|
|
position: absolute;
|
|
left: -5px;
|
|
top: 10px;
|
|
padding-left: inherit;
|
|
}
|
|
|
|
.y-axis-container > div {
|
|
position: relative;
|
|
height: 100%;
|
|
}
|
|
|
|
.y-axis-label {
|
|
position: absolute;
|
|
white-space: nowrap;
|
|
/* transform: translate(-100%, 0%); */
|
|
padding: 0 5px;
|
|
}
|
|
|
|
.tag-legend {
|
|
display: inline-block;
|
|
width: 10px;
|
|
height: 10px;
|
|
border: 1px solid #000;
|
|
}
|
|
</style>
|