Skip to content

Commit

Permalink
Merge branch 'rendering-scripts' into 'main'
Browse files Browse the repository at this point in the history
Rendering scripts

See merge request reportcreator/reportcreator!381
  • Loading branch information
MWedl committed Dec 21, 2023
2 parents 76790c5 + e811e14 commit 0e7d230
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 17 deletions.
2 changes: 2 additions & 0 deletions api/src/reportcreator_api/tests/test_rendering.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ def extract_html_part(self, html, start=None, end=None):
("{{ formatDate('2022-09-21', 'long', 'en-US') }}", "September 21, 2022"),
("{{ formatDate('2022-09-21', 'full', 'en-US') }}", "Wednesday, September 21, 2022"),
("{{ formatDate('2022-09-21', {year: '2-digit', month: 'narrow', day: '2-digit', numberingSystem: 'latn'}, 'en-US') }}", "S 21, 22"),
("""<div :set="helperFunction = function () { return report.title + ' function'; }">{{ helperFunction() }}</div>""", lambda self: f"<div set=\"function () {{ return report.title + ' function'; }}\">{self.project.data['title']} function</div>"),
("""<div :set="computedVar = computed(() => report.title + ' computed')">{{ computedVar.value }}</div>""", lambda self: f"<div set=\"[object Object]\">{self.project.data['title']} computed</div>"),
])
def test_variables_rendering(self, template, html):
if callable(html):
Expand Down
29 changes: 28 additions & 1 deletion docs/docs/designer/formatting-utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,31 @@ English (default): <comma-and-join>...</comma-and-join>
English (no commas, always "and"): <comma-and-join comma=" and " and=" and ">...</comma-and-join>
German: <comma-and-join and=" und ">...</comma-and-join>
French: <comma-and-join and=" et ">...</comma-and-join>
```
```


## Helper Functions
It is possible to define helper functions and variables inside the Vue template language to reuse logic.
Setting variables only works for native DOM tag (e.g. `<div>`, `<span>`, etc.), but not for Vue components (e.g. `<template>`, `<table-of-contents>`, etc.).
The name of the `:set` attributes does not matter, but they have to be unique per tag.
Helper functions are defined at the start of the template, they can be used by following template elements.

```html
<div
v-show="false"
:set1="helperFunction1 = function() {
return report.title + ' processed by helper function';
}"
:set3="calculateCustomScore = (finding) => finding.exploitability * finding.impact"
:set2="computedProperty = computed(() => report.title + ' processed by computed property')"

/>
<div>
Call helper function (without arguments): {{ helperFunction() }}<br>
Call helper function (with arguments): {{ calculateCustomScore(report.findings[0]) }}<br>
Use computed property: {{ computedProperty.value }}<br>
</div>
```

Note that defining variables and helper functions is not officially supported by the Vue template language, but rather a workaround.
For more details see: https://stackoverflow.com/questions/43999618/how-to-define-a-temporary-variable-in-vue-js-template
59 changes: 43 additions & 16 deletions rendering/src/main.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createApp, compile, nextTick } from 'vue';
import { createApp, compile, computed, ref } from 'vue';
import { generateCodeFrame } from '@vue/shared';
import ChartJsPluginDataLabels from 'chartjs-plugin-datalabels';
import Pagebreak from './components/Pagebreak.vue';
Expand All @@ -13,7 +13,6 @@ import Ref from './components/Ref.vue';
import { callForTicks } from './utils';
import lodash from 'lodash';


// injected as global variables
const REPORT_TEMPLATE = '<div>' + (window.REPORT_TEMPLATE || '') + '</div>';
const REPORT_DATA = window.REPORT_DATA || { report: {}, findings: [] };
Expand Down Expand Up @@ -59,20 +58,15 @@ const DEFAULT_COMPUTED = {
count_info: this.findings_info.length,
};
},
lodash() {
return lodash;
},
chartjsPlugins() {
return {
DataLabels: ChartJsPluginDataLabels
};
},
window() {
return window;
},
document() {
return document;
},
lodash: () => lodash,
window: () => window,
document: () => document,
computed: () => computed,
};

const DEFAULT_METHODS = {
Expand Down Expand Up @@ -144,6 +138,8 @@ if (!window.RENDERING_COMPLETED) {
data: () => ({
data: REPORT_DATA,
_tickCount: 0,
_pendingPromises: [],
_observer: null,
}),
computed: {
...DEFAULT_COMPUTED,
Expand All @@ -153,12 +149,43 @@ if (!window.RENDERING_COMPLETED) {
...DEFAULT_METHODS,
...REPORT_METHODS,
},
created() {
this._observer = new MutationObserver((mutationList) => {
for (const mutation of mutationList) {
if (mutation.type === 'childList') {
for (const node of mutation.addedNodes) {
if (node.nodeType === Node.ELEMENT_NODE && node.nodeName === 'SCRIPT') {
this._pendingPromises.push(new Promise((resolve, reject) => {
node.addEventListener('load', resolve);
node.addEventListener('error', reject);
}));
}
}
}
}
});
this._observer.observe(document, { childList: true, subtree: true });
},
beforeUnmount() {
this._observer.disconnect();
},
async mounted() {
// Wait some ticks before rendering is signaled as completed
// Allow multi-pass rendering (for e.g. table of contents)
await callForTicks(10, nextTick, () => {
this._tickCount += 1;
})
const waitUntilFinished = async () => {
// Wait some ticks before rendering is signaled as completed
// Allow multi-pass rendering (for e.g. table of contents)
await callForTicks(10, () => {
this._tickCount += 1;
});
// Wait for pending promises to finish
if (this._pendingPromises.length > 0) {
await Promise.allSettled(this._pendingPromises);
await callForTicks(10, () => {
this._tickCount += 1;
});
}
}
await waitUntilFinished();

window.RENDERING_COMPLETED = true;
},
});
Expand Down

0 comments on commit 0e7d230

Please sign in to comment.