summaryrefslogtreecommitdiff
path: root/addons/web/static
diff options
context:
space:
mode:
authorSimon Genin (ges) <ges@odoo.com>2020-07-30 20:21:03 +0000
committerLucas Perais (lpe) <lpe@odoo.com>2020-09-01 08:58:40 +0000
commitc121a246f16899735306266a3a12b526e08e7620 (patch)
tree489c09e57ae1c46a28c3c51d7b97f08c29917479 /addons/web/static
parente43c35d3c95c58d6d6af424d4eebf8244c83f332 (diff)
[IMP] base: Document layout preview improvements
The document layout preview is a complete defferent simplified template with its own css that replicates at best the different styles. It does not have the external layout features and lack of fidelity. The new preview actually use the real documents templates and put the result in an iframe. It now has a high fidelity, though not perfect. The goal is for a better onboarding, where clients see easely how documents will look if they had an app to generate them. Of course, the data on the document is a false invoice. Refactor all this from base to web. Task ID 2304177 closes odoo/odoo#56415 Signed-off-by: Lucas Perais (lpe) <lpe@odoo.com>
Diffstat (limited to 'addons/web/static')
-rw-r--r--addons/web/static/src/js/fields/field_registry.js3
-rw-r--r--addons/web/static/src/js/fields/special_fields.js64
-rw-r--r--addons/web/static/src/scss/base_document_layout.scss109
-rw-r--r--addons/web/static/src/scss/special_fields.scss55
-rw-r--r--addons/web/static/src/xml/base.xml8
-rw-r--r--addons/web/static/tests/fields/special_fields_tests.js52
6 files changed, 181 insertions, 110 deletions
diff --git a/addons/web/static/src/js/fields/field_registry.js b/addons/web/static/src/js/fields/field_registry.js
index ef174702579..a5c8a85da0a 100644
--- a/addons/web/static/src/js/fields/field_registry.js
+++ b/addons/web/static/src/js/fields/field_registry.js
@@ -94,5 +94,6 @@ registry
// Special fields
registry
.add('timezone_mismatch', special_fields.FieldTimezoneMismatch)
- .add('report_layout', special_fields.FieldReportLayout);
+ .add('report_layout', special_fields.FieldReportLayout)
+ .add('iframe_wrapper', special_fields.IframeWrapper)
});
diff --git a/addons/web/static/src/js/fields/special_fields.js b/addons/web/static/src/js/fields/special_fields.js
index ab040daa4f0..52ef6d513f7 100644
--- a/addons/web/static/src/js/fields/special_fields.js
+++ b/addons/web/static/src/js/fields/special_fields.js
@@ -4,9 +4,11 @@ odoo.define('web.special_fields', function (require) {
var core = require('web.core');
var field_utils = require('web.field_utils');
var relational_fields = require('web.relational_fields');
+var AbstractField = require('web.AbstractField');
var FieldSelection = relational_fields.FieldSelection;
var _t = core._t;
+var _lt = core._lt;
/**
@@ -62,7 +64,7 @@ var FieldTimezoneMismatch = FieldSelection.extend({
},
/**
* Display the timezone alert
- *
+ *
* Note: timezone alert is a span that is added after $el, and $el is now a
* set of two elements
*
@@ -192,9 +194,69 @@ var FieldReportLayout = relational_fields.FieldMany2One.extend({
});
+const IframeWrapper = AbstractField.extend({
+ description: _lt("Wrap raw html within an iframe"),
+
+ // If HTML, don't forget to adjust the sanitize options to avoid stripping most of the metadata
+ supportedFieldTypes: ['text', 'html'],
+
+ template: "web.IframeWrapper",
+
+ _render() {
+
+ const spinner = this.el.querySelector('.o_iframe_wrapper_spinner');
+ const iframe = this.el.querySelector('.o_preview_iframe');
+
+ iframe.style.display = 'none';
+ spinner.style.display = 'block';
+
+ // Promise for tests
+ let resolver;
+ $(iframe).data('ready', new Promise((resolve) => {
+ resolver = resolve;
+ }));
+
+ /**
+ * Certain browser don't trigger onload events of iframe for particular cases.
+ * In our case, chrome and safari could be problematic depending on version and environment.
+ * This rather unorthodox solution replace the onload event handler. (jquery on('load') doesn't fix it)
+ */
+ const onloadReplacement = setInterval(() => {
+ const iframeDoc = iframe.contentDocument;
+ if (iframeDoc && (iframeDoc.readyState === 'complete' || iframeDoc.readyState === 'interactive')) {
+
+ /**
+ * The document.write is not recommended. It is better to manipulate the DOM through $.appendChild and
+ * others. In our case though, we deal with an iframe without src attribute and with metadata to put in
+ * head tag. If we use the usual dom methods, the iframe is automatically created with its document
+ * component containing html > head & body. Therefore, if we want to make it work that way, we would
+ * need to receive each piece at a time to append it to this document (with this.record.data and extra
+ * model fields or with an rpc). It also cause other difficulties getting attribute on the most parent
+ * nodes, parsing to HTML complex elements, etc.
+ * Therefore, document.write makes it much more trivial in our situation.
+ */
+ iframeDoc.open();
+ iframeDoc.write(this.value);
+ iframeDoc.close();
+
+ iframe.style.display = 'block';
+ spinner.style.display = 'none';
+
+ resolver();
+
+ clearInterval(onloadReplacement);
+ }
+ }, 100);
+
+ }
+
+});
+
+
return {
FieldTimezoneMismatch: FieldTimezoneMismatch,
FieldReportLayout: FieldReportLayout,
+ IframeWrapper,
};
});
diff --git a/addons/web/static/src/scss/base_document_layout.scss b/addons/web/static/src/scss/base_document_layout.scss
index bf75cb71158..4203fe2bb73 100644
--- a/addons/web/static/src/scss/base_document_layout.scss
+++ b/addons/web/static/src/scss/base_document_layout.scss
@@ -9,6 +9,7 @@
max-height: 100px;
}
.o_document_layout_colors {
+ margin-bottom: 32px;
vertical-align: middle;
display: flex;
flex-direction: row;
@@ -33,114 +34,6 @@
}
}
- .o_document_layout_preview {
- flex: 4;
- border: 1px solid;
- border-color: gray;
- margin-right: 12px;
- padding: 12px;
- display: flex;
- min-height: 420px;
- flex-direction: column;
- }
-}
-
-.o_document_layout_preview_logo img {
- padding-left: 5px;
- max-height: 45px;
-}
-
-.o_document_layout_preview_html {
- flex: 1;
- .o_readonly {
- height: 100%;
- }
- .o_preview_layout {
- display: flex;
- flex-direction: column;
- height: 100%;
- }
- .header {
- padding: 0 10px;
- border-top: 1px solid;
- h2 {
- margin: 40px 0;
- }
- .o_layout_information {
- margin-bottom: 20px;
- }
- }
- .article {
- flex: 1;
- padding: 0 10px;
- table th, table td {
- text-align: right;
- &:first-child {
- text-align: left;
- }
- }
- }
- .footer {
- border-top: 1px solid;
- }
- .o_preview_background_layout {
- background-image: url(/base/static/img/bg_background_template.jpg);
- background-size: cover;
- background-position: bottom center;
- background-repeat: no-repeat;
- .table tbody tr:nth-child(odd) {
- background-color: rgba(220, 205, 216, 0.2);
- }
- .row table tbody tr:nth-child(odd) {
- background-color: transparent !important;
- }
- .footer {
- border-bottom: 1px solid;
- }
- }
- .o_preview_clean_layout {
- .header {
- border: none;
- }
- .article table {
- border-bottom: 1px solid;
- border-top: 1px solid;
- margin: 0;
- }
- thead th {
- border-top: 1px solid;
- text-transform: uppercase;
- }
- }
- .o_preview_standard_layout {
- .o_subtotal, .o_total {
- td {
- border-top: 1px solid;
- }
- }
- }
- .o_preview_boxed_layout {
- table {
- border: 1px solid gray('700');
- thead th {
- border: 1px solid gray('700');
- text-transform: uppercase;
- }
- tbody tr td {
- border-top: none;
- border-right: 1px solid gray('700');
- }
- }
- .o_subtotal td, .o_total td {
- border-right: none;
- border-top: 1px solid gray('700');
- }
- .o_total td {
- strong, span {
- color: white;
- }
- }
- }
}
@media (max-width: 620px) {
diff --git a/addons/web/static/src/scss/special_fields.scss b/addons/web/static/src/scss/special_fields.scss
new file mode 100644
index 00000000000..cabdf919c09
--- /dev/null
+++ b/addons/web/static/src/scss/special_fields.scss
@@ -0,0 +1,55 @@
+/*
+ IFRAME WRAPPER SCSS
+ */
+
+$preview_height: 1123 + 32;
+$preview_width: 794;
+$preview_scale: 0.50;
+
+@mixin o_preview_iframe_styling($scale) {
+
+ .o_preview_iframe_wrapper {
+ padding: 0;
+ overflow: hidden;
+ width: ($preview_width * $scale) + 0px;
+ height: ($preview_height * $scale) + 0px;
+ position: relative;
+ }
+
+ .o_preview_iframe {
+ width: $preview_width + 0px;
+ height: $preview_height + 0px;
+ border: 2px solid lightgrey;
+ overflow: hidden;
+
+ padding-top: 16px;
+ padding-bottom: 16px;
+
+ -ms-zoom: 0.5;
+ -moz-transform: scale($scale);
+ -moz-transform-origin: 0 0;
+ -o-transform: scale($scale);
+ -o-transform-origin: 0 0;
+ -webkit-transform: scale($scale);
+ -webkit-transform-origin: 0 0;
+ }
+
+ .o_iframe_wrapper_spinner {
+ position: absolute;
+ left: (($preview_width * $scale) / 2) - 40 + 0px;
+ top: (($preview_height * $scale) / 2) - 40 + 0px;
+ // -40 for width/2 and height/2 of the spinner
+ }
+
+}
+
+
+@include o_preview_iframe_styling($preview_scale)
+
+@media (max-width: 1488px) {
+ @include o_preview_iframe_styling($preview_scale * 0.80)
+}
+
+@media (max-width: 600px) {
+ @include o_preview_iframe_styling($preview_scale * 0.60)
+}
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index 27cc0fdc4f9..374b7715a33 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -1852,4 +1852,12 @@
</button>
</t>
+<t t-name="web.IframeWrapper">
+ <div class="o_preview_iframe_wrapper">
+ <i class="o_iframe_wrapper_spinner fa fa-spinner fa-spin fa-4x" style="color: #875a7b; z-index: 2"/>
+ <!--suppress CheckTagEmptyBody -->
+ <iframe src="about:blank" class="o_preview_iframe" style="display:none"></iframe>
+ </div>
+</t>
+
</templates>
diff --git a/addons/web/static/tests/fields/special_fields_tests.js b/addons/web/static/tests/fields/special_fields_tests.js
index bfde65abf12..6f0ea650d43 100644
--- a/addons/web/static/tests/fields/special_fields_tests.js
+++ b/addons/web/static/tests/fields/special_fields_tests.js
@@ -308,6 +308,58 @@ QUnit.module('special_fields', {
form.destroy();
});
+
+ QUnit.module('IframeWrapper');
+
+ QUnit.test('iframe_wrapper widget in form view', async function (assert) {
+
+ assert.expect(2);
+
+ this.data = {
+ report: {
+ fields: {
+ report_content: {string: "Content of report", type: "html"}
+ },
+ records: [{
+ id: 1,
+ report_content:
+ `<html>
+ <head>
+ <style>
+ body { color : rgb(255, 0, 0); }
+ </style>
+ <head>
+ <body>
+ <div class="nice_div"><p>Some content</p></div>
+ </body>
+ </html>`
+ }]
+ }
+ };
+
+ const form = await createView({
+ View: FormView,
+ model: 'report',
+ data: this.data,
+ arch: `<form><field name="report_content" widget="iframe_wrapper"/></form>`,
+ res_id: 1,
+ });
+
+ const $iframe = form.$('iframe');
+ await $iframe.data('ready');
+ const doc = $iframe.contents()[0];
+
+ assert.strictEqual($(doc).find('.nice_div').html(), '<p>Some content</p>',
+ "should have rendered a div with correct content");
+
+ assert.strictEqual($(doc).find('.nice_div p').css('color'), 'rgb(255, 0, 0)',
+ "head tag style should have been applied");
+
+ form.destroy();
+
+ });
+
+
});
});
});