Commit 8b53c91c authored by Lukas Niegsch's avatar Lukas Niegsch

included html2pdf converter/devtools files from standalone version to erp5

parent 566b56cb
...@@ -59,21 +59,23 @@ ...@@ -59,21 +59,23 @@
<item> <item>
<key> <string>center</string> </key> <key> <string>center</string> </key>
<value> <value>
<list/> <list>
<string>action_description</string>
</list>
</value> </value>
</item> </item>
<item> <item>
<key> <string>hidden</string> </key> <key> <string>hidden</string> </key>
<value> <value>
<list/> <list>
<string>gadget_field_action_js_script</string>
</list>
</value> </value>
</item> </item>
<item> <item>
<key> <string>left</string> </key> <key> <string>left</string> </key>
<value> <value>
<list> <list/>
<string>gadget_field_action_js_script</string>
</list>
</value> </value>
</item> </item>
<item> <item>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="LabelField" module="Products.Formulator.StandardFields"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>action_description</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Confirm Export to PDF</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -2,6 +2,10 @@ url_list = [ ...@@ -2,6 +2,10 @@ url_list = [
"gadget_officejs_html2pdf_router.html", "gadget_officejs_html2pdf_router.html",
"gadget_officejs_html2pdf.configuration", "gadget_officejs_html2pdf.configuration",
"action_export_pdf.html", "action_export_pdf.html",
"action_export_pdf.js" "action_export_pdf.js",
"gadget_devtools.html",
"gadget_devtools.js",
"gadget_converter.html",
"gadget_converter.js"
] ]
return url_list return url_list
<!DOCTYPE html>
<html>
<head>
<title>Export Gadget</title>
<script src="rsvp.js"></script>
<script src="jio.js"></script>
<script src="renderjs.js"></script>
<script src="action_export_pdf.js"></script>
</head>
<body>
<div
data-gadget-url="gadget_converter.html"
data-gadget-scope="converter"
data-gadget-sandbox="public">
</div>
</body>
</html>
(function (window, rJS, RSVP) {
"use strict";
rJS(window)
.declareAcquiredMethod("jio_get", "jio_get")
.declareAcquiredMethod("jio_post", "jio_post")
.declareMethod("preRenderDocument", function (parent_options) {
var gadget = this;
return gadget.jio_get(parent_options.jio_key)
.push(function (parent_document) {
return parent_document;
});
})
.declareMethod("handleSubmit", function (content_dict, parent_options) {
var gadget = this;
var document = parent_options.doc;
delete content_dict.dialog_method;
console.log(document);
var return_submit_dict = {
notify: {
message: "",
status: ""
},
redirect: {
command: "display",
options: {}
}
};
// do cloning for now, convert html to pdf later
for (let property in content_dict) {
if (content_dict.hasOwnProperty(property)) {
document[property] = content_dict[property];
}
}
return gadget.jio_post(document)
.push(function (jio_key) {
return_submit_dict.notify.message = "Document Cloned";
return_submit_dict.notify.status = "success";
return_submit_dict.redirect.options = {
jio_key: jio_key,
editable: true
};
return return_submit_dict;
}, function (error) {
if (error instanceof jIO.util.jIOError) {
return_submit_dict.notify.message = "Failure cloning document";
return_submit_dict.notify.status = "error";
return return_submit_dict;
}
throw error;
});
});
}(window, rJS, RSVP));
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
</item> </item>
<item> <item>
<key> <string>content_type</string> </key> <key> <string>content_type</string> </key>
<value> <string>application/javascript</string> </value> <value> <string>text/javascript</string> </value>
</item> </item>
<item> <item>
<key> <string>precondition</string> </key> <key> <string>precondition</string> </key>
......
<!DOCTYPE html>
<html>
<head>
<title>Converter Gadget</title>
<script src="include/nexedi/rsvp.js"></script>
<script src="include/nexedi/jio.js"></script>
<script src="include/nexedi/renderjs.js"></script>
<script src="gadget_converter.js"></script>
</head>
<body>
<div
data-gadget-url="gadget_devtools.html"
data-gadget-scope="devtools"
data-gadget-sandbox="public">
</div>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_converter.html</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
(function(window, rJS) {
"use strict";
function mm2inches (mm)
{
return mm / 25.4
}
/*
Options for printing:
https://chromedevtools.github.io/devtools-protocol/1-3/Page/#method-printToPDF
*/
var optionsForPrintToPdf =
{
landscape: false,
displayHeaderFooter: false,
printBackground: true,
scale: 1,
paperWidth: mm2inches(210 /* millimetre */),
paperHeight: mm2inches(297 /* millimetre */),
marginTop: mm2inches(10 /* millimetre */),
marginBottom: mm2inches(10 /* millimetre */),
marginLeft: mm2inches(10 /* millimetre */),
marginRight: mm2inches(10 /* millimetre */),
pageRanges: "",
headerTemplate: "",
footerTemplate: "",
preferCSSPageSize: false
}
rJS(window)
.declareMethod("convert", function(html) {
var gadget = this;
var devtools;
var targetId;
var sessionId;
var base64data;
return gadget.getDeclaredGadget("devtools")
.push(function (subgadget) {
return subgadget.getDevtools();
})
.push(function (result) {
devtools = result;
return devtools.Target.createTarget({url: ""});
})
.push(function (result) {
targetId = result.targetId;
return devtools.Target.attachToTarget({targetId: targetId, flatten: true});
})
.push(function (result) {
sessionId = result.sessionId;
return devtools.Target.activateTarget({targetId: targetId});
})
.push(function () {
return devtools.Network.enable();
})
.push(function () {
return devtools.Page.enable();
})
.push(function () {
return devtools.Page.getFrameTree();
})
.push(function (result) {
var frameId = result.frameTree.frame.id;
return devtools.Page.setDocumentContent({frameId: frameId, html: html});
})
.push(function () {
return devtools.Page.loadEventFired();
})
.push(function () {
return devtools.Page.printToPDF(optionsForPrintToPdf);
})
.push(function (result) {
base64data = result.data;
return devtools.Target.detachFromTarget({sessionId: sessionId});
})
.push(function () {
return devtools.Target.closeTarget({targetId: targetId});
})
.push(function () {
return base64data;
})
})
}(window, rJS));
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_converter.js</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/javascript</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<!DOCTYPE html>
<html>
<!--
This gadget controls an remote browser via the chrome devtools protocol.
The remote browser must support the protocol, run in headless mode, and
must be available with the following parameters:
BROWSER_URL
BROWSER_USERNAME
BROWSER_PASSWORD
This gadget also requires that Cross-Origin Resource Sharing with the
BROWSER_URL is enabled.
Example (chromium running on localhost):
google-chrome-stable --headless --remote-debugging-port=9222
-> BROWSER_URL="http://localhost:9222"
-> BROWSER_USERNAME="ignored"
-> BROWSER_PASSWORD="ignored"
Example (chromium running on slapos):
slapos request ${SOFTWARE_NAME} ${SOFTWARE_RELEASE_URI}
-> BROWSER_URL="https://softinstXXXXX.host.vifib.net"
-> BROWSER_USERNAME="admin"
-> BROWSER_PASSWORD="password"
Usage:
var devtools = gadget.getDevtools()
.push(function () {
console.log(devtools.Page.description) // "..."
console.log(devtools.Page.enable.description); // "..."
console.log(devtools.Page.enable.experimental); // false
return devtools.Page.enable();
})
.push(function () {
return devtools.Page.navigate({url: "https://www.example.com"});
})
.push(function () {
return devtools.Page.loadEventFired();
})
This devtools object has several subobjects according to the
protocol, e.g. Page. Each subobjects has methods and events. Both of
them can be used like regular functions. Calling a method will trigger
it, and calling an event will wait until the browser triggers it. Some
methods will return some types, which we encode as objects. See the
references on how to use the different methods, events, and types.
References:
https://chromedevtools.github.io/devtools-protocol
--->
<head>
<title>Devtools Gadget</title>
<script src="include/nexedi/rsvp.js"></script>
<script src="include/nexedi/renderjs.js"></script>
<script src="gadget_devtools.js"></script>
</head>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_devtools.html</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
// todo: move outside of this gadget
var BROWSER_URL = "http://localhost:9222";
var BROWSER_USERNAME = "optional";
var BROWSER_PASSWORD = "optional";
(function(window, rJS, RSVP) {
"use strict";
/**
* Makes an HTTP GET request to the browser to retrieve some JSON data.
*
* @param {String} path The path of the json file.
* @returns The promise for the the JSON data as object.
*/
function getBrowserJSON (path) {
var callback = (resolve, reject) => {
var request = new XMLHttpRequest();
var url = (new URL(path, BROWSER_URL)).href;
request.open("GET", url, true, BROWSER_USERNAME, BROWSER_PASSWORD);
request.withCredentials = true;
request.onload = () => {
resolve(JSON.parse(request.responseText));
}
request.onerror = (error) => {
reject("Could not connect to browser!");
}
request.send();
}
return RSVP.Queue().push(function () {
return RSVP.Promise(callback);
});
}
/**
* Returns the devtools API for the given domain. Domains are defined
* by the devtools protocol, e.g. Page. Each domain defines multiple
* methods, events, and types.
*
* @param {object} domain The domain data from the devtools API.
* @returns The promise for the devtools API object and its name.
*/
function getDevtoolsAPI (domain) {
var callback = (resolve) => {
var object = {};
(domain.commands || []).forEach((method) => {
addMethodAPI(object, domain, method);
});
(domain.events || []).forEach((event) => {
addEventAPI(object, domain, event);
});
resolve([domain.domain, object]);
}
return RSVP.Queue().push(function () {
return RSVP.Promise(callback);
});
}
/**
* Adds the method API from the method JSON data to the object.
*
* @param {object} object The container for the method.
* @param {object} domain The domain data from the protocol.
* @param {object} method The method data from the protocol.
*/
function addMethodAPI (object, domain, method) {
var result = (gadget, params) => {
var callback = (resolve, reject) => {
var sessionId = gadget.state.sessionId;
var command = {
sessionId,
id: gadget.state.nextIndex,
method: `${domain.domain}.${method.name}`,
params: params,
}
console.log(command);
var message = JSON.stringify(command);
gadget.state.websocket.send(message);
var handler = {
sessionId,
id: gadget.state.nextIndex,
result: resolve,
error: reject
}
gadget.state.handlers.add(handler);
return gadget.changeState({update: true});
}
return RSVP.Queue().push(function () {
return RSVP.Promise(callback);
});
}
object[method.name] = result;
}
/**
* Adds the event API from the method JSON data to the object.
*
* @param {object} object The container for the method.
* @param {object} domain The domain data from the protocol.
* @param {object} method The method data from the protocol.
*/
function addEventAPI (object, domain, method) {
var result = (gadget, params) => {
var callback = (resolve, reject) => {
var sessionId = gadget.state.sessionId;
var handler = {
sessionId,
method: `${domain.domain}.${method.name}`,
result: resolve,
error: reject
}
gadget.state.handlers.add(handler);
return gadget.changeState({update: true});
}
return RSVP.Queue().push(function () {
return RSVP.Promise(callback);
});
}
object[method.name] = result;
}
/**
* Wraps the devtools API inside the gadget state which handels the
* connection with specific targets.
*
* @param {*} gadget
* @param {*} devtoolsAPI
*/
function updateDevtools (gadget, devtoolsAPI) {
var callback = () => {
for (let [domain, methods] of Object.entries(devtoolsAPI)) {
for (let [method, callback] of Object.entries(methods)) {
devtoolsAPI[domain][method] = (params) => {
return callback(gadget, params);
};
}
}
let attachToTarget = devtoolsAPI.Target.attachToTarget;
devtoolsAPI.Target.attachToTarget = (params) => {
var response;
return attachToTarget(params)
.push(function (result) {
response = result;
return gadget.changeState({sessionId: response.sessionId});
})
.push(function () {
return response;
})
}
let detachFromTarget = devtoolsAPI.Target.detachFromTarget;
devtoolsAPI.Target.detachFromTarget = (params) => {
return gadget.changeState({sessionId: undefined})
.push(function () {
return detachFromTarget(params);
})
}
return gadget.changeState({devtools: devtoolsAPI});
}
return RSVP.Queue().push(callback);
}
/**
* Creates a new websocket that puts messages into the gadget's state.
* It also automatically reconnects on network errors.
*
* @param {object} gadget The gadget that should receive messages.
* @param {string} websocketUrl The url of the websocket.
*/
function updateWebsocket (gadget, websocketUrl) {
var callback = (resolve) => {
var websocket = new WebSocket(websocketUrl);
websocket.onopen = resolve;
websocket.onmessage = (message) => {
gadget.state.messages.add(JSON.parse(message.data));
gadget.changeState({nextIndex: gadget.state.nextIndex + 1});
}
websocket.onerror = (error) => {
websocket.close();
updateWebsocket(gadget, websocketUrl);
}
gadget.changeState({websocket: websocket});
}
return RSVP.Queue().push(function () {
return RSVP.Promise(callback);
});
}
/**
* Find the corresponding handler for the message and resolves it.
*
* @param {object} message The message that contains the result data.
* @param {object} gadget The gadget that contains all the handlers.
*/
function handleMethodMessage(gadget, message) {
for (let it = gadget.state.handlers.values(), handler = null; handler = it.next().value;) {
if (handler.id != message.id) {
continue;
}
gadget.state.handlers.delete(handler);
gadget.state.messages.delete(message);
if ('error' in message) {
handler.error(message.error);
} else {
handler.result(message.result)
}
}
}
/**
* Find the corresponding handler for the event and resolves it.
*
* @param {object} message The message that contains the event data.
* @param {object} gadget The gadget that contains all the handlers.
*/
function handleEventMessage(gadget, message) {
for (let it = gadget.state.handlers.values(), handler = null; handler = it.next().value;) {
setTimeout(function () {
gadget.state.messages.delete(message);
}, 5000);
if (handler.method != message.method) {
continue;
}
gadget.state.handlers.delete(handler);
handler.result(message.params);
}
}
rJS(window)
.setState({
update: false,
websocket: null,
sessionId: undefined,
nextIndex: 0,
handlers: null,
messages: null,
devtools: {},
})
.ready(function () {
var gadget = this;
gadget.state.handlers = new Set();
gadget.state.messages = new Set();
return getBrowserJSON("json/version")
.push(function (result) {
var browserUrl = result.webSocketDebuggerUrl;
return updateWebsocket(gadget, browserUrl);
})
.push(function () {
return getBrowserJSON("json/protocol")
})
.push(function (result) {
var promises = result.domains.map(getDevtoolsAPI);
return RSVP.all(promises);
})
.push(function (result) {
var devtools = devtools = Object.fromEntries(result);
return updateDevtools(gadget, devtools);
})
})
.declareMethod("getDevtools", function () {
var gadget = this;
return RSVP.Queue().push(function () {
return gadget.state.devtools;
})
})
.onStateChange(function () {
var gadget = this;
gadget.state.update = false;
for (let it = gadget.state.messages.values(), message = null; message = it.next().value;) {
if (message.hasOwnProperty("id")) {
handleMethodMessage(gadget, message);
}
if (message.hasOwnProperty("method")) {
handleEventMessage(gadget, message);
}
}
})
}(window, rJS, RSVP));
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_devtools.js</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/javascript</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment