From bcd570db0d967187270a8a15d38b0bd00e8101d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Houz=C3=A9fa=20Abbasbhay?= Date: Mon, 25 Sep 2023 17:54:10 +0200 Subject: [PATCH] [MIG] attachment_preview: Migration to 16.0 Overall implementation remains the same as in previous versions: * patch into attachment list to prepare prev/next buttons in the viewer * patch into attachment cards to observe clicks on new buttons this module adds * fetch attachment extensions * add an iframe into the DOM tree next to the main form one, display viewer inside it * add preview button within binary fields Main changes in this migration: * update imports/exports to proper JS modules - in particular, this fixes "service already defined" console messages we also get in 15.0 * rework FormRenderer injector, previous (legacy) one was no longer used * patch attachment list / cards with new methods in the mail module * fix preview button inclusion within binary fields --- attachment_preview/__manifest__.py | 6 +- attachment_preview/readme/CONTRIBUTORS.rst | 3 + attachment_preview/readme/ROADMAP.rst | 8 + .../src/js/attachmentPreviewWidget.esm.js | 9 +- .../src/js/components/chatter/chatter.esm.js | 338 +++---------- .../attachment_card/attachment_card.esm.js | 467 ++++++++---------- .../static/src/scss/attachment_preview.scss | 33 +- .../static/src/xml/attachment_preview.xml | 33 +- .../odoo/addons/attachment_preview | 1 + setup/attachment_preview/setup.py | 6 + 10 files changed, 334 insertions(+), 570 deletions(-) create mode 100644 attachment_preview/readme/ROADMAP.rst create mode 120000 setup/attachment_preview/odoo/addons/attachment_preview create mode 100644 setup/attachment_preview/setup.py diff --git a/attachment_preview/__manifest__.py b/attachment_preview/__manifest__.py index 5c56817cddb..a0fa6a5768b 100644 --- a/attachment_preview/__manifest__.py +++ b/attachment_preview/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Preview attachments", - "version": "15.0.1.0.0", + "version": "16.0.1.0.0", "author": "Therp BV," "Onestein," "Odoo Community Association (OCA)", "website": "https://github.com/OCA/knowledge", "license": "AGPL-3", @@ -15,15 +15,15 @@ "assets": { "web._assets_primary_variables": [], "web.assets_backend": [ - "attachment_preview/static/src/js/models/attachment_card/attachment_card.esm.js", "attachment_preview/static/src/js/attachmentPreviewWidget.esm.js", + "attachment_preview/static/src/js/models/attachment_card/attachment_card.esm.js", "attachment_preview/static/src/js/components/chatter/chatter.esm.js", "attachment_preview/static/src/scss/attachment_preview.scss", + "attachment_preview/static/src/xml/attachment_preview.xml", ], "web.assets_frontend": [], "web.assets_tests": [], "web.qunit_suite_tests": [], - "web.assets_qweb": ["attachment_preview/static/src/xml/attachment_preview.xml"], }, "installable": True, } diff --git a/attachment_preview/readme/CONTRIBUTORS.rst b/attachment_preview/readme/CONTRIBUTORS.rst index da0e7b99f01..d33de1cd794 100644 --- a/attachment_preview/readme/CONTRIBUTORS.rst +++ b/attachment_preview/readme/CONTRIBUTORS.rst @@ -1,2 +1,5 @@ * Holger Brunn * Dennis Sluijk +* `XCG Consulting `_: + + * Houzéfa Abbasbhay diff --git a/attachment_preview/readme/ROADMAP.rst b/attachment_preview/readme/ROADMAP.rst new file mode 100644 index 00000000000..bf8a169a08c --- /dev/null +++ b/attachment_preview/readme/ROADMAP.rst @@ -0,0 +1,8 @@ +* Remove `FormRenderer` patch, convert `AttachmentPreviewWidget` into a component instead. +* Remove `BinaryField` patch, convert preview button into a component instead. +* Don't use `bus.trigger("open_attachment_preview", ...)` to open viewer from an attachment; there + must be a smoother way. +* Binary fields only have an external preview button. Also add inline preview; stub code is already + there. +* Add tests to ensure preview & open buttons are rendered in attachment cards. +* Add JS tests to ensure preview & open buttons work as expected (display viewer / open url). diff --git a/attachment_preview/static/src/js/attachmentPreviewWidget.esm.js b/attachment_preview/static/src/js/attachmentPreviewWidget.esm.js index 61d5fb63bbf..35c75fe9a59 100644 --- a/attachment_preview/static/src/js/attachmentPreviewWidget.esm.js +++ b/attachment_preview/static/src/js/attachmentPreviewWidget.esm.js @@ -4,7 +4,7 @@ import Widget from "web.Widget"; var active_attachment_index = 0; var is_first_click = true; -var AttachmentPreviewWidget = Widget.extend({ +export const AttachmentPreviewWidget = Widget.extend({ template: "attachment_preview.AttachmentPreviewWidget", activeIndex: 0, @@ -116,9 +116,8 @@ var AttachmentPreviewWidget = Widget.extend({ this.$iframe.attr("src", att.previewUrl); }, - setAttachments: function (attachments, active_attachment_id, first_click) { - is_first_click = first_click; - + setAttachments: function (attachments, active_attachment_id) { + is_first_click = true; if (active_attachment_id) { this.active_attachment_id = active_attachment_id; } @@ -130,5 +129,3 @@ var AttachmentPreviewWidget = Widget.extend({ } }, }); - -export default AttachmentPreviewWidget; diff --git a/attachment_preview/static/src/js/components/chatter/chatter.esm.js b/attachment_preview/static/src/js/components/chatter/chatter.esm.js index 393a6706ce3..74741068bdb 100644 --- a/attachment_preview/static/src/js/components/chatter/chatter.esm.js +++ b/attachment_preview/static/src/js/components/chatter/chatter.esm.js @@ -1,277 +1,89 @@ /** @odoo-module **/ -import AttachmentPreviewWidget from "../../attachmentPreviewWidget"; -import {Chatter} from "@mail/components/chatter/chatter"; -import {patch} from "web.utils"; - -odoo.define("attachment_preview.chatter", function (require) { - const components = {Chatter}; - var rpc = require("web.rpc"); - var basic_fields = require("web.basic_fields"); - var core = require("web.core"); - var _t = core._t; +import {canPreview, getUrl} from "../../models/attachment_card/attachment_card.esm"; +import {BinaryField} from "@web/views/fields/binary/binary_field"; +import {_t} from "@web/core/l10n/translation"; +import {bus} from "web.core"; +import {onMounted} from "@odoo/owl"; +import {patch} from "@web/core/utils/patch"; +import {query} from "web.rpc"; +import {sprintf} from "@web/core/utils/strings"; + +patch(BinaryField.prototype, "attachment_preview.BinaryField", { + setup() { + var res = this._super(...arguments); + onMounted(this._preview_onMounted); + return res; + }, + + _preview_onMounted() { + if (this.props.record.data.id) { + var self = this; + query({ + model: "ir.attachment", + method: "get_attachment_extension", + args: [this.props.record.data.id], + }).then(function (extension) { + if (canPreview(extension)) { + self._renderPreviewButton(extension); + } + }); + } + }, - function getUrl( + showPreview( attachment_id, attachment_url, attachment_extension, - attachment_title + attachment_title, + split_screen ) { - var url = ""; - if (attachment_url) { - if (attachment_url.slice(0, 21) === "/web/static/lib/pdfjs") { - url = (window.location.origin || "") + attachment_url; - } else { - url = - (window.location.origin || "") + - "/attachment_preview/static/lib/ViewerJS/index.html" + - "?type=" + - encodeURIComponent(attachment_extension) + - "&zoom=automatic" + - "&title=" + - encodeURIComponent(attachment_title) + - "#" + - attachment_url.replace(window.location.origin, ""); - } - } else { - url = - (window.location.origin || "") + - "/attachment_preview/static/lib/ViewerJS/index.html" + - "?type=" + - encodeURIComponent(attachment_extension) + - "&title=" + - encodeURIComponent(attachment_title) + - "&zoom=automatic" + - "#" + - "/web/content/" + - attachment_id + - "?model%3Dir.attachment"; - } - - return url; - } - - function canPreview(extension) { - return ( - $.inArray(extension, [ - "odt", - "odp", - "ods", - "fodt", - "pdf", - "ott", - "fodp", - "otp", - "fods", - "ots", - ]) > -1 - ); - } - - patch( - components.Chatter.prototype, - "attachment_preview/static/src/js/components/chatter/chatter.js", - { - /** - * @override - */ - constructor(...args) { - this._super(...args); - - this._showPreview = this._showPreview.bind(this); - this.canPreview = this.canPreview.bind(this); - }, - - /** - * @override - */ - _update() { - // Var res = this._super.apply(this, arguments); - var self = this; - self._getPreviewableAttachments().then(function (atts) { - self.previewableAttachments = atts; - self._updatePreviewButtons(self.previewableAttachments); - if (!self.attachmentPreviewWidget) { - self.attachmentPreviewWidget = new AttachmentPreviewWidget( - self - ); - self.attachmentPreviewWidget.setAttachments(atts); - } - self.previewableAttachments = atts; - // ChatterpreviewableAttachments = atts; - self.attachmentPreviewWidget.setAttachments(atts); - }); - }, - - _getPreviewableAttachments: function () { - var self = this; - var deferred = $.Deferred(); - const chatter = self.messaging.models["mail.chatter"].get( - self.props.chatterLocalId - ); - const thread = chatter ? chatter.thread : undefined; - - var attachments = {}; - if (thread) { - attachments = thread.allAttachments; - } - - attachments = _.object( - attachments.map((attachment) => { - return attachment.id; - }), - attachments.map((attachment) => { - if (attachment.defaultSource) { - return { - url: attachment.defaultSource, - extension: attachment.extension, - title: attachment.name, - }; - } - return { - url: "/web/content?id=" + attachment.id + "&download=true", - extension: attachment.extension, - title: attachment.name, - }; - }) - ); - - rpc.query({ - model: "ir.attachment", - method: "get_attachment_extension", - args: [ - _.map(_.keys(attachments), function (id) { - return parseInt(id, 10); - }), - ], - }).then( - function (extensions) { - var reviewableAttachments = _.map( - _.keys( - _.pick(extensions, function (extension) { - return canPreview(extension); - }) - ), - function (id) { - return { - id: id, - url: attachments[id].url, - extension: extensions[id], - title: attachments[id].title, - previewUrl: getUrl( - id, - attachments[id].url, - extensions[id], - attachments[id].title - ), - }; - } - ); - - deferred.resolve(reviewableAttachments); - }, - - function () { - deferred.reject(); - } - ); - - return deferred.promise(); - }, - - _updatePreviewButtons: function (previewableAttachments) { - $(this) - .find(".o_attachment_preview") - .each(function () { - var $this = $(this); - var att = _.findWhere(previewableAttachments, { - id: $this.attr("data-id"), - }); - if (att) { - $this.attr("data-extension", att.extension); - } else { - $this.remove(); - } - }); - }, + if (!canPreview(attachment_extension)) { + return; } - ); - basic_fields.FieldBinaryFile.include(components.Chatter); - basic_fields.FieldBinaryFile.include({ - showPreview( + var url = getUrl( attachment_id, attachment_url, attachment_extension, - attachment_title, - split_screen - ) { - if (!canPreview(attachment_extension)) { - return; - } - - var url = getUrl( - attachment_id, - attachment_url, - attachment_extension, - attachment_title - ); - - if (split_screen) { - this.trigger("onAttachmentPreview", {url: url}); - } else { - window.open(url); - } - }, - - _renderReadonly: function () { - var self = this; - this._super.apply(this, arguments); - - if (this.recordData.id) { - this._getBinaryExtension().then(function (extension) { - if (canPreview(extension)) { - // Self._renderPreviewButton(extension, recordData); - self._renderPreviewButton(extension); - } - }); - } - }, - - _renderPreviewButton: function (extension) { - this.$previewBtn = $(""); - this.$previewBtn.addClass("fa fa-external-link mr-2"); - this.$previewBtn.attr("href"); - this.$previewBtn.attr( - "title", - _.str.sprintf(_t("Preview %s"), this.field.string) - ); - this.$previewBtn.attr("data-extension", extension); - this.$el.find(".fa-download").after(this.$previewBtn); - this.$previewBtn.on("click", this._onPreview.bind(this)); - }, + attachment_title + ); - _getBinaryExtension: function () { - return rpc.query({ - model: "ir.attachment", - method: "get_binary_extension", - args: [this.model, this.recordData.id, this.name, this.attrs.filename], - }); - }, + if (split_screen) { + bus.trigger("open_attachment_preview", attachment_id); + } else { + window.open(url); + } + }, - _onPreview: function (event) { - this.showPreview( - null, - _.str.sprintf( - "/web/content?model=%s&field=%s&id=%d", - this.model, - this.name, - this.recordData.id - ), - $(event.currentTarget).attr("data-extension"), - _.str.sprintf(_t("Preview %s"), this.field.string), - false - ); - event.stopPropagation(); - }, - }); + _renderPreviewButton(extension) { + // Add a button same as standard fa-download one. + var dl_button = $(this.__owl__.bdom.parentEl).find("button.fa-download"); + if (dl_button.length !== 1) { + return; + } + var preview_button = $(" + diff --git a/setup/attachment_preview/odoo/addons/attachment_preview b/setup/attachment_preview/odoo/addons/attachment_preview new file mode 120000 index 00000000000..491ac8db96d --- /dev/null +++ b/setup/attachment_preview/odoo/addons/attachment_preview @@ -0,0 +1 @@ +../../../../attachment_preview \ No newline at end of file diff --git a/setup/attachment_preview/setup.py b/setup/attachment_preview/setup.py new file mode 100644 index 00000000000..28c57bb6403 --- /dev/null +++ b/setup/attachment_preview/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)