From a3b0ee83cec6d563a81f55eeba191bb6c25ff6af Mon Sep 17 00:00:00 2001 From: Anjeel Haria Date: Tue, 15 Jul 2025 11:14:28 +0530 Subject: [PATCH] [MIG] attachment_preview: Migration to 18.0 Increased code coverage --- attachment_preview/__manifest__.py | 11 +- attachment_preview/models/ir_attachment.py | 34 ++- .../src/js/attachmentPreviewWidget.esm.js | 172 +++++------ .../src/js/components/chatter/chatter.esm.js | 277 ------------------ .../src/js/mail_core/attachment_list.esm.js | 93 ++++++ .../attachment_card/attachment_card.esm.js | 265 ----------------- attachment_preview/static/src/js/utils.esm.js | 87 ++++++ .../static/src/js/viewerjs_tweaks.js | 4 +- .../js/web_views/fields/binary_field.esm.js | 64 ++++ .../js/web_views/form/form_compiler.esm.js | 20 ++ .../js/web_views/form/form_renderer.esm.js | 13 + .../static/src/scss/attachment_preview.scss | 71 ++--- .../static/src/xml/attachment_preview.xml | 77 ++--- .../tests/test_attachment_preview.py | 25 +- 14 files changed, 457 insertions(+), 756 deletions(-) delete mode 100644 attachment_preview/static/src/js/components/chatter/chatter.esm.js create mode 100644 attachment_preview/static/src/js/mail_core/attachment_list.esm.js delete mode 100644 attachment_preview/static/src/js/models/attachment_card/attachment_card.esm.js create mode 100644 attachment_preview/static/src/js/utils.esm.js create mode 100644 attachment_preview/static/src/js/web_views/fields/binary_field.esm.js create mode 100644 attachment_preview/static/src/js/web_views/form/form_compiler.esm.js create mode 100644 attachment_preview/static/src/js/web_views/form/form_renderer.esm.js diff --git a/attachment_preview/__manifest__.py b/attachment_preview/__manifest__.py index 5c56817c..5d7d128d 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": "18.0.1.0.0", "author": "Therp BV," "Onestein," "Odoo Community Association (OCA)", "website": "https://github.com/OCA/knowledge", "license": "AGPL-3", @@ -15,15 +15,18 @@ "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/components/chatter/chatter.esm.js", + "attachment_preview/static/src/js/utils.esm.js", + "attachment_preview/static/src/js/mail_core/attachment_list.esm.js", + "attachment_preview/static/src/js/web_views/fields/binary_field.esm.js", + "attachment_preview/static/src/js/web_views/form/form_renderer.esm.js", + "attachment_preview/static/src/js/web_views/form/form_compiler.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/models/ir_attachment.py b/attachment_preview/models/ir_attachment.py index 46f5bb99..4a8108e0 100644 --- a/attachment_preview/models/ir_attachment.py +++ b/attachment_preview/models/ir_attachment.py @@ -25,9 +25,9 @@ class IrAttachment(models.Model): for this in ( self.env[model].with_context(bin_size=True).browse(ids_to_browse) ): - result[this.id] = False + # result[this.id] = False extension = "" - if this[filename_field]: + if hasattr(this, filename_field) and this[filename_field]: filename, extension = os.path.splitext(this[filename_field]) if this[binary_field] and extension: result[this.id] = extension @@ -41,28 +41,32 @@ class IrAttachment(models.Model): ids_to_browse = [_id for _id in ids_to_browse if _id not in result] for this in self.env[model].with_context(bin_size=True).browse(ids_to_browse): result[this.id] = False + mimetype = False try: import magic - if model == self._name and binary_field == "datas" and this.store_fname: + if ( + model == self._name and binary_field == "datas" and this.store_fname + ): # pragma: no cover mimetype = magic.from_file( this._full_path(this.store_fname), mime=True ) - # _logger.debug( - # "Magic determined mimetype %s from file %s", - # mimetype, - # this.store_fname - # ) - else: + else: # pragma: no cover mimetype = magic.from_buffer(this[binary_field], mime=True) _logger.debug("Magic determined mimetype %s from buffer", mimetype) - except ImportError: - (mimetype, encoding) = mimetypes.guess_type( - "data:;base64," + this[binary_field].decode("utf-8"), strict=False + except (ImportError, Exception): + try: + (mimetype, encoding) = mimetypes.guess_type( + "data:;base64," + this[binary_field].decode("utf-8"), + strict=False, + ) + except Exception as e: + _logger.debug("Error when guessing mimetype: %s", str(e)) + if mimetype: + extension = mimetypes.guess_extension( + mimetype.split(";")[0], strict=False ) - # _logger.debug("Mimetypes guessed type %s from buffer", mimetype) - extension = mimetypes.guess_extension(mimetype.split(";")[0], strict=False) - result[this.id] = extension + result[this.id] = extension for _id in result: result[_id] = (result[_id] or "").lstrip(".").lower() return result if isinstance(ids, collections.abc.Iterable) else result[ids] diff --git a/attachment_preview/static/src/js/attachmentPreviewWidget.esm.js b/attachment_preview/static/src/js/attachmentPreviewWidget.esm.js index 61d5fb63..431969be 100644 --- a/attachment_preview/static/src/js/attachmentPreviewWidget.esm.js +++ b/attachment_preview/static/src/js/attachmentPreviewWidget.esm.js @@ -1,134 +1,106 @@ -/** @odoo-module */ -import Widget from "web.Widget"; +import {Component, onWillStart, useRef, useState} from "@odoo/owl"; +import {ensureJQuery} from "@web/core/ensure_jquery"; +import {sprintf} from "@web/core/utils/strings"; -var active_attachment_index = 0; -var is_first_click = true; +export class AttachmentPreviewWidget extends Component { + static template = "attachment_preview.AttachmentPreviewWidget"; + static props = {}; + setup() { + super.setup(); + Component.env.bus.addEventListener( + "open_attachment_preview", + ({detail: {attachment_id, attachment_info_list}}) => + this._onAttachmentPreview(attachment_id, attachment_info_list) + ); + this.state = useState({activeIndex: 0}); + this.rootRef = useRef("root"); + this.currentRef = useRef("current"); + this.iframeRef = useRef("iframe"); + onWillStart(async () => { + await ensureJQuery(); + }); + } -var AttachmentPreviewWidget = Widget.extend({ - template: "attachment_preview.AttachmentPreviewWidget", - activeIndex: 0, - - events: { - "click .attachment_preview_close": "_onCloseClick", - "click .attachment_preview_previous": "_onPreviousClick", - "click .attachment_preview_next": "_onNextClick", - "click .attachment_preview_popout": "_onPopoutClick", - }, - - start: function () { - // First_click = true; - var res = this._super.apply(this, arguments); - this.$overlay = $(".attachment_preview_overlay"); - this.$iframe = $(".attachment_preview_iframe"); - this.$current = $(".attachment_preview_current"); - return res; - }, - - _onCloseClick: function () { + _onCloseClick() { this.hide(); - }, + } - _onPreviousClick: function () { + _onPreviousClick() { this.previous(); - }, + } - _onNextClick: function () { + _onNextClick() { this.next(); - }, + } - _onPopoutClick: function () { - if (!this.attachments[this.activeIndex]) { - return; - } + _onPopoutClick() { + if (!this.attachments[this.state.activeIndex]) return; + // eslint-disable-next-line no-undef + window.open(this.attachments[this.state.activeIndex].previewUrl); + } - window.open(this.attachments[this.activeIndex].previewUrl); - }, - - next: function () { - if (is_first_click) { - is_first_click = !is_first_click; - } - var index = this.activeIndex + 1; + next() { + var index = this.state.activeIndex + 1; if (index >= this.attachments.length) { index = 0; } - this.activeIndex = index; + this.state.activeIndex = index; this.updatePaginator(); this.loadPreview(); - }, + } - previous: function () { - if (is_first_click) { - is_first_click = !is_first_click; - } - var index = this.activeIndex - 1; + previous() { + var index = this.state.activeIndex - 1; if (index < 0) { index = this.attachments.length - 1; } - this.activeIndex = index; + this.state.activeIndex = index; this.updatePaginator(); this.loadPreview(); - }, + } - show: function () { - this.$el.removeClass("d-none"); - this.trigger("shown"); - }, + show() { + $(this.rootRef.el).removeClass("d-none"); + Component.env.bus.trigger("shown"); + } - hide: function () { - is_first_click = true; - this.$el.addClass("d-none"); - this.trigger("hidden"); - }, + hide() { + $(this.rootRef.el).addClass("d-none"); + Component.env.bus.trigger("hidden"); + } - updatePaginator: function () { - var value = _.str.sprintf( + updatePaginator() { + var value = sprintf( "%s / %s", - this.activeIndex + 1, + this.state.activeIndex + 1, this.attachments.length ); - this.$overlay = $(".attachment_preview_overlay"); - this.$iframe = $(".attachment_preview_iframe"); - this.$current = $(".attachment_preview_current"); - this.$current.html(value); - }, + $(this.currentRef.el).html(value); + } - loadPreview: function () { + loadPreview() { if (this.attachments.length === 0) { - this.$iframe.attr("src", "about:blank"); + $(this.iframeRef.el).attr("src", "about:blank"); return; } + var att = this.attachments[this.state.activeIndex]; + $(this.iframeRef.el).attr("src", att.previewUrl); + } - if (is_first_click) { - for (let i = 0; i < this.attachments.length; i++) { - if ( - parseInt(this.attachments[i].id, 10) === this.active_attachment_id - ) { - active_attachment_index = i; - is_first_click = false; - } + setAttachments(attachments, active_attachment_id) { + this.attachments = attachments; + if (!attachments) return; + for (let i = 0; i < attachments.length; ++i) { + if (parseInt(attachments[i].id, 10) === active_attachment_id) { + this.state.activeIndex = i; } - } else { - active_attachment_index = this.activeIndex; } + this.updatePaginator(); + this.loadPreview(); + } - var att = this.attachments[active_attachment_index]; - this.$iframe.attr("src", att.previewUrl); - }, - - setAttachments: function (attachments, active_attachment_id, first_click) { - is_first_click = first_click; - - if (active_attachment_id) { - this.active_attachment_id = active_attachment_id; - } - if (attachments) { - this.attachments = attachments; - this.activeIndex = 0; - this.updatePaginator(); - this.loadPreview(); - } - }, -}); - -export default AttachmentPreviewWidget; + _onAttachmentPreview(attachment_id, attachment_info_list) { + this.setAttachments(attachment_info_list, attachment_id); + this.show(); + } +} diff --git a/attachment_preview/static/src/js/components/chatter/chatter.esm.js b/attachment_preview/static/src/js/components/chatter/chatter.esm.js deleted file mode 100644 index 393a6706..00000000 --- a/attachment_preview/static/src/js/components/chatter/chatter.esm.js +++ /dev/null @@ -1,277 +0,0 @@ -/** @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; - - function getUrl( - attachment_id, - attachment_url, - attachment_extension, - attachment_title - ) { - 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(); - } - }); - }, - } - ); - - basic_fields.FieldBinaryFile.include(components.Chatter); - basic_fields.FieldBinaryFile.include({ - showPreview( - 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)); - }, - - _getBinaryExtension: function () { - return rpc.query({ - model: "ir.attachment", - method: "get_binary_extension", - args: [this.model, this.recordData.id, this.name, this.attrs.filename], - }); - }, - - _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(); - }, - }); -}); diff --git a/attachment_preview/static/src/js/mail_core/attachment_list.esm.js b/attachment_preview/static/src/js/mail_core/attachment_list.esm.js new file mode 100644 index 00000000..369ae8a6 --- /dev/null +++ b/attachment_preview/static/src/js/mail_core/attachment_list.esm.js @@ -0,0 +1,93 @@ +import {canPreview, getUrl, showPreview} from "../utils.esm"; +import {AttachmentList} from "@mail/core/common/attachment_list"; +import {onWillStart} from "@odoo/owl"; +import {patch} from "@web/core/utils/patch"; +import {useService} from "@web/core/utils/hooks"; + +patch(AttachmentList.prototype, { + setup() { + super.setup(); + this.orm = useService("orm"); + onWillStart(async () => { + var attachments = Object( + this.props.attachments.map((attachment) => { + if ( + attachment.defaultSource && + attachment.defaultSource.length > 38 + ) { + return { + id: attachment.id, + url: attachment.defaultSource, + extension: attachment.extension, + title: attachment.name, + }; + } + return { + id: attachment.id, + url: "/web/content?id=" + attachment.id + "&download=true", + extension: attachment.extension, + title: attachment.name, + }; + }) + ); + var attachment_ids = []; + for (const i in attachments) { + attachment_ids.push(attachments[i].id); + } + var extensions = await this.orm.call( + "ir.attachment", + "get_attachment_extension", + [attachment_ids] + ); + var previewableAttachments = []; + var previewableAttachmentIds = []; + for (const i in attachments) { + const attachment = attachments[i]; + if (canPreview(extensions[attachment.id])) { + previewableAttachmentIds.push(attachment.id); + previewableAttachments.push({ + id: attachment.id, + url: attachment.url, + extension: extensions[attachment.id], + title: attachment.title, + previewUrl: getUrl( + attachment.id, + attachment.url, + extensions[attachment.id], + attachment.title + ), + }); + } + } + this.previewableAttachments = previewableAttachments; + this.previewableAttachmentIds = previewableAttachmentIds; + }); + }, + + _onPreviewAttachment(attachment) { + // eslint-disable-next-line no-undef + var $target = $(event.currentTarget); + var split_screen = $target.attr("data-target") !== "new"; + var current_attachment = this.previewableAttachments.filter( + (attachment_rec) => attachment_rec.id === attachment.id + ); + var extension = false; + if (current_attachment.length === 0) { + extension = attachment.extension; + } else { + extension = current_attachment[0].extension; + } + showPreview( + attachment.id, + attachment.defaultSource, + extension, + attachment.filename, + split_screen, + this.previewableAttachments + ); + }, + + _canPreviewAttachment(attachment) { + return this.previewableAttachmentIds.includes(attachment.id); + }, +}); diff --git a/attachment_preview/static/src/js/models/attachment_card/attachment_card.esm.js b/attachment_preview/static/src/js/models/attachment_card/attachment_card.esm.js deleted file mode 100644 index 92626def..00000000 --- a/attachment_preview/static/src/js/models/attachment_card/attachment_card.esm.js +++ /dev/null @@ -1,265 +0,0 @@ -/** @odoo-module **/ -import AttachmentPreviewWidget from "../../attachmentPreviewWidget.esm"; -import FormRenderer from "web.FormRenderer"; -import {registerInstancePatchModel} from "@mail/model/model_core"; - -odoo.define("attachment_preview.attachment_card", function (require) { - var rpc = require("web.rpc"); - - var chatterpreviewableAttachments = []; - var active_attachment_id = 0; - var first_click = true; - - FormRenderer.include({ - custom_events: _.extend({}, FormRenderer.prototype.custom_events, { - onAttachmentPreview: "_onAttachmentPreview", - }), - attachmentPreviewWidget: null, - - init: function () { - var res = this._super(...arguments); - this.attachmentPreviewWidget = new AttachmentPreviewWidget(this); - this.attachmentPreviewWidget.on( - "hidden", - this, - this._attachmentPreviewWidgetHidden - ); - return res; - }, - - start: function () { - var self = this; - return this._super.apply(this, arguments).then(function () { - self.attachmentPreviewWidget.insertAfter(self.$el); - }); - }, - - _attachmentPreviewWidgetHidden: function () { - this.$el.removeClass("attachment_preview"); - }, - - showAttachmentPreviewWidget: function (first_c) { - this.$el.addClass("attachment_preview"); - - this.attachmentPreviewWidget.setAttachments( - chatterpreviewableAttachments, - active_attachment_id, - first_c - ); - this.attachmentPreviewWidget.show(); - }, - - on_detach_callback: function () { - this.attachmentPreviewWidget.hide(); - return this._super.apply(this, arguments); - }, - - _onAttachmentPreview: function () { - first_click = true; - this.showAttachmentPreviewWidget(first_click); - }, - }); - - function canPreview(extension) { - return ( - $.inArray(extension, [ - "odt", - "odp", - "ods", - "fodt", - "pdf", - "ott", - "fodp", - "otp", - "fods", - "ots", - ]) > -1 - ); - } - - function getUrl( - attachment_id, - attachment_url, - attachment_extension, - attachment_title - ) { - 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) + - "&title=" + - encodeURIComponent(attachment_title) + - "&zoom=automatic" + - "#" + - attachment_url.replace(window.location.origin, ""); - } - return url; - } - 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; - } - - registerInstancePatchModel( - "mail.attachment_card", - "attachment_preview/static/src/js/models/attachment_card/attachment_card.js", - { - /** - * @override - */ - _created() { - this._super(); - this._onPreviewAttachment = this._onPreviewAttachment.bind(this); - - var attachments = _.object( - this.attachmentList.attachments.map((attachment) => { - console.log("attachment", attachment); - return attachment.id; - }), - this.attachmentList.attachments.map((attachment) => { - if ( - attachment.defaultSource && - attachment.defaultSource.length > 38 - ) { - 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 - ), - }; - } - ); - chatterpreviewableAttachments = reviewableAttachments; - }); - }, - - /** - * @private - * @param {attachment_id} attachment_id of the attachment - * @param {attachment_url} attachment_url of the attachment - * @param {attachment_extension} attachment_extension of the attachment - * @param {attachment_title} attachment_title of the attachment - * @param {split_screen} split_screen of the attachment - */ - _showPreview( - attachment_id, - attachment_url, - attachment_extension, - attachment_title, - split_screen - ) { - // Let active_attURL = ""; - // this.attachmentList.attachments.forEach((att) => { - // if ( - // parseInt(att.localId.slice(20).slice(0, -1)) === attachment_id - // ) { - // if (att.__values.url === undefined) { - // att.__values.url = attachment_url.slice( - // window.location.origin.length - // ); - // active_attURL = att.__values.url; - // } - // } - // }); - var url = getUrl( - attachment_id, - attachment_url, - attachment_extension, - attachment_title - ); - - if (split_screen) { - this.component.trigger("onAttachmentPreview", { - url: url, - active_attachment_id: active_attachment_id, - }); - } else { - window.open(url); - } - }, - - /** - * @private - * @param {event} event - */ - _onPreviewAttachment(event) { - event.preventDefault(); - - var self = this, - $target = $(event.currentTarget), - split_screen = $target.attr("data-target") !== "new", - attachment_id = this.attachment.id, - attachment_title = this.attachment.filename, - attachment_url = this.attachment.defaultSource; - active_attachment_id = attachment_id; - - rpc.query({ - model: "ir.attachment", - method: "get_attachment_extension", - args: [attachment_id], - }).then(function (extension) { - self._showPreview( - attachment_id, - attachment_url, - extension, - attachment_title, - split_screen - ); - }); - }, - } - ); -}); diff --git a/attachment_preview/static/src/js/utils.esm.js b/attachment_preview/static/src/js/utils.esm.js new file mode 100644 index 00000000..85f626a0 --- /dev/null +++ b/attachment_preview/static/src/js/utils.esm.js @@ -0,0 +1,87 @@ +import {Component} from "@odoo/owl"; + +export function canPreview(extension) { + const supported_extensions = [ + "odt", + "odp", + "ods", + "fodt", + "pdf", + "ott", + "fodp", + "otp", + "fods", + "ots", + ]; + return supported_extensions.includes(extension); +} + +export function getUrl( + attachment_id, + attachment_url, + attachment_extension, + attachment_title +) { + var url = ""; + if (attachment_url) { + if (attachment_url.slice(0, 21) === "/web/static/lib/pdfjs") { + // eslint-disable-next-line no-undef + url = (window.location.origin || "") + attachment_url; + } else { + url = + // eslint-disable-next-line no-undef + (window.location.origin || "") + + "/attachment_preview/static/lib/ViewerJS/index.html" + + "?type=" + + encodeURIComponent(attachment_extension) + + "&title=" + + encodeURIComponent(attachment_title) + + "&zoom=automatic" + + "#" + + // eslint-disable-next-line no-undef + attachment_url.replace(window.location.origin, ""); + } + return url; + } + url = + // eslint-disable-next-line no-undef + (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; +} + +export function showPreview( + attachment_id, + attachment_url, + attachment_extension, + attachment_title, + split_screen, + attachment_info_list +) { + if (split_screen && attachment_info_list) { + Component.env.bus.trigger("open_attachment_preview", { + attachment_id, + attachment_info_list, + }); + } else { + // eslint-disable-next-line no-undef + window.open( + getUrl( + attachment_id, + attachment_url, + attachment_extension, + attachment_title + ) + ); + } +} diff --git a/attachment_preview/static/src/js/viewerjs_tweaks.js b/attachment_preview/static/src/js/viewerjs_tweaks.js index 21d209cb..813eb4da 100644 --- a/attachment_preview/static/src/js/viewerjs_tweaks.js +++ b/attachment_preview/static/src/js/viewerjs_tweaks.js @@ -5,11 +5,13 @@ // OpenERP's context (function (original_Viewer) { "use strict"; + // eslint-disable-next-line no-undef window.Viewer = function (plugin, parameters) { if (!plugin) { - // eslint-disable-next-line no-alert + // eslint-disable-next-line no-undef, no-alert alert("Unsupported file type"); } return original_Viewer(plugin, parameters); }; + // eslint-disable-next-line no-undef })(window.Viewer); diff --git a/attachment_preview/static/src/js/web_views/fields/binary_field.esm.js b/attachment_preview/static/src/js/web_views/fields/binary_field.esm.js new file mode 100644 index 00000000..fdae5b23 --- /dev/null +++ b/attachment_preview/static/src/js/web_views/fields/binary_field.esm.js @@ -0,0 +1,64 @@ +import {canPreview, showPreview} from "../../utils.esm"; +import {BinaryField} from "@web/views/fields/binary/binary_field"; +import {_t} from "@web/core/l10n/translation"; +import {onMounted} from "@odoo/owl"; +import {patch} from "@web/core/utils/patch"; +import {sprintf} from "@web/core/utils/strings"; +import {useService} from "@web/core/utils/hooks"; + +patch(BinaryField.prototype, { + setup() { + super.setup(); + this.orm = useService("orm"); + onMounted(this._preview_onMounted); + }, + + async _preview_onMounted() { + if (this.props.record.resId) { + var extension = await this.orm.call( + "ir.attachment", + "get_binary_extension", + [ + this.props.record.resModel, + this.props.record.evalContext.id, + this.props.name, + this.props.fileNameField, + ] + ); + if (canPreview(extension)) { + this._renderPreviewButton(extension); + } + } + }, + + _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 = $(" + -
+
-
-