Use `style` element to create css class for dynamic inline style

The `style` element with `nonce` attribute is used to create css classes for
inline styles that are not static. This prevents Content-Security-Policy violations.
This commit is contained in:
raviks789 2023-07-18 10:30:27 +02:00
parent 459f4198c3
commit 33a5f765b9
4 changed files with 192 additions and 51 deletions

View File

@ -6,7 +6,9 @@ namespace Icinga\Web\Widget\Chart;
use DateInterval;
use DateTime;
use Icinga\Util\Color;
use Icinga\Util\Csp;
use Icinga\Web\Widget\AbstractWidget;
use ipl\Web\Style;
/**
* Display a colored grid that visualizes a set of values for each day
@ -32,6 +34,9 @@ class HistoryColorGrid extends AbstractWidget
private $color;
public $opacity = 1.0;
/** @var array<string, array<string, string>> History grid css rulesets */
protected $rulesets = [];
public function __construct($color = '#51e551', $start = null, $end = null)
{
$this->setColor($color);
@ -123,18 +128,30 @@ class HistoryColorGrid extends AbstractWidget
{
if (array_key_exists($day, $this->data) && $this->data[$day]['value'] > 0) {
$entry = $this->data[$day];
return '<a ' .
'style="background-color: ' . $this->calculateColor($entry['value']) . ';'
. ' opacity: ' . $this->opacity . ';" ' .
'aria-label="' . $entry['caption'] . '" ' .
'title="' . $entry['caption'] . '" ' .
'href="' . $entry['url'] . '" ' .
'></a>';
$this->rulesets['.grid-day-with-entry-' . $entry['value']] = [
'background-color' => $this->calculateColor($entry['value']),
'opacity' => $this->opacity
];
return '<a class="grid-day-with-entry-'
. $entry['value']
. '" '
. 'aria-label="' . $entry['caption']
. '" '
. 'title="' . $entry['caption']
. '" '
. 'href="' . $entry['url']
. '" '
. '"></a>';
} else {
return '<span ' .
'style="background-color: ' . $this->calculateColor(0) . '; opacity: ' . $this->opacity . ';" ' .
'title="No entries for ' . $day . '" ' .
'></span>';
if (! isset($this->rulesets['.grid-day-no-entry'])) {
$this->rulesets['.grid-day-no-entry'] = [
'background-color' => $this->calculateColor(0),
'opacity' => $this->opacity
];
}
return '<span class="grid-day-no-entry"' . ' title="No entries for ' . $day . '"></span>';
}
}
@ -366,8 +383,18 @@ class HistoryColorGrid extends AbstractWidget
}
$grid = $this->createGrid();
if ($this->orientation === self::ORIENTATION_HORIZONTAL) {
return $this->renderHorizontal($grid);
$html = $this->renderHorizontal($grid);
} else {
$html = $this->renderVertical($grid);
}
return $this->renderVertical($grid);
$historyGridStyle = new Style();
$historyGridStyle->setNonce(Csp::getStyleNonce());
foreach ($this->rulesets as $selector => $properties) {
$historyGridStyle->add($selector, $properties);
}
return $html . $historyGridStyle;
}
}

View File

@ -1,9 +1,15 @@
<?php
use Icinga\Util\Csp;
use Icinga\Web\Url;
use Icinga\Util\Color;
use ipl\Web\Style;
$groupInfo = $timeline->getGroupInfo();
$firstRow = ! $beingExtended;
$timelineStyle = (new Style())
->setNonce(Csp::getStyleNonce())
->setModule('monitoring');
if (! $beingExtended && !$this->compact): ?>
<div class="controls">
@ -79,24 +85,72 @@ if (! $beingExtended && !$this->compact): ?>
<?php foreach ($groupInfo as $groupName => $labelAndColor): ?>
<?php if (array_key_exists($groupName, $timeInfo[1])): ?>
<?php
$styleId = uniqid();
$circleWidth = $timeline->calculateCircleWidth($timeInfo[1][$groupName], 2);
$extrapolatedCircleWidth = $timeline->getExtrapolatedCircleWidth($timeInfo[1][$groupName], 2);
?>
<?php if ($firstRow && $extrapolatedCircleWidth !== $circleWidth): ?>
<div class="circle-box" style="width: <?= $extrapolatedCircleWidth; ?>;">
<div class="outer-circle extrapolated <?= $timeInfo[1][$groupName]->getClass() ?>" style="<?= sprintf(
'width: %2$s; height: %2$s; margin-top: -%1$Fem;',
(float) substr($extrapolatedCircleWidth, 0, -2) / 2,
$extrapolatedCircleWidth
); ?>">
<?php
$timelineStyle->add(
"#circle-box-$styleId",
['width' => $extrapolatedCircleWidth]
);
$timelineStyle->add(
"#outer-circle-$styleId",
[
'width' => $extrapolatedCircleWidth,
'height' => $extrapolatedCircleWidth,
'margin-top' => sprintf(
'-%Fem',
(float)substr($extrapolatedCircleWidth, 0, -2) / 2
)
]
);
?>
<div id="circle-box-<?= $styleId ?>" class="circle-box">
<div id="outer-circle-<?= $styleId ?>" class="outer-circle extrapolated <?= $timeInfo[1][$groupName]->getClass() ?>">
<?php else: ?>
<div class="circle-box" style="width: <?= $circleWidth; ?>;">
<div class="outer-circle" style="<?= sprintf(
'width: %2$s; height: %2$s; margin-top: -%1$Fem;',
(float) substr($circleWidth, 0, -2) / 2,
$circleWidth
); ?>">
<?php
$timelineStyle->add(
"#circle-box-$styleId",
['width' => $circleWidth]
);
$timelineStyle->add(
"#outer-circle-$styleId",
[
'width' => $circleWidth,
'height' => $circleWidth,
'margin-top' => sprintf(
'-%Fem',
(float)substr($circleWidth, 0, -2) / 2
)
]
);
?>
<div id="circle-box-<?= $styleId ?>" class="circle-box">
<div id="outer-circle-<?= $styleId ?>" class="outer-circle">
<?php endif ?>
<?php
$timelineStyle->add(
"#inner-circle-$styleId",
[
'width' => $circleWidth,
'height' => $circleWidth,
'margin-top' => sprintf(
'-%Fem',
(float)substr($circleWidth, 0, -2) / 2
),
'margin-left' => sprintf(
'-%Fem',
(float)substr($circleWidth, 0, -2) / 2
),
]
);
?>
<?= $this->qlink(
'',
$timeInfo[1][$groupName]->getDetailUrl(),
@ -112,12 +166,8 @@ $extrapolatedCircleWidth = $timeline->getExtrapolatedCircleWidth($timeInfo[1][$g
strtolower($labelAndColor['label']),
$titleTime
),
'class' => 'inner-circle ' . $timeInfo[1][$groupName]->getClass(),
'style' => sprintf(
'width: %2$s; height: %2$s; margin-top: -%1$Fem; margin-left: -%1$Fem;',
(float) substr($circleWidth, 0, -2) / 2,
(string) $circleWidth
)
'id' => "inner-circle-$styleId",
'class' => "inner-circle " . $timeInfo[1][$groupName]->getClass()
)
); ?>
</div>
@ -143,3 +193,4 @@ $extrapolatedCircleWidth = $timeline->getExtrapolatedCircleWidth($timeInfo[1][$g
</div>
</div>
<?php endif ?>
<?= $timelineStyle; ?>

View File

@ -1,12 +1,17 @@
<?php
use Icinga\Util\Csp;
use Icinga\Web\Notification;
use ipl\Web\Style;
$pages = $wizard->getPages();
$finished = isset($success);
$configPages = array_slice($pages, 3, count($pages) - 4, true);
$currentPos = array_search($wizard->getCurrentPage(), $pages, true);
list($configPagesLeft, $configPagesRight) = array_chunk($configPages, (int)(count($configPages) / 2), true);
$setupStyle = (new Style())
->setSelector('.setup-header > .progress-bar')
->setNonce(Csp::getStyleNonce());
$visitedPages = array_keys($wizard->getPageData());
$maxProgress = max(array_merge([0], array_keys(array_filter(
@ -14,6 +19,13 @@ $maxProgress = max(array_merge([0], array_keys(array_filter(
function ($page) use ($visitedPages) { return in_array($page->getName(), $visitedPages); }
))));
$setupStyle->add(
'.width-percent-10',
['width' => '10%']
)->add(
'.width-percent-60',
['width' => '60%']
);
?>
<div id="setup-content-wrapper" data-base-target="layout">
<div class="setup-header">
@ -76,14 +88,43 @@ $maxProgress = max(array_merge([0], array_keys(array_filter(
$pos < $maxProgress ? ' visited' : ($currentPos > 2 ? ' active' : '')
); ?>
<?php if ($page === $firstPage): ?>
<div class="line left<?= $stateClass; ?>" style="float: left; width: <?= sprintf(
'%.2F',
100 - (count($configPagesLeft) - 1) * $lineWidth
); ?>%; margin-right: 0"></div>
<?php
$setupStyle->add(
'.step .left-line-' . $pos,
[
'float' => 'left',
'width' => sprintf(
'%.2F%%',
100 - (count($configPagesLeft) - 1) * $lineWidth
),
'margin-right' => 0
]
);
?>
<div class="line left<?= $stateClass; ?> left-line-<?= $pos; ?>"></div>
<?php elseif ($page === $lastPage): ?>
<div class="line<?= $stateClass; ?>" style="float: left; width: <?= $lineWidth; ?>%; margin-right: -0.1em;"></div>
<?php
$setupStyle->add(
'.step .left-line-' . $pos,
[
'float' => 'left',
'width' => $lineWidth . '%',
'margin-right' => '-0.1em'
]
);
?>
<div class="line<?= $stateClass; ?> left-line-<?= $pos; ?>"></div>
<?php else: ?>
<div class="line<?= $stateClass; ?>" style="float: left; width: <?= $lineWidth; ?>%;"></div>
<?php
$setupStyle->add(
'.step .left-line-' . $pos,
[
'float' => 'left',
'width' => $lineWidth . '%'
]
);
?>
<div class="line<?= $stateClass; ?> left-line-<?= $pos; ?>"></div>
<?php endif ?>
<?php endforeach ?>
</td>
@ -106,14 +147,43 @@ $maxProgress = max(array_merge([0], array_keys(array_filter(
$pos < $maxProgress ? ' visited' : ($currentPos > 2 ? ' active' : '')
); ?>
<?php if ($page === $firstPage): ?>
<div class="line<?= $stateClass; ?>" style="float: left; width: <?= $lineWidth; ?>%; margin-left: -0.1em;"></div>
<?php
$setupStyle->add(
'.step .right-line-' . $pos,
[
'float' => 'left',
'width' => $lineWidth . '%',
'margin-right' => '-0.1em'
]
);
?>
<div class="line<?= $stateClass; ?> right-line-<?= $pos; ?>"></div>
<?php elseif ($page === $lastPage): ?>
<div class="line right<?= $stateClass; ?>" style="float: left; width: <?= sprintf(
'%.2F',
100 - (count($configPagesRight) - 1) * $lineWidth
); ?>%; margin-left: 0;"></div>
<?php
$setupStyle->add(
'.step .right-line-' . $pos,
[
'float' => 'left',
'width' => sprintf(
'%.2F%%',
100 - (count($configPagesRight) - 1) * $lineWidth
),
'margin-right' => 0
]
);
?>
<div class="line right<?= $stateClass; ?> right-line-<?= $pos; ?>"></div>
<?php else: ?>
<div class="line<?= $stateClass; ?>" style="float: left; width: <?= $lineWidth; ?>%;"></div>
<?php
$setupStyle->add(
'.step .right-line-' . $pos,
[
'float' => 'left',
'width' => $lineWidth . '%'
]
);
?>
<div class="line<?= $stateClass; ?> right-line-<?= $pos; ?>"></div>
<?php endif ?>
<?php endforeach ?>
</td>
@ -151,3 +221,4 @@ $maxProgress = max(array_merge([0], array_keys(array_filter(
}
?></ul>
</div>
<?= $setupStyle; ?>

View File

@ -114,14 +114,6 @@
}
}
.width-percent-10 {
width: 10%;
}
.width-percent-60 {
width: 60%;
}
.setup-content {
padding: 1.5em 10em 0 10em;