From 687e23cd828932a58212f700679684c7fa0b84cb Mon Sep 17 00:00:00 2001 From: vancouver29 Date: Mon, 22 Aug 2022 12:59:34 +0200 Subject: [PATCH] [MIG] attachment_preview: Migration to 14.0 --- attachment_preview/__manifest__.py | 3 +- attachment_preview/models/ir_attachment.py | 24 +- .../static/src/js/attachment_preview.js | 651 +++++++++++------- .../static/src/js/viewerjs_tweaks.js | 1 + .../static/src/scss/attachment_preview.scss | 7 + .../static/src/xml/attachment_preview.xml | 67 ++ attachment_preview/templates/assets.xml | 20 + .../tests/test_attachment_preview.py | 12 +- 8 files changed, 492 insertions(+), 293 deletions(-) diff --git a/attachment_preview/__manifest__.py b/attachment_preview/__manifest__.py index 96a2d86f..fd30925e 100644 --- a/attachment_preview/__manifest__.py +++ b/attachment_preview/__manifest__.py @@ -3,10 +3,11 @@ { "name": "Preview attachments", - "version": "12.0.1.0.2", + "version": "14.0.1.0.0", "author": "Therp BV," "Onestein," "Odoo Community Association (OCA)", "license": "AGPL-3", "summary": "Preview attachments supported by Viewer.js", + "website": "https://github.com/OCA/knowledge", "category": "Knowledge Management", "depends": ["web", "mail"], "data": ["templates/assets.xml"], diff --git a/attachment_preview/models/ir_attachment.py b/attachment_preview/models/ir_attachment.py index a24cb4cc..f109d881 100644 --- a/attachment_preview/models/ir_attachment.py +++ b/attachment_preview/models/ir_attachment.py @@ -25,9 +25,7 @@ class IrAttachment(models.Model): for this in ( self.env[model].with_context(bin_size=True).browse(ids_to_browse) ): - if not this.id: - result[this.id] = False - continue + result[this.id] = False extension = "" if this[filename_field]: filename, extension = os.path.splitext(this[filename_field]) @@ -42,9 +40,7 @@ class IrAttachment(models.Model): # to get the extension from the content 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): - if not this[binary_field]: - result[this.id] = False - continue + result[this.id] = False try: import magic @@ -52,17 +48,17 @@ class IrAttachment(models.Model): 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, - ) + # _logger.debug( + # "Magic determined mimetype %s from file %s", + # mimetype, + # this.store_fname, + # ) else: mimetype = magic.from_buffer(this[binary_field], mime=True) - _logger.debug("Magic determined mimetype %s from buffer", mimetype) + # _logger.debug("Magic determined mimetype %s from buffer", mimetype) except ImportError: (mimetype, encoding) = mimetypes.guess_type( - "data:;base64," + this[binary_field], strict=False + "data:;base64," + this[binary_field].decode("utf-8"), strict=False ) _logger.debug("Mimetypes guessed type %s from buffer", mimetype) extension = mimetypes.guess_extension(mimetype.split(";")[0], strict=False) @@ -73,4 +69,4 @@ class IrAttachment(models.Model): @api.model def get_attachment_extension(self, ids): - return self.get_binary_extension(self._name, ids, "datas", "datas_fname") + return self.get_binary_extension(self._name, ids, "datas", "name") diff --git a/attachment_preview/static/src/js/attachment_preview.js b/attachment_preview/static/src/js/attachment_preview.js index 80340a6a..fa0284c7 100644 --- a/attachment_preview/static/src/js/attachment_preview.js +++ b/attachment_preview/static/src/js/attachment_preview.js @@ -1,280 +1,157 @@ -/* Copyright 2014 Therp BV () - * License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */ - -odoo.define("attachment_preview", function (require) { +odoo.define("/attachment_preview/static/src/js/attachment_preview.js", function ( + require +) { "use strict"; - - var core = require("web.core"); - var _t = core._t; - var qweb = core.qweb; - var Chatter = require("mail.Chatter"); + const components = { + Attachment: require("mail/static/src/components/attachment/attachment.js"), + Chatter: require("mail/static/src/components/chatter/chatter.js"), + ChatterContainer: require("mail/static/src/components/chatter_container/chatter_container.js"), + }; + const {patch} = require("web.utils"); + var rpc = require("web.rpc"); var basic_fields = require("web.basic_fields"); var FormRenderer = require("web.FormRenderer"); - var FormController = require("web.FormController"); var Widget = require("web.Widget"); + var core = require("web.core"); + var _t = core._t; - var AttachmentPreviewMixin = { - canPreview: function (extension) { - return ( - $.inArray(extension, [ - "odt", - "odp", - "ods", - "fodt", - "pdf", - "ott", - "fodp", - "otp", - "fods", - "ots", - ]) > -1 - ); - }, + var chatterpreviewableAttachments = []; + var active_attachment_id = 0; + var active_attachment_index = 0; + var first_click = true; - getUrl: function ( - attachment_id, - attachment_url, - attachment_extension, - attachment_title - ) { - var url = - (window.location.origin || "") + - "/attachment_preview/static/lib/ViewerJS/index.html" + - "?type=" + - encodeURIComponent(attachment_extension) + - "&title=" + - encodeURIComponent(attachment_title) + - "#" + - attachment_url.replace(window.location.origin, ""); + 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 + ) { + if (attachment_url) { + var 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) + + "#" + + 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) + + "#" + + "/web/content/" + + attachment_id + + "?model%3Dir.attachment"; + return url; + } - showPreview: function ( - attachment_id, - attachment_url, - attachment_extension, - attachment_title, - split_screen - ) { - var url = this.getUrl( + const attachment = patch( + components.Attachment, + "mail/static/src/components/attachment/attachment.js", + { + events: _.extend({}, components.Chatter.prototype.events, { + "click .o_attachment_preview": "_onPreviewAttachment", + }), + + showPreview( attachment_id, attachment_url, attachment_extension, - attachment_title - ); - if (split_screen) { - this.trigger_up("onAttachmentPreview", {url: url}); - } else { - window.open(url); - } - }, - }; - - Chatter.include(AttachmentPreviewMixin); - Chatter.include({ - events: _.extend({}, Chatter.prototype.events, { - "click .o_attachment_preview": "_onPreviewAttachment", - }), - - previewableAttachments: null, - - _openAttachmentBox: function () { - var res = this._super.apply(this, arguments); - - this.getPreviewableAttachments().done( - function (atts) { - this.previewableAttachments = atts; - this.updatePreviewButtons(atts); - this.getParent().attachmentPreviewWidget.setAttachments(atts); - }.bind(this) - ); - - return res; - }, - - update: function () { - var res = this._super.apply(this, arguments); - var self = this; - if (this.getParent().$el.hasClass("attachment_preview")) { - this._fetchAttachments().done(function () { - self._openAttachmentBox(); - self.getPreviewableAttachments().done(function (atts) { - self.updatePreviewButtons(self.previewableAttachments); - self.previewableAttachments = atts; - self.getParent().attachmentPreviewWidget.setAttachments(atts); - }); + attachment_title, + split_screen + ) { + chatterpreviewableAttachments.forEach((att) => { + if (parseInt(att.id, 10) === attachment_id) { + if (att.url === undefined) { + att.url = attachment_url.slice( + window.location.origin.length + ); + } + } }); - } - return res; - }, - - _onPreviewAttachment: function (event) { - event.preventDefault(); - var self = this, - $target = $(event.currentTarget), - split_screen = $target.attr("data-target") !== "new", - attachment_id = parseInt($target.attr("data-id"), 10), - attachment_url = $target.attr("data-url"), - attachment_extension = $target.attr("data-extension"), - attachment_title = $target.attr("data-original-title"); - - if (attachment_extension) { - this.showPreview( + var url = getUrl( attachment_id, attachment_url, attachment_extension, - attachment_title, - split_screen + attachment_title ); - } else { - this._rpc({ - model: "ir.attachment", - method: "get_attachment_extension", - args: [attachment_id], - }).then(function (extension) { - self.showPreview( + if (split_screen) { + this.trigger("onAttachmentPreview", {url: url}); + } else { + window.open(url); + } + }, + + _onPreviewAttachment(event) { + first_click = true; + event.preventDefault(); + var $target = $(event.currentTarget), + split_screen = $target.attr("data-target") !== "new", + attachment_id = parseInt($target.attr("data-id"), 10), + attachment_extension = "pdf", + attachment_title = $target.attr("data-original-title"), + attachment_url = this.attachmentUrl; + active_attachment_id = attachment_id; + + if (attachment_extension) { + this.showPreview( attachment_id, attachment_url, - extension, - null, + attachment_extension, + attachment_title, split_screen ); - }); - } - }, - - getPreviewableAttachments: function () { - var self = this; - var deferred = $.Deferred(); - - var $items = this.$el.find(".o_attachment_preview"); - var attachments = _.object( - $items.map(function () { - return parseInt($(this).attr("data-id"), 10); - }), - $items.map(function () { - return { - url: $(this).attr("data-url"), - extension: $(this).attr("data-extension"), - title: $(this).attr("data-original-title"), - }; - }) - ); - - this._rpc({ - 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, id) { - return self.canPreview(extension); - }) - ), - function (id) { - return { - id: id, - url: attachments[id].url, - extension: extensions[id], - title: attachments[id].title, - previewUrl: self.getUrl( - id, - attachments[id].url, - extensions[id], - id + " - " + attachments[id].title - ), - }; - } - ); - deferred.resolve(reviewableAttachments); - }, - function () { - deferred.reject(); - } - ); - return deferred.promise(); - }, - - updatePreviewButtons: function (previewableAttachments) { - this.$el.find(".o_attachment_preview").each(function () { - var $this = $(this); - var id = $this.attr("data-id"); - var att = _.findWhere(previewableAttachments, {id: id}); - if (att) { - $this.attr("data-extension", att.extension); } else { - $this.remove(); + rpc.query({ + model: "ir.attachment", + method: "get_attachment_extension", + args: [attachment_id], + }).then(function (extension) { + this.showPreview( + attachment_id, + attachment_url, + extension, + null, + split_screen + ); + }); } - }); - }, - }); - - basic_fields.FieldBinaryFile.include(AttachmentPreviewMixin); - basic_fields.FieldBinaryFile.include({ - events: _.extend({}, basic_fields.FieldBinaryFile.prototype.events, { - "click .fa-search": "_onPreview", - }), - - _renderReadonly: function () { - var self = this; - this._super.apply(this, arguments); - - if (this.recordData.id) { - this._getBinaryExtension().done(function (extension) { - if (self.canPreview(extension)) { - self._renderPreviewButton(extension); - } - }); - } - }, - - _renderPreviewButton: function (extension) { - this.$previewBtn = $(""); - this.$previewBtn.addClass("fa fa-search mr-2"); - this.$previewBtn.attr("href", "javascript:void(0)"); - this.$previewBtn.attr( - "title", - _.str.sprintf(_t("Preview %s"), this.field.string) - ); - this.$previewBtn.attr("data-extension", extension); - this.$el.find(".fa-download").before(this.$previewBtn); - }, - - _getBinaryExtension: function () { - return this._rpc({ - 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(); - }, - }); + }, + } + ); var AttachmentPreviewWidget = Widget.extend({ template: "attachment_preview.AttachmentPreviewWidget", activeIndex: 0, - attachments: null, events: { "click .attachment_preview_close": "_onCloseClick", @@ -284,10 +161,11 @@ odoo.define("attachment_preview", function (require) { }, start: function () { + first_click = true; var res = this._super.apply(this, arguments); - this.$overlay = this.$el.find(".attachment_preview_overlay"); - this.$iframe = this.$el.find(".attachment_preview_iframe"); - this.$current = this.$el.find(".attachment_preview_current"); + this.$overlay = $(".attachment_preview_overlay"); + this.$iframe = $(".attachment_preview_iframe"); + this.$current = $(".attachment_preview_current"); return res; }, @@ -312,6 +190,9 @@ odoo.define("attachment_preview", function (require) { }, next: function () { + if (first_click) { + first_click = !first_click; + } var index = this.activeIndex + 1; if (index >= this.attachments.length) { index = 0; @@ -322,6 +203,10 @@ odoo.define("attachment_preview", function (require) { }, previous: function () { + if (first_click) { + first_click = !first_click; + } + first_click = true; var index = this.activeIndex - 1; if (index < 0) { index = this.attachments.length - 1; @@ -337,6 +222,7 @@ odoo.define("attachment_preview", function (require) { }, hide: function () { + first_click = true; this.$el.addClass("d-none"); this.trigger("hidden"); }, @@ -347,6 +233,9 @@ odoo.define("attachment_preview", function (require) { this.activeIndex + 1, this.attachments.length ); + this.$overlay = $(".attachment_preview_overlay"); + this.$iframe = $(".attachment_preview_iframe"); + this.$current = $(".attachment_preview_current"); this.$current.html(value); }, @@ -356,15 +245,243 @@ odoo.define("attachment_preview", function (require) { return; } - var att = this.attachments[this.activeIndex]; + if (first_click) { + for (let i = 0; i < this.attachments.length; i++) { + if (this.attachments[i].id === active_attachment_id.toString()) { + active_attachment_index = i; + first_click = false; + } + } + } else { + active_attachment_index = this.activeIndex; + } + + var att = this.attachments[active_attachment_index]; this.$iframe.attr("src", att.previewUrl); }, setAttachments: function (attachments) { - this.attachments = attachments; - this.activeIndex = 0; - this.updatePaginator(); - this.loadPreview(); + if (attachments) { + this.attachments = attachments; + this.activeIndex = 0; + this.updatePaginator(); + this.loadPreview(); + } + }, + }); + + const chatter = patch( + components.Chatter, + "mail/static/src/components/chatter/chatter.js", + { + showPreview( + attachment_id, + attachment_url, + attachment_extension, + attachment_title, + split_screen + ) { + var url = getUrl( + attachment_id, + attachment_url, + attachment_extension, + attachment_title + ); + if (split_screen) { + this.trigger("onAttachmentPreview", {url: url}); + } else { + window.open(url); + } + }, + + /** + * @override + */ + _update() { + var self = this; + + setTimeout(() => { + self.getPreviewableAttachments().then( + function (atts) { + this.previewableAttachments = atts; + // This.updatePreviewButtons(this.previewableAttachments); + this.updatePreviewButtons(this.previewableAttachments); + if (!this.attachmentPreviewWidget) { + this.attachmentPreviewWidget = new AttachmentPreviewWidget( + this + ); + this.attachmentPreviewWidget.setAttachments(atts); + } + this.previewableAttachments = atts; + chatterpreviewableAttachments = atts; + this.attachmentPreviewWidget.setAttachments(atts); + }.bind(this) + ); + }, 500); + }, + + getPreviewableAttachments: function () { + var deferred = $.Deferred(); + var self = this; + var attachments = []; + const chatter_ref = this.env.models["mail.chatter"].get( + self.props.chatterLocalId + ); + const thread = chatter_ref ? chatter_ref.thread : undefined; + if (thread) { + attachments = thread.allAttachments; + } + + attachments = _.object( + attachments.map((att) => { + return parseInt(att.localId.slice(16), 10); + }), + attachments.map((at) => { + if (at.defaultSource) { + return { + url: at.defaultSource, + extension: at.extension, + title: at.name, + }; + } + return { + url: "/web/content?id=" + at.id + "&download=true", + extension: at.extension, + title: at.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 + ) { + 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(); }, }); @@ -372,11 +489,10 @@ odoo.define("attachment_preview", function (require) { custom_events: _.extend({}, FormRenderer.prototype.custom_events, { onAttachmentPreview: "_onAttachmentPreview", }), - attachmentPreviewWidget: null, init: function () { - var res = this._super.apply(this, arguments); + var res = this._super(...arguments); this.attachmentPreviewWidget = new AttachmentPreviewWidget(this); this.attachmentPreviewWidget.on( "hidden", @@ -389,7 +505,7 @@ odoo.define("attachment_preview", function (require) { start: function () { var self = this; return this._super.apply(this, arguments).then(function () { - return self.attachmentPreviewWidget.insertAfter(self.$el); + self.attachmentPreviewWidget.insertAfter(self.$el); }); }, @@ -399,10 +515,8 @@ odoo.define("attachment_preview", function (require) { showAttachmentPreviewWidget: function () { this.$el.addClass("attachment_preview"); - - this.attachmentPreviewWidget.setAttachments( - this.chatter.previewableAttachments - ); + this.chatterpreviewableAttachments = chatterpreviewableAttachments; + this.attachmentPreviewWidget.setAttachments(chatterpreviewableAttachments); this.attachmentPreviewWidget.show(); }, @@ -411,13 +525,14 @@ odoo.define("attachment_preview", function (require) { return this._super.apply(this, arguments); }, - _onAttachmentPreview: function (event) { + _onAttachmentPreview: function () { this.showAttachmentPreviewWidget(); }, }); return { - AttachmentPreviewMixin: AttachmentPreviewMixin, + attachment, + chatter, AttachmentPreviewWidget: AttachmentPreviewWidget, }; }); diff --git a/attachment_preview/static/src/js/viewerjs_tweaks.js b/attachment_preview/static/src/js/viewerjs_tweaks.js index ad745646..21d209cb 100644 --- a/attachment_preview/static/src/js/viewerjs_tweaks.js +++ b/attachment_preview/static/src/js/viewerjs_tweaks.js @@ -4,6 +4,7 @@ // This file contains tweaks for viewerjs itself and is not meant to be run in // OpenERP's context (function (original_Viewer) { + "use strict"; window.Viewer = function (plugin, parameters) { if (!plugin) { // eslint-disable-next-line no-alert diff --git a/attachment_preview/static/src/scss/attachment_preview.scss b/attachment_preview/static/src/scss/attachment_preview.scss index 354cabe8..49cafb47 100644 --- a/attachment_preview/static/src/scss/attachment_preview.scss +++ b/attachment_preview/static/src/scss/attachment_preview.scss @@ -55,3 +55,10 @@ } } } + +.o_group { + .mr-2, + .mx-2 { + margin-left: 0.5rem !important; + } +} diff --git a/attachment_preview/static/src/xml/attachment_preview.xml b/attachment_preview/static/src/xml/attachment_preview.xml index e69de29b..e512e684 100644 --- a/attachment_preview/static/src/xml/attachment_preview.xml +++ b/attachment_preview/static/src/xml/attachment_preview.xml @@ -0,0 +1,67 @@ + + + + +
+ + +
+
+
+ + +
+
+
+ + + +
+ + + +
+