summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorjpr-odoo <jpr@openerp.com>2017-09-04 18:18:12 +0200
committerYannick Tivisse <yti@odoo.com>2017-09-05 15:52:15 +0200
commit46699c5214229ea8aebcc04e08d0786fd2b67f35 (patch)
tree60f74e793b442ba8d2379f68743fb5a4724472e0 /addons
parentfa432175e991be13035757e5174625f954bae171 (diff)
[IMP] mail: Add a preview popup for attachments
Purpose ======= The goal is that when you click on an attachment, you should get a preview of it and then you get the possibility to download it.
Diffstat (limited to 'addons')
-rw-r--r--addons/im_livechat/views/im_livechat_channel_templates.xml1
-rw-r--r--addons/mail/controllers/main.py1
-rw-r--r--addons/mail/static/src/js/composer.js26
-rw-r--r--addons/mail/static/src/js/document_viewer.js302
-rw-r--r--addons/mail/static/src/js/thread.js28
-rw-r--r--addons/mail/static/src/less/composer.less129
-rw-r--r--addons/mail/static/src/less/thread.less1
-rw-r--r--addons/mail/static/src/xml/thread.xml124
-rw-r--r--addons/mail/static/tests/chatter_tests.js86
-rw-r--r--addons/mail/views/mail_templates.xml1
-rw-r--r--addons/web/static/src/less/mimetypes.less2
-rw-r--r--addons/website_livechat/views/website_livechat.xml1
12 files changed, 677 insertions, 25 deletions
diff --git a/addons/im_livechat/views/im_livechat_channel_templates.xml b/addons/im_livechat/views/im_livechat_channel_templates.xml
index ff9b5ab1e25..e40a311c892 100644
--- a/addons/im_livechat/views/im_livechat_channel_templates.xml
+++ b/addons/im_livechat/views/im_livechat_channel_templates.xml
@@ -135,6 +135,7 @@
<script type="text/javascript" src="/bus/static/src/js/bus.js"></script>
<script type="text/javascript" src="/mail/static/src/js/chat_window.js"></script>
<script type="text/javascript" src="/mail/static/src/js/thread.js"></script>
+ <script type="text/javascript" src="/mail/static/src/js/document_viewer.js"></script>
<script type="text/javascript" src="/im_livechat/static/src/js/im_livechat.js"></script>
<t t-call="web.less_helpers"/>
<link rel="stylesheet" href="/im_livechat/static/src/less/im_livechat_bootstrap.less"/>
diff --git a/addons/mail/controllers/main.py b/addons/mail/controllers/main.py
index c1b4433e74a..6146b5b37e5 100644
--- a/addons/mail/controllers/main.py
+++ b/addons/mail/controllers/main.py
@@ -2,7 +2,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import base64
-import json
import logging
import psycopg2
import werkzeug
diff --git a/addons/mail/static/src/js/composer.js b/addons/mail/static/src/js/composer.js
index 29806a08e51..f4ba98696a5 100644
--- a/addons/mail/static/src/js/composer.js
+++ b/addons/mail/static/src/js/composer.js
@@ -2,6 +2,7 @@ odoo.define('mail.composer', function (require) {
"use strict";
var chat_mixin = require('mail.chat_mixin');
+var DocumentViewer = require('mail.DocumentViewer');
var utils = require('mail.utils');
var core = require('web.core');
@@ -354,6 +355,8 @@ var BasicComposer = Widget.extend(chat_mixin, {
"click .o_composer_button_send": "send_message",
"click .o_composer_button_add_attachment": "on_click_add_attachment",
"click .o_attachment_delete": "on_attachment_delete",
+ "click .o_attachment_download": "_onAttachmentDownload",
+ "click .o_attachment_view": "_onAttachmentView",
},
// RPCs done to fetch the mention suggestions are throttled with the following value
MENTION_THROTTLE: 200,
@@ -758,6 +761,29 @@ var BasicComposer = Widget.extend(chat_mixin, {
focus: function () {
this.$input.focus();
},
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ * @param {MouseEvent} event
+ */
+ _onAttachmentDownload: function (event) {
+ event.stopPropagation();
+ },
+ /**
+ * @private
+ * @param {MouseEvent} event
+ */
+ _onAttachmentView: function (event) {
+ var activeAttachmentID = $(event.currentTarget).data('id');
+ var attachments = this.get('attachment_ids');
+ if (activeAttachmentID) {
+ var attachmentViewer = new DocumentViewer(this, attachments, activeAttachmentID);
+ attachmentViewer.appendTo($('body'));
+ }
+ },
});
var ExtendedComposer = BasicComposer.extend({
diff --git a/addons/mail/static/src/js/document_viewer.js b/addons/mail/static/src/js/document_viewer.js
new file mode 100644
index 00000000000..679e483c899
--- /dev/null
+++ b/addons/mail/static/src/js/document_viewer.js
@@ -0,0 +1,302 @@
+odoo.define('mail.DocumentViewer', function (require) {
+"use strict";
+
+var core = require('web.core');
+var Widget = require('web.Widget');
+
+var QWeb = core.qweb;
+
+var SCROLL_ZOOM_STEP = 0.1;
+var ZOOM_STEP = 0.5;
+
+var DocumentViewer = Widget.extend({
+ template: "DocumentViewer",
+ events: {
+ 'click .o_download_btn': '_onDownload',
+ 'click .o_viewer_img': '_onImageClicked',
+ 'click .o_viewer_video': '_onVideoClicked',
+ 'click .move_next': '_onNext',
+ 'click .move_previous': '_onPrevious',
+ 'click .o_zoom_in': '_onZoomIn',
+ 'click .o_zoom_out': '_onZoomOut',
+ 'click .o_close_btn, .o_viewer_img_wrapper': '_onClose',
+ 'click .o_print_btn': '_onPrint',
+ 'DOMMouseScroll .o_viewer_content': '_onScroll', // Firefox
+ 'mousewheel .o_viewer_content': '_onScroll', // Chrome, Safari, IE
+ 'keydown': '_onKeydown',
+ 'mousedown .o_viewer_img': '_onStartDrag',
+ 'mousemove .o_viewer_content': '_onDrag',
+ 'mouseup .o_viewer_content': '_onEndDrag'
+ },
+ /**
+ * The documentViewer takes an array of objects describing attachments in
+ * argument, and the ID of an active attachment (the one to display first).
+ * Documents that are not of type image or video are filtered out.
+ *
+ * @override
+ * @param {Array[Object]} attachments list of attachments
+ * @param {integer} activeAttachmentID
+ */
+ init: function (parent, attachments, activeAttachmentID) {
+ this._super.apply(this, arguments);
+ this.attachment = _.filter(attachments, function (attachment) {
+ var type = attachment.mimetype.split('/').shift();
+ if (type === 'image' || type === 'video') {
+ attachment.type = type;
+ return true;
+ }
+ });
+ this.activeAttachment = _.findWhere(attachments, {id: activeAttachmentID});
+ this._reset();
+ },
+ /**
+ * Open a modal displaying the active attachment
+ * @override
+ */
+ start: function () {
+ this.$el.modal('show');
+ this.$el.on('hidden.bs.modal', _.bind(this._onDestroy, this));
+ this.$('.o_viewer_img').load(_.bind(this._onImageLoaded, this));
+ return this._super.apply(this, arguments);
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //---------------------------------------------------------------------------
+
+ /**
+ * @private
+ */
+ _next: function () {
+ var index = _.findIndex(this.attachment, this.activeAttachment);
+ index = (index + 1) % this.attachment.length;
+ this.activeAttachment = this.attachment[index];
+ this._updateContent();
+ },
+ /**
+ * @private
+ */
+ _previous: function () {
+ var index = _.findIndex(this.attachment, this.activeAttachment);
+ index = index === 0 ? this.attachment.length - 1 : index - 1;
+ this.activeAttachment = this.attachment[index];
+ this._updateContent();
+ },
+ /**
+ * @private
+ */
+ _reset: function () {
+ this.scale = 1;
+ this.dragStartX = this.dragstopX = 0;
+ this.dragStartY = this.dragstopY = 0;
+ },
+ /**
+ * Render the active attachment
+ *
+ * @private
+ */
+ _updateContent: function () {
+ this.$('.o_viewer_content').html(QWeb.render('DocumentViewer.Content', {
+ widget: this
+ }));
+ this.$('.o_viewer_img').load(_.bind(this._onImageLoaded, this));
+ this._reset();
+ },
+ /**
+ * Zoom in/out image by provided scale
+ *
+ * @private
+ * @param {integer} scale
+ */
+ _zoom: function (scale) {
+ if (scale > 0.5) {
+ this.$('.o_viewer_img').css('transform', 'scale3d(' + scale + ', ' + scale + ', 1)');
+ this.scale = scale;
+ }
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ * @param {MouseEvent} e
+ */
+ _onClose: function (e) {
+ e.preventDefault();
+ this.$el.modal('hide');
+ },
+ /**
+ * When popup close complete destroyed modal even DOM footprint too
+ * @private
+ */
+ _onDestroy: function () {
+ if (this.isDestroyed()) {
+ return;
+ }
+ this.$el.modal('hide');
+ this.$el.remove();
+ },
+ /**
+ * @private
+ * @param {MouseEvent} e
+ */
+ _onDownload: function (e) {
+ e.preventDefault();
+ window.location = '/web/content/' + this.activeAttachment.id + '?download=true';
+ },
+ /**
+ * @private
+ * @param {MouseEvent} e
+ */
+ _onDrag: function (e) {
+ e.preventDefault();
+ if (this.enableDrag) {
+ var $image = this.$('.o_viewer_img');
+ var $zoomer = this.$('.o_viewer_zoomer');
+ var top = $image.prop('offsetHeight') * this.scale > $zoomer.height() ? e.clientY - this.dragStartY : 0;
+ var left = $image.prop('offsetWidth') * this.scale > $zoomer.width() ? e.clientX - this.dragStartX : 0;
+ $zoomer.css("transform", "translate3d("+ left +"px, " + top + "px, 0)");
+ }
+ },
+ /**
+ * @private
+ * @param {MouseEvent} e
+ */
+ _onEndDrag: function (e) {
+ e.preventDefault();
+ if (this.enableDrag) {
+ this.enableDrag = false;
+ this.dragstopX = e.clientX - this.dragStartX;
+ this.dragstopY = e.clientY - this.dragStartY;
+ }
+ },
+ /**
+ * On click of image do not close modal so stop event propagation
+ *
+ * @private
+ * @param {MouseEvent} e
+ */
+ _onImageClicked: function (e) {
+ e.stopPropagation();
+ },
+ /**
+ * Remove loading indicator when image loaded
+ * @private
+ */
+ _onImageLoaded: function () {
+ this.$('.o_loading_img').hide();
+ },
+ /**
+ * Move next previous attachment on keyboard right left key
+ *
+ * @private
+ * @param {KeyEvent} e
+ */
+ _onKeydown: function (e){
+ switch (e.which) {
+ case $.ui.keyCode.RIGHT:
+ e.preventDefault();
+ this._next();
+ break;
+ case $.ui.keyCode.LEFT:
+ e.preventDefault();
+ this._previous();
+ break;
+ }
+ },
+ /**
+ * @private
+ * @param {MouseEvent} e
+ */
+ _onNext: function (e) {
+ e.preventDefault();
+ this._next();
+ },
+ /**
+ * @private
+ * @param {MouseEvent} e
+ */
+ _onPrevious: function (e) {
+ e.preventDefault();
+ this._previous();
+ },
+ /**
+ * @private
+ * @param {MouseEvent} e
+ */
+ _onPrint: function (e) {
+ e.preventDefault();
+ var src = this.$('.o_viewer_img').prop('src');
+ var script = QWeb.render('PrintImage', {
+ src: src
+ });
+ var printWindow = window.open('about:blank', "_new");
+ printWindow.document.open();
+ printWindow.document.write(script);
+ printWindow.document.close();
+ },
+ /**
+ * Zoom image on scroll
+ *
+ * @private
+ * @param {MouseEvent} e
+ */
+ _onScroll: function (e) {
+ var scale;
+ if (e.originalEvent.wheelDelta > 0 || e.originalEvent.detail < 0) {
+ scale = this.scale + SCROLL_ZOOM_STEP;
+ this._zoom(scale);
+ } else {
+ scale = this.scale - SCROLL_ZOOM_STEP;
+ this._zoom(scale);
+ }
+ },
+ /**
+ * @private
+ * @param {MouseEvent} e
+ */
+ _onStartDrag: function (e) {
+ e.preventDefault();
+ this.enableDrag = true;
+ this.dragStartX = e.clientX - (this.dragstopX || 0);
+ this.dragStartY = e.clientY - (this.dragstopY || 0);
+ },
+ /**
+ * On click of video do not close modal so stop event propagation
+ * and provide play/pause the video instead of quitting it
+ *
+ * @private
+ * @param {MouseEvent} e
+ */
+ _onVideoClicked: function (e) {
+ e.stopPropagation();
+ var videoElement = e.target;
+ if (videoElement.paused) {
+ videoElement.play();
+ } else {
+ videoElement.pause();
+ }
+ },
+ /**
+ * @private
+ * @param {MouseEvent} e
+ */
+ _onZoomIn: function (e) {
+ e.preventDefault();
+ var scale = this.scale + ZOOM_STEP;
+ this._zoom(scale);
+ },
+ /**
+ * @private
+ * @param {MouseEvent} e
+ */
+ _onZoomOut: function (e) {
+ e.preventDefault();
+ var scale = this.scale - ZOOM_STEP;
+ this._zoom(scale);
+ },
+});
+return DocumentViewer;
+});
diff --git a/addons/mail/static/src/js/thread.js b/addons/mail/static/src/js/thread.js
index 038bbf93de9..80d4c4c8a4c 100644
--- a/addons/mail/static/src/js/thread.js
+++ b/addons/mail/static/src/js/thread.js
@@ -2,6 +2,7 @@ odoo.define('mail.ChatThread', function (require) {
"use strict";
var core = require('web.core');
+var DocumentViewer = require('mail.DocumentViewer');
var Widget = require('web.Widget');
var QWeb = core.qweb;
@@ -30,6 +31,8 @@ var Thread = Widget.extend({
"click img": "on_click_redirect",
"click strong": "on_click_redirect",
"click .o_thread_show_more": "on_click_show_more",
+ "click .o_attachment_download": "_onAttachmentDownload",
+ "click .o_attachment_view": "_onAttachmentView",
"click .o_thread_message_needaction": function (event) {
var message_id = $(event.currentTarget).data('message-id');
this.trigger("mark_as_read", message_id);
@@ -110,6 +113,8 @@ var Thread = Widget.extend({
ORDER: ORDER,
}));
+ this.attachments = _.uniq(_.flatten(_.map(messages, 'attachment_ids')));
+
_.each(msgs, function(msg) {
var $msg = self.$('.o_thread_message[data-message-id="'+ msg.id +'"]');
$msg.find('.o_mail_timestamp').data('date', msg.date);
@@ -300,6 +305,29 @@ var Thread = Widget.extend({
destroy: function () {
clearInterval(this.update_timestamps_interval);
},
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ * @param {MouseEvent} event
+ */
+ _onAttachmentDownload: function (event) {
+ event.stopPropagation();
+ },
+ /**
+ * @private
+ * @param {MouseEvent} event
+ */
+ _onAttachmentView: function (event) {
+ var activeAttachmentID = $(event.currentTarget).data('id');
+ if (activeAttachmentID) {
+ var attachmentViewer = new DocumentViewer(this, this.attachments, activeAttachmentID);
+ attachmentViewer.appendTo($('body'));
+ }
+ },
});
Thread.ORDER = ORDER;
diff --git a/addons/mail/static/src/less/composer.less b/addons/mail/static/src/less/composer.less
index 970d0649aa9..4073cef80c2 100644
--- a/addons/mail/static/src/less/composer.less
+++ b/addons/mail/static/src/less/composer.less
@@ -238,36 +238,60 @@
100% { background: @odoo-brand-optional }
}
-@o-mail-attachment-image-size: 60px;
+@o-mail-attachment-image-size: 100px;
.o_attachment {
position: relative;
- width: 2*@o-mail-attachment-image-size;
+ vertical-align: top;
display: inline-block;
- height: @o-mail-attachment-image-size;
text-align: center;
- margin-bottom: 15px;
-
- .o_image {
- .o-position-absolute(@top: 0, @left: @o-mail-attachment-image-size/2);
- width: @o-mail-attachment-image-size;
- height: @o-mail-attachment-image-size;
+ width: @o-mail-attachment-image-size;
+ margin: 5px;
+ .o_image_box {
+ position: relative;
+ img {
+ width: @o-mail-attachment-image-size;
+ height: @o-mail-attachment-image-size;
+ }
+ .o_image_overlay{
+ display: none;
+ }
+ &:hover .o_image_overlay {
+ display: block;
+ .o-position-absolute(@top: 0, @left: 0, @right:0, @bottom:0);
+ z-index: 20;
+ background: rgba(0, 0, 0, 0.5);
+ overflow: hidden;
+ cursor: pointer;
+ .o_attachment_download {
+ .o-position-absolute(@left: 10px, @bottom:5px);
+ }
+ .o_attachment_play {
+ .o-position-absolute(@top: 50%, @left: 50%);
+ transform: translate(-50%, -50%);
+ }
+ .o_overlay_download {
+ width: 100%;
+ height: 100%;
+ }
+ i {
+ color: #FFFFFF;
+ }
+ }
}
-
- .o_attachment_name {
- display: inline-block;
- max-width: 100%;
- margin-top: @o-mail-attachment-image-size;
- overflow: hidden;
+ .caption {
text-overflow: ellipsis;
white-space: nowrap;
- color: @odoo-main-text-color;
+ overflow: hidden;
+ padding: 5px;
+ a {
+ color: @odoo-main-text-color;
+ }
}
-
.o_attachment_progress_bar {
display: none;
}
.o_attachment_delete {
- .o-position-absolute(@top: 0, @right: 0);
+ .o-position-absolute(@top: -10px, @right: -8px);
color: @odoo-main-text-color;
cursor: pointer;
opacity: 0;
@@ -289,9 +313,76 @@
display: none;
}
}
-
&:hover .o_attachment_delete {
opacity: 1;
.o-transition(opacity, 0.2s, linear);
}
}
+
+.o_modal_fullscreen {
+ .o_viewer_content {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ cursor: pointer;
+ .o_viewer-header {
+ width: 100%;
+ height: 45px;
+ padding: 10px 10px 0;
+ background-color: rgba(0, 0, 0, 0.45);
+ cursor: auto;
+ .o_image_caption {
+ color: #FFFFFF;
+ display: inline;
+ }
+ }
+ .o_loading_img {
+ .o-position-absolute(@top: 50%);
+ width: 25px;
+ right: 50%;
+ z-index: 1;
+ }
+ .o_viewer_img_wrapper {
+ width: 100%;
+ height: 100%;
+ position: fixed;
+ .o_viewer_zoomer {
+ width: 100%;
+ height: 100%;
+ .o-flex-display();
+ justify-content: center;
+ align-items: center;
+ img, video {
+ max-width: 100%;
+ max-height: 100%;
+ transition: 0.3s cubic-bezier(0, 0, 0.49, 1.6) 0s, opacity 0.15s;
+ }
+ .o_viewer_video {
+ height: 80%;
+ }
+ }
+ }
+ .o_viewer_toolbar {
+ .o-position-absolute(@left: 50%);
+ bottom: 15px;
+ transform: translate(-50%, -50%);
+ padding: 8px 10px 0px 10px;
+ background-color: rgba(0, 0, 0, 0.6);
+ border-radius: 4px;
+ }
+ }
+ .arrow {
+ .o-position-absolute(@top: 50%);
+ color: #FFFFFF;
+ }
+ .arrow-left {
+ left: 0;
+ }
+
+ .arrow-right {
+ right: 0;
+ }
+ i.fa {
+ color: #FFFFFF;
+ }
+}
diff --git a/addons/mail/static/src/less/thread.less b/addons/mail/static/src/less/thread.less
index be39413f573..41512f60b08 100644
--- a/addons/mail/static/src/less/thread.less
+++ b/addons/mail/static/src/less/thread.less
@@ -127,7 +127,6 @@
}
img {
max-width: 100%;
- height: auto;
}
.o_mail_body_long {
diff --git a/addons/mail/static/src/xml/thread.xml b/addons/mail/static/src/xml/thread.xml
index 6f23e4a5e34..43cf896e1cc 100644
--- a/addons/mail/static/src/xml/thread.xml
+++ b/addons/mail/static/src/xml/thread.xml
@@ -18,6 +18,87 @@
</div>
</t>
+
+ <t t-name="DocumentViewer.Content">
+ <div class="o_viewer_content">
+ <div class="o_viewer-header">
+ <h2 class="o_image_caption"><t t-esc="widget.activeAttachment.name"/></h2>
+ <ul class="list-inline pull-right">
+ <li t-if="widget.activeAttachment.type == 'image'">
+ <a class="o_print_btn" href="#">
+ <i class="fa fa-2x fa-print"/>
+ </a>
+ </li>
+ <li>
+ <a class="o_close_btn" href="#">
+ <i class="fa fa-2x fa-times"/>
+ </a>
+ </li>
+ </ul>
+ </div>
+ <div class="o_viewer_img_wrapper">
+ <div class="o_viewer_zoomer">
+ <div t-if="widget.activeAttachment.type == 'image'" class="o_loading_img">
+ <i class="fa fa-spinner fa-spin fa-3x fa-fw" aria-hidden="true"/>
+ </div>
+ <img t-if="widget.activeAttachment.type == 'image'" class="o_viewer_img" t-attf-src="/web/image/#{widget.activeAttachment.id}?unique=1"/>
+ <video t-if="widget.activeAttachment.type == 'video'" class="o_viewer_video" controls="controls">
+ <source t-attf-src="/web/image/#{widget.activeAttachment.id}" t-att-data-type="widget.activeAttachment.mimetype"/>
+ </video>
+ </div>
+ </div>
+ <div t-if="widget.activeAttachment.type == 'image'" class="o_viewer_toolbar">
+ <ul class="list-inline">
+ <li>
+ <a class="o_zoom_out" href="#">
+ <i class="fa fa-2x fa-search-minus"></i>
+ </a>
+ </li>
+ <li>
+ <a class="o_zoom_in" href="#">
+ <i class="fa fa-2x fa-search-plus"></i>
+ </a>
+ </li>
+ <li>
+ <a class="o_download_btn" href="#">
+ <i class="fa fa-2x fa-download"/>
+ </a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </t>
+
+ <t t-name="DocumentViewer">
+ <div class="modal o_modal_fullscreen" tabindex="-1" role="dialog" aria-hidden="true">
+ <t t-call="DocumentViewer.Content"/>
+ <a class="arrow arrow-left move_previous" href="#">
+ <span class="fa fa-3x fa-chevron-left"/>
+ </a>
+ <a class="arrow arrow-right move_next" href="#">
+ <span class="fa fa-3x fa-chevron-right"/>
+ </a>
+ </div>
+ </t>
+ <t t-name="PrintImage">
+ <html>
+ <head>
+ <script>
+ function onload_img() {
+ setTimeout('print_img()', 10);
+ }
+ function print_img() {
+ window.print();
+ window.close();
+ }
+ </script>
+ </head>
+ <body onload='onload_img()'>
+ <img t-att-src='src' />
+ </body>
+ </html>
+ </t>
+
<!-- When a channel is empty-->
<div t-name="mail.EmptyChannel" class="o_mail_no_content">
<t t-if="options.channel_id==='channel_inbox'">
@@ -175,10 +256,47 @@
</t>
<t t-name="mail.Attachment">
+ <t t-set="type" t-value="attachment.mimetype.split('/').shift()"/>
<div t-attf-class="o_attachment #{attachment.upload ? 'o_attachment_uploading' : ''}" t-att-title="attachment.name">
- <a class="o_image" t-att-href='attachment.url' target="_blank" t-att-data-mimetype="attachment.mimetype" t-attf-data-src="/web/image/#{attachment.id}/100x80">
- <span class='o_attachment_name'><t t-esc='attachment.name'/></span>
- </a>
+ <t t-if="type == 'image'">
+ <div class="o_image_box">
+ <img t-attf-src="/web/image/#{attachment.id}/200x200/?crop=True"/>
+ <div t-attf-class="o_image_overlay o_attachment_view" t-att-data-id="attachment.id">
+ <a class="o_attachment_download" t-att-href='attachment.url' target="_blank">
+ <i t-attf-class="fa fa-2x fa-arrow-circle-o-down" title="Download this attachment" aria-hidden="true"></i>
+ </a>
+ </div>
+ </div>
+ </t>
+ <t t-elif="type == 'video'">
+ <div class="o_image_box o_image_preview">
+ <img class="o_image" t-att-data-mimetype="attachment.mimetype"/>
+ <div t-attf-class="o_image_overlay o_attachment_view" t-att-data-id="attachment.id">
+ <a class="o_attachment_play">
+ <i t-attf-class="fa fa-2x fa-play-circle" title="Play this video" aria-hidden="true"></i>
+ </a>
+ <a class="o_attachment_download" t-att-href='attachment.url' target="_blank">
+ <i t-attf-class="fa fa-2x fa-arrow-circle-o-down" title="Download this attachment" aria-hidden="true"></i>
+ </a>
+ </div>
+ </div>
+ </t>
+ <t t-else="">
+ <div class="o_image_box">
+ <img class="o_image" t-att-data-mimetype="attachment.mimetype"/>
+ <div t-attf-class="o_image_overlay">
+ <a class="o_overlay_download" t-att-href='attachment.url' target="_blank"/>
+ <a class="o_attachment_download" t-att-href='attachment.url' target="_blank">
+ <i t-attf-class="fa fa-2x fa-arrow-circle-o-down" title="Download this attachment" aria-hidden="true"></i>
+ </a>
+ </div>
+ </div>
+ </t>
+ <div class="caption">
+ <a t-att-href='attachment.url' target="_blank">
+ <t t-esc='attachment.name'/>
+ </a>
+ </div>
<t t-if="editable">
<div class="o_attachment_delete">
<i class='fa fa-times-circle' title="Delete this attachment" t-att-data-id="attachment.id"/>
diff --git a/addons/mail/static/tests/chatter_tests.js b/addons/mail/static/tests/chatter_tests.js
index b5d7feab56e..337f3e9f9d1 100644
--- a/addons/mail/static/tests/chatter_tests.js
+++ b/addons/mail/static/tests/chatter_tests.js
@@ -494,6 +494,92 @@ QUnit.test('chatter: post, receive and star messages', function (assert) {
});
});
+QUnit.test('chatter: Attachment viewer', function (assert) {
+ assert.expect(5);
+ this.data.partner.records[0].message_ids = [1];
+ var messages = [{
+ attachment_ids: [{
+ filename: 'image1.jpg',
+ id:1,
+ mimetype: 'image/jpeg',
+ name: 'Test Image 1',
+ url: '/web/content/1?download=true'
+ },{
+ filename: 'image2.jpg',
+ id:2,
+ mimetype: 'image/jpeg',
+ name: 'Test Image 2',
+ url: '/web/content/2?download=true'
+ },{
+ filename: 'image3.jpg',
+ id:3,
+ mimetype: 'image/jpeg',
+ name: 'Test Image 3',
+ url: '/web/content/3?download=true'
+ }],
+ author_id: ["1", "John Doe"],
+ body: "Attachement viewer test",
+ date: moment("2016-12-20 09:35:40"),
+ displayed_author: "John Doe",
+ id: 1,
+ is_note: false,
+ is_starred: false,
+ model: 'partner',
+ res_id: 2,
+ }];
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '<form string="Partners">' +
+ '<sheet>' +
+ '<field name="foo"/>' +
+ '</sheet>' +
+ '<div class="oe_chatter">' +
+ '<field name="message_ids" widget="mail_thread" options="{\'display_log_button\': True}"/>' +
+ '</div>' +
+ '</form>',
+ res_id: 2,
+ mockRPC: function (route) {
+ if(_.str.contains(route, '/mail/attachment/preview/')){
+ var canvas = document.createElement('canvas');
+ return $.when(canvas.toDataURL());
+ }
+ return this._super.apply(this, arguments);
+ },
+ intercepts: {
+ get_messages: function (event) {
+ event.stopPropagation();
+ var requested_msgs = _.filter(messages, function (msg) {
+ return _.contains(event.data.options.ids, msg.id);
+ });
+ event.data.callback($.when(requested_msgs));
+ },
+ get_bus: function (event) {
+ event.stopPropagation();
+ event.data.callback(new Bus());
+ },
+ },
+ });
+ assert.strictEqual(form.$('.o_thread_message .o_attachment').length, 3,
+ "there should be three attachment on message");
+ assert.strictEqual(form.$('.o_thread_message .o_attachment .caption a').first().attr('href'), '/web/content/1?download=true',
+ "image caption should have correct download link");
+ // click on first image attachement
+ form.$('.o_thread_message .o_attachment .o_image_box .o_image_overlay').first().click();
+ assert.strictEqual($('.o_modal_fullscreen img.o_viewer_img[src*="/web/image/1?unique=1"]').length, 1,
+ "Modal popup should open with first image src");
+ // click on next button
+ $('.modal .arrow.arrow-right.move_next span').click();
+ assert.strictEqual($('.o_modal_fullscreen img.o_viewer_img[src*="/web/image/2?unique=1"]').length, 1,
+ "Modal popup should have now second image src");
+ assert.strictEqual($('.o_modal_fullscreen .o_viewer_toolbar .o_download_btn').length, 1,
+ "Modal popup should have download button");
+ // close attachment popup
+ $('.o_modal_fullscreen .o_viewer-header .o_close_btn').click();
+ form.destroy();
+});
+
QUnit.test('form activity widget: schedule next activity', function (assert) {
assert.expect(5);
this.data.partner.records[0].activity_ids = [1];
diff --git a/addons/mail/views/mail_templates.xml b/addons/mail/views/mail_templates.xml
index 368978bb551..2fe052975c3 100644
--- a/addons/mail/views/mail_templates.xml
+++ b/addons/mail/views/mail_templates.xml
@@ -8,6 +8,7 @@
<script type="text/javascript" src="/mail/static/src/js/client_action.js"></script>
<script type="text/javascript" src="/mail/static/src/js/chat_window.js"></script>
<script type="text/javascript" src="/mail/static/src/js/extended_chat_window.js"></script>
+ <script type="text/javascript" src="/mail/static/src/js/document_viewer.js"></script>
<script type="text/javascript" src="/mail/static/src/js/composer.js"></script>
<script type="text/javascript" src="/mail/static/src/js/chat_manager.js"></script>
<script type="text/javascript" src="/mail/static/src/js/chatter.js"></script>
diff --git a/addons/web/static/src/less/mimetypes.less b/addons/web/static/src/less/mimetypes.less
index 12e86a34ab8..ad0ddbacd81 100644
--- a/addons/web/static/src/less/mimetypes.less
+++ b/addons/web/static/src/less/mimetypes.less
@@ -2,7 +2,7 @@
background-image: url('/web/static/src/img/mimetypes/unknown.png');
background-size: contain;
width: 100px;
- height: 80px;
+ height: 100px;
display: inline-block;
background-position: center;
background-repeat: no-repeat;
diff --git a/addons/website_livechat/views/website_livechat.xml b/addons/website_livechat/views/website_livechat.xml
index 6aef10b7211..523705d93a9 100644
--- a/addons/website_livechat/views/website_livechat.xml
+++ b/addons/website_livechat/views/website_livechat.xml
@@ -10,6 +10,7 @@
<xpath expr="." position="inside">
<script type="text/javascript" src="/bus/static/src/js/bus.js"></script>
<script type="text/javascript" src="/mail/static/src/js/chat_window.js"></script>
+ <script type="text/javascript" src="/mail/static/src/js/document_viewer.js"></script>
<script type="text/javascript" src="/mail/static/src/js/thread.js"></script>
<script type="text/javascript" src="/im_livechat/static/src/js/im_livechat.js"></script>
<link rel="stylesheet" href="/mail/static/src/less/chat_window.less"></link>