Merge pull request #5707 from MISP/feature-widgets-scoped-css

Scoped css for widget
pull/5709/head
Andras Iklody 2020-03-20 09:09:40 +01:00 committed by GitHub
commit d384cced23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 120 additions and 18 deletions

View File

@ -4,6 +4,7 @@ App::uses('AppController', 'Controller');
class DashboardsController extends AppController
{
public $components = array('Session', 'RequestHandler');
public $helpers = array('ScopedCSS');
public function beforeFilter()
{

View File

@ -1,6 +1,10 @@
<?php
$widgetHtml = $this->element('/dashboard/Widgets/' . $config['render']);
$scopedHtml = $this->ScopedCSS->createScopedCSS($widgetHtml);
?>
<div id="widgetContentInner_<?= h($widget_id) ?>">
<?php
echo $this->element('/dashboard/Widgets/' . $config['render']);
echo $scopedHtml['bundle'];
?>
</div>
<script type="text/javascript">

View File

@ -96,3 +96,24 @@ var insight = "<?= h($data['insight']) ?>";
.attr("dy", ".35em")
.text(function(d) { return d.name; });
</script>
<style widget-scoped>
.path_multi_line_chart {
stroke-width: 1;
fill: none;
stroke-linejoin: round;
stroke-linecap: round;
}
.path_multi_line_chart {
stroke-width: 1;
}
.axis_multi_line_chart path,
.axis_multi_line_chart line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
</style>

View File

@ -0,0 +1,93 @@
<?php
App::uses('AppHelper', 'View/Helper');
// prepend user names on the header with some text based on the given rules
class ScopedCSSHelper extends AppHelper {
private function endsWith($haystack, $needle)
{
$length = strlen($needle);
if ($length == 0) {
return true;
}
return (substr($haystack, -$length) === $needle);
}
private function preppendScopedId($css, $seed)
{
$prependSelector = sprintf('[data-scoped="%s"]', $seed);
$cssLines = explode("\n", $css);
foreach ($cssLines as $i => $line) {
if (strlen($line) > 0) {
if ($this->endsWith($line, "{") || $this->endsWith($line, ",")) {
$cssLines[$i] = sprintf("%s %s", $prependSelector, $line);
}
}
}
$cssScopedLines = implode(PHP_EOL, $cssLines);
return sprintf("<style>%s%s%s</style>", PHP_EOL, $cssScopedLines, PHP_EOL);
}
/**
* Replace a declared CSS scoped style and prepend a random CSS data filter to any CSS selector discovered.
* Usage: Add the following style tag `<style widget-scoped>` to use the scoped feature. Nearly every selector path will have their rule modified to adhere to the scope
* Restrictions:
* - Applying class to the root document (i.e. `body`) will not work
* - Selector rules must end with either `{` or `,`, their content MUST be put in a new line:
* [bad]
* element { ... }
* [good]
* element {
* ...
* }
* - Selectors with the `and` (`,`) rule MUST be split in multiple lines:
* [bad]
* element,element {
* ...
* }
* [good]
* element,
* element {
* ...
* }
* @param string $param1 HTML potentially containing scoped CSS
* @return array Return an array composed of 3 keys (html, css and seed)
* - bundle: Include both scoped HTML and scoped CSS or the original html if the scoped feature is not requested
* - html: Untouched HTML including nested in a scoped DIV or original html if the scoped feature is not requested
* - css: CSS with an additional filter rule prepended to every selectors or the empty string if the scoped feature is not requested
* - seed: The random generated number
* - originalHtml: Untouched HTML
*/
public function createScopedCSS(string $html) : array
{
$css = "";
$seed = "";
$originalHtml = $html;
$bundle = $originalHtml;
$scopedHtml = $html;
$scopedCss = "";
$htmlStyleTag = "<style widget-scoped>";
$styleClosingTag = "</style>";
$styleTagIndex = strpos($html, $htmlStyleTag);
$closingStyleTagIndex = strpos($html, $styleClosingTag, $styleTagIndex) + strlen($styleClosingTag);
if ($styleTagIndex !== false && $closingStyleTagIndex !== false && $closingStyleTagIndex > $styleTagIndex) { // enforced scoped css
$seed = rand();
$css = substr($html, $styleTagIndex, $closingStyleTagIndex);
$html = str_replace($css, "", $html); // remove CSS part
$css = str_replace($htmlStyleTag, "", $css); // remove the style node
$css = str_replace($styleClosingTag, "", $css); // remove closing style node
$scopedCss = $this->preppendScopedId($css, $seed);
$scopedHtml = sprintf("<div %s>%s</div>", sprintf("data-scoped=\"%s\" ", $seed), $html);
$bundle = sprintf("%s %s", $scopedHtml, $scopedCss);
}
return array(
"bundle" => $bundle,
"html" => $scopedHtml,
"css" => $scopedCss,
"seed" => $seed,
"originalHtml" => $originalHtml,
);
}
}

View File

@ -2523,20 +2523,3 @@ table tr:hover .down-expand-button {
font-size: 125%;
margin:5px;
}
.path_multi_line_chart {
stroke-width: 1;
fill: none;
stroke-linejoin: round;
stroke-linecap: round;
}
.path_multi_line_chart {
stroke-width: 1;
}
.axis_multi_line_chart path,
.axis_multi_line_chart line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}