[IMP]: Added Upstream patch for Mail
This commit is contained in:
parent
f4ba553e51
commit
d4c0819ce9
@ -2,6 +2,7 @@
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import date, datetime, timedelta
|
||||
import pytz
|
||||
|
||||
from flectra import api, exceptions, fields, models, _
|
||||
|
||||
@ -77,7 +78,7 @@ class MailActivity(models.Model):
|
||||
summary = fields.Char('Summary')
|
||||
note = fields.Html('Note')
|
||||
feedback = fields.Html('Feedback')
|
||||
date_deadline = fields.Date('Due Date', index=True, required=True, default=fields.Date.today)
|
||||
date_deadline = fields.Date('Due Date', index=True, required=True, default=fields.Date.context_today)
|
||||
# description
|
||||
user_id = fields.Many2one(
|
||||
'res.users', 'Assigned to',
|
||||
@ -108,8 +109,16 @@ class MailActivity(models.Model):
|
||||
|
||||
@api.depends('date_deadline')
|
||||
def _compute_state(self):
|
||||
today = date.today()
|
||||
today_default = date.today()
|
||||
|
||||
for record in self.filtered(lambda activity: activity.date_deadline):
|
||||
today = today_default
|
||||
tz = record.user_id.sudo().tz
|
||||
if tz:
|
||||
today_utc = pytz.UTC.localize(datetime.utcnow())
|
||||
today_tz = today_utc.astimezone(pytz.timezone(tz))
|
||||
today = date(year=today_tz.year, month=today_tz.month, day=today_tz.day)
|
||||
|
||||
date_deadline = fields.Date.from_string(record.date_deadline)
|
||||
diff = (date_deadline - today)
|
||||
if diff.days == 0:
|
||||
@ -132,7 +141,8 @@ class MailActivity(models.Model):
|
||||
|
||||
@api.onchange('recommended_activity_type_id')
|
||||
def _onchange_recommended_activity_type_id(self):
|
||||
self.activity_type_id = self.recommended_activity_type_id
|
||||
if self.recommended_activity_type_id:
|
||||
self.activity_type_id = self.recommended_activity_type_id
|
||||
|
||||
@api.multi
|
||||
def _check_access(self, operation):
|
||||
|
@ -505,7 +505,15 @@ class Channel(models.Model):
|
||||
partners_to_add = partners - channel.channel_partner_ids
|
||||
channel.write({'channel_last_seen_partner_ids': [(0, 0, {'partner_id': partner_id}) for partner_id in partners_to_add.ids]})
|
||||
for partner in partners_to_add:
|
||||
notification = _('<div class="o_mail_notification">joined <a href="#" class="o_channel_redirect" data-oe-id="%s">#%s</a></div>') % (self.id, self.name,)
|
||||
if partner.id != self.env.user.partner_id.id:
|
||||
notification = _('<div class="o_mail_notification">%(author)s invited %(new_partner)s to <a href="#" class="o_channel_redirect" data-oe-id="%(channel_id)s">#%(channel_name)s</a></div>') % {
|
||||
'author': self.env.user.display_name,
|
||||
'new_partner': partner.display_name,
|
||||
'channel_id': channel.id,
|
||||
'channel_name': channel.name,
|
||||
}
|
||||
else:
|
||||
notification = _('<div class="o_mail_notification">joined <a href="#" class="o_channel_redirect" data-oe-id="%s">#%s</a></div>') % (channel.id, channel.name,)
|
||||
self.message_post(body=notification, message_type="notification", subtype="mail.mt_comment", author_id=partner.id)
|
||||
|
||||
# broadcast the channel header to the added partner
|
||||
@ -674,7 +682,7 @@ class Channel(models.Model):
|
||||
msg += _(" This channel is private. People must be invited to join it.")
|
||||
else:
|
||||
channel_partners = self.env['mail.channel.partner'].search([('partner_id', '!=', partner.id), ('channel_id', '=', self.id)])
|
||||
msg = _("You are in a private conversation with <b>@%s</b>.") % channel_partners[0].partner_id.name
|
||||
msg = _("You are in a private conversation with <b>@%s</b>.") % (channel_partners[0].partner_id.name if channel_partners else _('Anonymous'))
|
||||
msg += _("""<br><br>
|
||||
You can mention someone by typing <b>@username</b>, this will grab its attention.<br>
|
||||
You can mention a channel by typing <b>#channel</b>.<br>
|
||||
|
@ -5,6 +5,7 @@ import logging
|
||||
import re
|
||||
|
||||
from email.utils import formataddr
|
||||
from openerp.http import request
|
||||
|
||||
from flectra import _, api, fields, models, modules, SUPERUSER_ID, tools
|
||||
from flectra.exceptions import UserError, AccessError
|
||||
@ -299,11 +300,12 @@ class Message(models.Model):
|
||||
|
||||
# 2. Attachments as SUPERUSER, because could receive msg and attachments for doc uid cannot see
|
||||
attachments_data = attachments.sudo().read(['id', 'datas_fname', 'name', 'mimetype'])
|
||||
safari = request and request.httprequest.user_agent.browser == 'safari'
|
||||
attachments_tree = dict((attachment['id'], {
|
||||
'id': attachment['id'],
|
||||
'filename': attachment['datas_fname'],
|
||||
'name': attachment['name'],
|
||||
'mimetype': attachment['mimetype'],
|
||||
'mimetype': 'application/octet-stream' if safari and 'video' in attachment['mimetype'] else attachment['mimetype'],
|
||||
}) for attachment in attachments_data)
|
||||
|
||||
# 3. Tracking values
|
||||
@ -422,7 +424,18 @@ class Message(models.Model):
|
||||
subtype_ids = [msg['subtype_id'][0] for msg in message_values if msg['subtype_id']]
|
||||
subtypes = self.env['mail.message.subtype'].sudo().browse(subtype_ids).read(['internal', 'description'])
|
||||
subtypes_dict = dict((subtype['id'], subtype) for subtype in subtypes)
|
||||
|
||||
# fetch notification status
|
||||
notif_dict = {}
|
||||
notifs = self.env['mail.notification'].sudo().search([('mail_message_id', 'in', list(mid for mid in message_tree)), ('is_read', '=', False)])
|
||||
for notif in notifs:
|
||||
mid = notif.mail_message_id.id
|
||||
if not notif_dict.get(mid):
|
||||
notif_dict[mid] = {'partner_id': list()}
|
||||
notif_dict[mid]['partner_id'].append(notif.res_partner_id.id)
|
||||
|
||||
for message in message_values:
|
||||
message['needaction_partner_ids'] = notif_dict.get(message['id'], dict()).get('partner_id', [])
|
||||
message['is_note'] = message['subtype_id'] and subtypes_dict[message['subtype_id'][0]]['internal']
|
||||
message['subtype_description'] = message['subtype_id'] and subtypes_dict[message['subtype_id'][0]]['description']
|
||||
if message['model'] and self.env[message['model']]._original_module:
|
||||
|
@ -126,7 +126,8 @@ class MailThread(models.AbstractModel):
|
||||
followers = self.env['mail.followers'].sudo().search([
|
||||
('res_model', '=', self._name),
|
||||
('partner_id', operator, operand)])
|
||||
return [('id', 'in', followers.mapped('res_id'))]
|
||||
# using read() below is much faster than followers.mapped('res_id')
|
||||
return [('id', 'in', [res['res_id'] for res in followers.read(['res_id'])])]
|
||||
|
||||
@api.model
|
||||
def _search_follower_channels(self, operator, operand):
|
||||
@ -139,7 +140,8 @@ class MailThread(models.AbstractModel):
|
||||
followers = self.env['mail.followers'].sudo().search([
|
||||
('res_model', '=', self._name),
|
||||
('channel_id', operator, operand)])
|
||||
return [('id', 'in', followers.mapped('res_id'))]
|
||||
# using read() below is much faster than followers.mapped('res_id')
|
||||
return [('id', 'in', [res['res_id'] for res in followers.read(['res_id'])])]
|
||||
|
||||
@api.multi
|
||||
@api.depends('message_follower_ids')
|
||||
@ -149,7 +151,8 @@ class MailThread(models.AbstractModel):
|
||||
('res_id', 'in', self.ids),
|
||||
('partner_id', '=', self.env.user.partner_id.id),
|
||||
])
|
||||
following_ids = followers.mapped('res_id')
|
||||
# using read() below is much faster than followers.mapped('res_id')
|
||||
following_ids = [res['res_id'] for res in followers.read(['res_id'])]
|
||||
for record in self:
|
||||
record.message_is_follower = record.id in following_ids
|
||||
|
||||
@ -161,9 +164,11 @@ class MailThread(models.AbstractModel):
|
||||
])
|
||||
# Cases ('message_is_follower', '=', True) or ('message_is_follower', '!=', False)
|
||||
if (operator == '=' and operand) or (operator == '!=' and not operand):
|
||||
return [('id', 'in', followers.mapped('res_id'))]
|
||||
# using read() below is much faster than followers.mapped('res_id')
|
||||
return [('id', 'in', [res['res_id'] for res in followers.read(['res_id'])])]
|
||||
else:
|
||||
return [('id', 'not in', followers.mapped('res_id'))]
|
||||
# using read() below is much faster than followers.mapped('res_id')
|
||||
return [('id', 'not in', [res['res_id'] for res in followers.read(['res_id'])])]
|
||||
|
||||
@api.multi
|
||||
def _get_message_unread(self):
|
||||
@ -1373,7 +1378,13 @@ class MailThread(models.AbstractModel):
|
||||
located in tools. """
|
||||
if not body:
|
||||
return body, attachments
|
||||
root = lxml.html.fromstring(body)
|
||||
try:
|
||||
root = lxml.html.fromstring(body)
|
||||
except ValueError:
|
||||
# In case the email client sent XHTML, fromstring will fail because 'Unicode strings
|
||||
# with encoding declaration are not supported'.
|
||||
root = lxml.html.fromstring(body.encode('utf-8'))
|
||||
|
||||
postprocessed = False
|
||||
to_remove = []
|
||||
for node in root.iter():
|
||||
|
@ -58,6 +58,8 @@ class Users(models.Model):
|
||||
|
||||
# create a welcome message
|
||||
user._create_welcome_message()
|
||||
# Auto-subscribe to channels
|
||||
self.env['mail.channel'].search([('group_ids', 'in', user.groups_id.ids)])._subscribe_users()
|
||||
return user
|
||||
|
||||
@api.multi
|
||||
@ -127,16 +129,19 @@ class Users(models.Model):
|
||||
def activity_user_count(self):
|
||||
query = """SELECT m.id, count(*), act.res_model as model,
|
||||
CASE
|
||||
WHEN now()::date - act.date_deadline::date = 0 Then 'today'
|
||||
WHEN now()::date - act.date_deadline::date > 0 Then 'overdue'
|
||||
WHEN now()::date - act.date_deadline::date < 0 Then 'planned'
|
||||
WHEN %(today)s::date - act.date_deadline::date = 0 Then 'today'
|
||||
WHEN %(today)s::date - act.date_deadline::date > 0 Then 'overdue'
|
||||
WHEN %(today)s::date - act.date_deadline::date < 0 Then 'planned'
|
||||
END AS states
|
||||
FROM mail_activity AS act
|
||||
JOIN ir_model AS m ON act.res_model_id = m.id
|
||||
WHERE user_id = %s
|
||||
WHERE user_id = %(user_id)s
|
||||
GROUP BY m.id, states, act.res_model;
|
||||
"""
|
||||
self.env.cr.execute(query, [self.env.uid])
|
||||
self.env.cr.execute(query, {
|
||||
'today': fields.Date.context_today(self),
|
||||
'user_id': self.env.uid,
|
||||
})
|
||||
activity_data = self.env.cr.dictfetchall()
|
||||
model_ids = [a['id'] for a in activity_data]
|
||||
model_names = {n[0]:n[1] for n in self.env['ir.model'].browse(model_ids).name_get()}
|
||||
|
@ -32,6 +32,7 @@ function _readActivities(self, ids) {
|
||||
model: 'mail.activity',
|
||||
method: 'read',
|
||||
args: [ids],
|
||||
context: (self.record && self.record.getContext()) || self.getSession().user_context,
|
||||
}).then(function (activities) {
|
||||
// convert create_date and date_deadline to moments
|
||||
_.each(activities, function (activity) {
|
||||
@ -108,6 +109,7 @@ var AbstractActivityField = AbstractField.extend({
|
||||
method: 'action_feedback',
|
||||
args: [[id]],
|
||||
kwargs: {feedback: feedback},
|
||||
context: this.record.getContext(),
|
||||
});
|
||||
},
|
||||
_scheduleActivity: function (id, previous_activity_type_id, callback) {
|
||||
|
@ -149,7 +149,16 @@ function make_message (data) {
|
||||
_.each(_.keys(emoji_substitutions), function (key) {
|
||||
var escaped_key = String(key).replace(/([.*+?=^!:${}()|[\]\/\\])/g, '\\$1');
|
||||
var regexp = new RegExp("(?:^|\\s|<[a-z]*>)(" + escaped_key + ")(?=\\s|$|</[a-z]*>)", "g");
|
||||
var msg_bak = msg.body;
|
||||
msg.body = msg.body.replace(regexp, ' <span class="o_mail_emoji">'+emoji_substitutions[key]+'</span> ');
|
||||
|
||||
// Idiot-proof limit. If the user had the amazing idea of copy-pasting thousands of emojis,
|
||||
// the image rendering can lead to memory overflow errors on some browsers (e.g. Chrome).
|
||||
// Set an arbitrary limit to 200 from which we simply don't replace them (anyway, they are
|
||||
// already replaced by the unicode counterpart).
|
||||
if (_.str.count(msg.body, 'o_mail_emoji') > 200) {
|
||||
msg.body = msg_bak;
|
||||
}
|
||||
});
|
||||
|
||||
function property_descr(channel) {
|
||||
|
@ -16,7 +16,8 @@ var HEIGHT_FOLDED = '34px';
|
||||
return Widget.extend({
|
||||
template: "mail.ChatWindow",
|
||||
custom_events: {
|
||||
escape_pressed: '_onEscapePressed'
|
||||
escape_pressed: '_onEscapePressed',
|
||||
document_viewer_closed: '_onDocumentViewerClose',
|
||||
},
|
||||
events: {
|
||||
'click .o_chat_composer': '_onComposerClick',
|
||||
@ -180,6 +181,9 @@ return Widget.extend({
|
||||
}
|
||||
this.focus_input();
|
||||
},
|
||||
_onDocumentViewerClose: function (ev) {
|
||||
this.focus_input();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
@ -514,6 +514,9 @@ var BasicComposer = Widget.extend(chat_mixin, {
|
||||
on_click_add_attachment: function () {
|
||||
this.$('input.o_input_file').click();
|
||||
this.$input.focus();
|
||||
// set ignoreEscape to avoid escape_pressed event when file selector dialog is opened
|
||||
// when user press escape to cancel file selector dialog then escape_pressed event should not be trigerred
|
||||
this.ignoreEscape = true;
|
||||
},
|
||||
|
||||
setState: function (state) {
|
||||
@ -565,6 +568,8 @@ var BasicComposer = Widget.extend(chat_mixin, {
|
||||
if (this.mention_manager.is_open()) {
|
||||
event.stopPropagation();
|
||||
this.mention_manager.reset_suggestions();
|
||||
} else if (this.ignoreEscape) {
|
||||
this.ignoreEscape = false;
|
||||
} else {
|
||||
this.trigger_up("escape_pressed");
|
||||
}
|
||||
@ -789,6 +794,7 @@ var BasicComposer = Widget.extend(chat_mixin, {
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
_onAttachmentView: function (event) {
|
||||
event.stopPropagation();
|
||||
var activeAttachmentID = $(event.currentTarget).data('id');
|
||||
var attachments = this.get('attachment_ids');
|
||||
if (activeAttachmentID) {
|
||||
|
@ -25,6 +25,7 @@ var DocumentViewer = Widget.extend({
|
||||
'DOMMouseScroll .o_viewer_content': '_onScroll', // Firefox
|
||||
'mousewheel .o_viewer_content': '_onScroll', // Chrome, Safari, IE
|
||||
'keydown': '_onKeydown',
|
||||
'keyup': '_onKeyUp',
|
||||
'mousedown .o_viewer_img': '_onStartDrag',
|
||||
'mousemove .o_viewer_content': '_onDrag',
|
||||
'mouseup .o_viewer_content': '_onEndDrag'
|
||||
@ -152,6 +153,7 @@ var DocumentViewer = Widget.extend({
|
||||
_onClose: function (e) {
|
||||
e.preventDefault();
|
||||
this.$el.modal('hide');
|
||||
this.trigger_up('document_viewer_closed');
|
||||
},
|
||||
/**
|
||||
* When popup close complete destroyed modal even DOM footprint too
|
||||
@ -232,6 +234,20 @@ var DocumentViewer = Widget.extend({
|
||||
break;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Close popup on ESCAPE keyup
|
||||
*
|
||||
* @private
|
||||
* @param {KeyEvent} e
|
||||
*/
|
||||
_onKeyUp: function (e) {
|
||||
switch (e.which) {
|
||||
case $.ui.keyCode.ESCAPE:
|
||||
e.preventDefault();
|
||||
this._onClose(e);
|
||||
break;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {MouseEvent} e
|
||||
|
@ -122,7 +122,7 @@ var MessagingMenu = Widget.extend({
|
||||
if (channelID === 'channel_inbox') {
|
||||
var resID = $(event.currentTarget).data('res_id');
|
||||
var resModel = $(event.currentTarget).data('res_model');
|
||||
if (resModel && resID) {
|
||||
if (resModel && resModel !== 'mail.channel' && resID) {
|
||||
this.do_action({
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: resModel,
|
||||
@ -130,7 +130,11 @@ var MessagingMenu = Widget.extend({
|
||||
res_id: resID
|
||||
});
|
||||
} else {
|
||||
this.do_action('mail.mail_channel_action_client_chat', {clear_breadcrumbs: true})
|
||||
var clientChatOptions = {clear_breadcrumbs: true};
|
||||
if (resModel && resModel === 'mail.channel' && resID) {
|
||||
clientChatOptions.active_id = resID;
|
||||
}
|
||||
this.do_action('mail.mail_channel_action_client_chat', clientChatOptions)
|
||||
.then(function () {
|
||||
self.trigger_up('hide_app_switcher');
|
||||
core.bus.trigger('change_menu_section', chat_manager.get_discuss_menu_id());
|
||||
|
@ -328,6 +328,7 @@ var Thread = Widget.extend({
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
_onAttachmentView: function (event) {
|
||||
event.stopPropagation();
|
||||
var activeAttachmentID = $(event.currentTarget).data('id');
|
||||
if (activeAttachmentID) {
|
||||
var attachmentViewer = new DocumentViewer(this, this.attachments, activeAttachmentID);
|
||||
|
@ -60,7 +60,7 @@ function _parse_and_transform(nodes, transform_function) {
|
||||
|
||||
// Suggested URL Javascript regex of http://stackoverflow.com/questions/3809401/what-is-a-good-regular-expression-to-match-a-url
|
||||
// Adapted to make http(s):// not required if (and only if) www. is given. So `should.notmatch` does not match.
|
||||
var url_regexp = /\b(?:https?:\/\/|(www\.))[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,13}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/gi;
|
||||
var url_regexp = /\b(?:https?:\/\/\d{1,3}(?:\.\d{1,3}){3}|(?:https?:\/\/|(?:www\.))[-a-z0-9@:%._\+~#=]{2,256}\.[a-z]{2,13})\b(?:[-a-z0-9@:%_\+.~#?&'$//=]*)/gi;
|
||||
function linkify(text, attrs) {
|
||||
attrs = attrs || {};
|
||||
if (attrs.target === undefined) {
|
||||
@ -70,7 +70,7 @@ function linkify(text, attrs) {
|
||||
return key + '="' + _.escape(value) + '"';
|
||||
}).join(' ');
|
||||
return text.replace(url_regexp, function (url) {
|
||||
var href = (!/^(f|ht)tps?:\/\//i.test(url)) ? "http://" + url : url;
|
||||
var href = (!/^https?:\/\//i.test(url)) ? "http://" + url : url;
|
||||
return '<a ' + attrs + ' href="' + href + '">' + url + '</a>';
|
||||
});
|
||||
}
|
||||
@ -92,6 +92,7 @@ function strip_html (node, transform_children) {
|
||||
|
||||
function inline (node, transform_children) {
|
||||
if (node.nodeType === 3) return node.data;
|
||||
if (node.nodeType === 8) return "";
|
||||
if (node.tagName === "BR") return " ";
|
||||
if (node.tagName.match(/^(A|P|DIV|PRE|BLOCKQUOTE)$/)) return transform_children();
|
||||
node.innerHTML = transform_children();
|
||||
@ -100,16 +101,23 @@ function inline (node, transform_children) {
|
||||
|
||||
// Parses text to find email: Tagada <address@mail.fr> -> [Tagada, address@mail.fr] or False
|
||||
function parse_email (text) {
|
||||
var result = text.match(/(.*)<(.*@.*)>/);
|
||||
if (result) {
|
||||
return [_.str.trim(result[1]), _.str.trim(result[2])];
|
||||
if (text){
|
||||
var result = text.match(/(.*)<(.*@.*)>/);
|
||||
if (result) {
|
||||
return [_.str.trim(result[1]), _.str.trim(result[2])];
|
||||
}
|
||||
result = text.match(/(.*@.*)/);
|
||||
if (result) {
|
||||
return [_.str.trim(result[1]), _.str.trim(result[1])];
|
||||
}
|
||||
return [text, false];
|
||||
}
|
||||
result = text.match(/(.*@.*)/);
|
||||
/* result = text.match(/(.*@.*)/);
|
||||
if (result) {
|
||||
return [_.str.trim(result[1]), _.str.trim(result[1])];
|
||||
}
|
||||
return [text, false];
|
||||
}
|
||||
}*/
|
||||
|
||||
// Replaces textarea text into html text (add <p>, <a>)
|
||||
// TDE note : should be done server-side, in Python -> use mail.compose.message ?
|
||||
|
@ -51,7 +51,7 @@
|
||||
border: none;
|
||||
}
|
||||
.o_composer_button_full_composer {
|
||||
.o-position-absolute(0, 0);
|
||||
.o-position-absolute(auto, 0);
|
||||
}
|
||||
@media (max-width: @screen-xs-max) {
|
||||
.o_composer_button_send {
|
||||
@ -136,7 +136,7 @@
|
||||
|
||||
&.o_chat_inline_composer {
|
||||
.o_composer_container {
|
||||
.o-flex(1, 0, auto);
|
||||
.o-flex(1, 1, auto);
|
||||
}
|
||||
.o_composer {
|
||||
padding: @o-mail-chatter-gap @o-mail-chatter-gap 0 @o-mail-chatter-gap;
|
||||
@ -339,6 +339,7 @@
|
||||
}
|
||||
|
||||
.o_modal_fullscreen {
|
||||
z-index: @o-chat-window-zindex + 1; // To overlap chat window
|
||||
.o_viewer_content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -107,7 +107,7 @@
|
||||
&.o_mail_note {
|
||||
background-color: @mail-thread-note;
|
||||
padding-left: @grid-gutter-width*0.3;
|
||||
border-bottom: 1px solid @gray-lighter-dark;
|
||||
border-bottom: 1px solid @gray-lighter-darker;
|
||||
}
|
||||
|
||||
.o_mail_subject {
|
||||
|
@ -76,7 +76,7 @@
|
||||
</t>
|
||||
|
||||
<t t-name="DocumentViewer">
|
||||
<div class="modal o_modal_fullscreen" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal o_modal_fullscreen" tabindex="-1" data-keyboard="false" role="dialog" aria-hidden="true">
|
||||
<t t-call="DocumentViewer.Content"/>
|
||||
|
||||
<t t-if="widget.attachment.length != 1">
|
||||
|
@ -50,6 +50,84 @@ QUnit.module('mail', {}, function () {
|
||||
parent.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('open document viewer and close using ESCAPE key should reset focus to chat window', function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
function createParent(params) {
|
||||
var widget = new Widget();
|
||||
|
||||
testUtils.addMockEnvironment(widget, params);
|
||||
return widget;
|
||||
}
|
||||
var messages = [{
|
||||
attachment_ids: [{
|
||||
filename: 'image1.jpg',
|
||||
id:1,
|
||||
mimetype: 'image/jpeg',
|
||||
name: 'Test Image 1',
|
||||
url: '/web/content/1?download=true'
|
||||
}],
|
||||
author_id: ["1", "John Doe"],
|
||||
body: "A message",
|
||||
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 parent = createParent({
|
||||
mockRPC: function (route, args) {
|
||||
if(_.str.contains(route, '/mail/attachment/preview/') ||
|
||||
_.str.contains(route, '/web/static/lib/pdfjs/web/viewer.html')){
|
||||
var canvas = document.createElement('canvas');
|
||||
return $.when(canvas.toDataURL());
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
data: {},
|
||||
});
|
||||
|
||||
var chatWindow = new ChatWindow(parent, 1, "user", false, messages.length, {});
|
||||
chatWindow.appendTo($('#qunit-fixture'));
|
||||
chatWindow.render(messages);
|
||||
|
||||
testUtils.intercept(chatWindow, '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));
|
||||
}, true);
|
||||
|
||||
testUtils.intercept(chatWindow, 'get_bus', function(event) {
|
||||
event.stopPropagation();
|
||||
event.data.callback(new Bus());
|
||||
}, true);
|
||||
|
||||
chatWindow.on('document_viewer_closed', null, function () {
|
||||
assert.ok(true, "chat window should trigger a close document viewer event");
|
||||
});
|
||||
assert.strictEqual(chatWindow.$('.o_thread_message .o_attachment').length, 1,
|
||||
"there should be three attachment on message");
|
||||
// click on first image attachement
|
||||
chatWindow.$('.o_thread_message .o_attachment .o_image_box .o_image_overlay').first().click();
|
||||
// check focus is on document viewer popup and then press escape to close it
|
||||
assert.strictEqual(document.activeElement, $('.o_modal_fullscreen')[0], "Modal popup should have focus");
|
||||
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");
|
||||
// trigger ESCAPE keyup on document viewer popup
|
||||
var upKeyEvent = jQuery.Event("keyup", {which: 27});
|
||||
$('.o_modal_fullscreen').trigger(upKeyEvent);
|
||||
assert.strictEqual(document.activeElement, chatWindow.$input[0],
|
||||
"input should be focused");
|
||||
var upKeyEvent = jQuery.Event( "keyup", {which: 27});
|
||||
chatWindow.$('.o_composer_input').trigger(upKeyEvent);
|
||||
assert.strictEqual(chatWindow.folded, false, "Closed chat Window");
|
||||
parent.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('chat window\'s input can still be focused when the UI is blocked', function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
|
@ -8,7 +8,7 @@ QUnit.module('mail', {}, function () {
|
||||
QUnit.module('Mail utils');
|
||||
|
||||
QUnit.test('add_link utility function', function (assert) {
|
||||
assert.expect(7);
|
||||
assert.expect(15);
|
||||
|
||||
var testInputs = {
|
||||
'http://admin:password@example.com:8/%2020': true,
|
||||
@ -24,6 +24,7 @@ QUnit.test('add_link utility function', function (assert) {
|
||||
var output = utils.parse_and_transform(content, utils.add_link);
|
||||
if (willLinkify) {
|
||||
assert.strictEqual(output.indexOf('<a '), 0, "There should be a link");
|
||||
assert.strictEqual(output.indexOf('</a>'), (output.length - 4), "Link should match the whole text");
|
||||
} else {
|
||||
assert.strictEqual(output.indexOf('<a '), -1, "There should be no link");
|
||||
}
|
||||
|
@ -366,6 +366,18 @@ class TestMailgateway(TestMail):
|
||||
self.assertEqual(res['body'], '')
|
||||
self.assertEqual(res['attachments'][0][0], 'thetruth.pdf')
|
||||
|
||||
@mute_logger('flectra.addons.mail.models.mail_thread')
|
||||
def test_message_parse_eml(self):
|
||||
""" Test that the parsing of mail with embedded emails as eml(msg) which generates empty attachments, can be processed.
|
||||
"""
|
||||
self.env['mail.thread'].message_process('mail.channel', MAIL_EML_ATTACHMENT)
|
||||
|
||||
@mute_logger('flectra.addons.mail.models.mail_thread')
|
||||
def test_message_parse_xhtml(self):
|
||||
""" Test that the parsing of mail with embedded emails as eml(msg) which generates empty attachments, can be processed.
|
||||
"""
|
||||
self.env['mail.thread'].message_process('mail.channel', MAIL_XHTML)
|
||||
|
||||
@mute_logger('flectra.addons.mail.models.mail_thread')
|
||||
def test_message_process_cid(self):
|
||||
new_groups = self.format_and_process(MAIL_MULTIPART_IMAGE, subject='My Frogs', to='groups@example.com')
|
||||
|
@ -23,7 +23,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6 o_setting_box" title="Using your own email server is required to send/receive emails in Community and Enterprise versions. Online users already benefit from a ready-to-use email server (@mycompany.flectra.com).">
|
||||
<div class="col-xs-12 col-md-6 o_setting_box" title="Using your own email server is required to send/receive emails in Community versions. Online users already benefit from a ready-to-use email server (@mycompany.flectra.com).">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="default_external_email_server"/>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user