I have embedded the Gantt diagram inside an existing framework. To have access to most web resources like images, fonts and css, that framework requires users to be logged in. This all works fine when viewing the diagram.
When exporting however, the export server which uses puppeteer (opens a headless chrome to render the html) has all sorts of problems because of that, mixed with CORS problems. I won't go into the details but I've been through a lot of trial and error
Now I've settled on a solution that will eliminate all security problems: make the html data standalone. That is, replace all external links to css, images, etc. by actual binary data in the html data sent to the export server. This should also improve the export server performance.
So the only way I can think of would be to do the CSS url() substitution when the page has finished loading on the user browser, and let the export feature work as usual. But that feels more like a hack and there's a risk of visual glitches
Any suggestion?
I'll gladly share what I've come up with when it's done
Speaking of async flow, while pageTpl is synchronous, there are async methods used by the export feature which you can extend and preload the resources. For instance, you can override the prepareComponent method to preload all the resources:
class MySinglePageExporter extends SinglePageExporter {
static get type() {
return 'mysinglepage';
}
static get title() {
// In case locale is missing exporter is still distinguishable
return 'My single page';
}
async prepareComponent(config) {
await super.prepareComponent(config);
// grab all style/link nodes
let stylesheets = this.stylesheets;
stylesheets = await myPreloadResourcesAndBase64EncodeThem(stylesheets);
// put them back on exporter, these links/styles will be inserted to the pageTpl
this.stylesheets = stylesheets;
}
}
new Grid({
features: {
pdfExport: {
exporters: [MySinglePageExporter]
}
}
})
Ok so as a man of his words, here's what I've come up with. It gathers all external CSS and image tags to make standalone HTML data that will require no additional request on the server side. It works well, but it has two caveats:
Most resources are gathered from cache, but CSS url() resources had to be redownloaded with fetch(). Since CDN resources in there are most likely to fail because of CORS, I decided to just clear them. Not great, but that's the best I could do and it had no impact in our specific situation. This is the meat of the code.
Because the same resource can be parsed multiple times everywhere in the HTML, this can lead to a lot of data for every exported pages. In out case it was a little more than 20 MB for every page of a 10 pages export, so that's 200 MB that's sent from the client to the export server via POST. It also leads to very slow performance of the export server. Not exactly fast and relies partly on the client's internet. But this could be alleviated for very lean Gantts.
Maybe there's some way of fixing #1 by accessing some cache, and also optimizing #2 by referencing the same recource instead of copying it everywhere, but at some point I decided to give up on this strategy.
JQuery is used to parse HTML data and could easily be replaced by native JS.
Not the prettiest code I admit. I would've loved to refactor it into multiple shorter methods but Override does not add new methods and I stopped working on this before I learnedOverride is not meant for permanent overrides and how to do it with prototypes.
Not tested against memory leaks
Although I won't be using this code, a lot of time went into it, so I hope it shomehow helps and/or inspires someone.
A possible new downloadResult() to go along downloadTestCase(), maybe?
What do you need that for? Inspect HTML that goes to the server? It is possible to do in the network tab, you just need to remove first/last quotes and replace \" with " - then you get plain HTML.
Also there is a method you can override and check all the data before request is sent: https://bryntum.com/docs/gantt/api/Grid/feature/export/PdfExport#function-receiveExportContent