[IMP] mail: js, compose, expandable and message extend messageglobal

bzr revid: chm@openerp.com-20121031145610-e8m9l7lsmzzjru9k
This commit is contained in:
Christophe Matthieu 2012-10-31 15:56:10 +01:00
parent fd0b674f5c
commit 1bbc2c0063
4 changed files with 151 additions and 159 deletions

View File

@ -227,7 +227,7 @@ class mail_message(osv.Model):
is_private = False is_private = False
try: try:
attachment_ids = [{'id': attach[0], 'name': attach[1]} for attach in self.pool.get('ir.attachment').name_get(cr, uid, message['attachment_ids'], context=context)] attachment_ids = [{'id': attach['id'], 'filename': attach['datas_fname']} for attach in self.pool.get('ir.attachment').read(cr, uid, message['attachment_ids'], ['id', 'datas_fname'], context=context)]
except (orm.except_orm, osv.except_osv): except (orm.except_orm, osv.except_osv):
attachment_ids = [] attachment_ids = []

View File

@ -5,35 +5,45 @@
<field name="name">Inbox</field> <field name="name">Inbox</field>
<field name="tag">mail.wall</field> <field name="tag">mail.wall</field>
<field name="params" eval="&quot;{'domain': [('notification_ids.partner_id.user_ids', 'in', [uid]), ('to_read', '=', True)], <field name="params" eval="&quot;{'domain': [('notification_ids.partner_id.user_ids', 'in', [uid]), ('to_read', '=', True)],
'context': {'default_model': 'res.users', 'default_res_id': uid} }&quot;"/> 'context': {'default_model': 'res.users', 'default_res_id': uid, 'view_mailbox': True} }&quot;"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to define a new sales tag.
</p><p>
Create specific tags that fit your company's activities
to better classify and analyse your leads and opportunities.
Such categories could for instance reflect your product
structure or the different types of sales you do.
</p>
</field>
</record> </record>
<record id="action_mail_to_me_feeds" model="ir.actions.client"> <record id="action_mail_to_me_feeds" model="ir.actions.client">
<field name="name">To: me</field> <field name="name">To: me</field>
<field name="tag">mail.wall</field> <field name="tag">mail.wall</field>
<field name="params" eval="&quot;{'domain': [('partner_ids.user_ids', 'in', [uid]), ('to_read', '=', True), ('author_id.user_ids', 'in', [uid])], <field name="params" eval="&quot;{'domain': [('partner_ids.user_ids', 'in', [uid]), ('to_read', '=', True), ('author_id.user_ids', 'in', [uid])],
'context': {'default_model': 'res.users', 'default_res_id': uid} }&quot;"/> 'context': {'default_model': 'res.users', 'default_res_id': uid, 'view_mailbox': True} }&quot;"/>
</record> </record>
<record id="action_mail_star_feeds" model="ir.actions.client"> <record id="action_mail_star_feeds" model="ir.actions.client">
<field name="name">Favorites</field> <field name="name">Todo</field>
<field name="tag">mail.wall</field> <field name="tag">mail.wall</field>
<field name="params" eval="&quot;{'domain': [('favorite_user_ids.user_ids', 'in', [uid])], <field name="params" eval="&quot;{'domain': [('favorite_user_ids.user_ids', 'in', [uid])],
'context': {'default_model': 'res.users', 'default_res_id': uid} }&quot;"/> 'context': {'default_model': 'res.users', 'default_res_id': uid, 'view_mailbox': True} }&quot;"/>
</record> </record>
<record id="action_mail_archives_feeds" model="ir.actions.client"> <record id="action_mail_archives_feeds" model="ir.actions.client">
<field name="name">Archives</field> <field name="name">Done</field>
<field name="tag">mail.wall</field> <field name="tag">mail.wall</field>
<field name="params" eval="&quot;{'domain': [('notification_ids.partner_id.user_ids', 'in', [uid]), ('to_read', '=', False)], <field name="params" eval="&quot;{'domain': [('notification_ids.partner_id.user_ids', 'in', [uid]), ('to_read', '=', False)],
'context': {'default_model': 'res.users', 'default_res_id': uid} }&quot;"/> 'context': {'default_model': 'res.users', 'default_res_id': uid, 'view_mailbox': True} }&quot;"/>
</record> </record>
<record id="action_mail_sent_feeds" model="ir.actions.client"> <record id="action_mail_sent_feeds" model="ir.actions.client">
<field name="name">Sent</field> <field name="name">Sent</field>
<field name="tag">mail.wall</field> <field name="tag">mail.wall</field>
<field name="params" eval="&quot;{'domain': [('author_id.user_ids', 'in', [uid])], <field name="params" eval="&quot;{'domain': [('author_id.user_ids', 'in', [uid])],
'context': {'default_model': 'res.users', 'default_res_id': uid} }&quot;"/> 'context': {'default_model': 'res.users', 'default_res_id': uid, 'view_mailbox': True} }&quot;"/>
</record> </record>
<!-- MENU --> <!-- MENU -->
@ -61,13 +71,13 @@
<field name="parent_id" ref="mail.mail_feeds"/> <field name="parent_id" ref="mail.mail_feeds"/>
</record> </record>
<record id="mail_starfeeds" model="ir.ui.menu"> <record id="mail_starfeeds" model="ir.ui.menu">
<field name="name">Favorites</field> <field name="name">Todo</field>
<field name="sequence" eval="14"/> <field name="sequence" eval="14"/>
<field name="action" ref="action_mail_star_feeds"/> <field name="action" ref="action_mail_star_feeds"/>
<field name="parent_id" ref="mail.mail_feeds"/> <field name="parent_id" ref="mail.mail_feeds"/>
</record> </record>
<record id="mail_archivesfeeds" model="ir.ui.menu"> <record id="mail_archivesfeeds" model="ir.ui.menu">
<field name="name">Archives</field> <field name="name">Done</field>
<field name="sequence" eval="16"/> <field name="sequence" eval="16"/>
<field name="action" ref="action_mail_archives_feeds"/> <field name="action" ref="action_mail_archives_feeds"/>
<field name="parent_id" ref="mail.mail_feeds"/> <field name="parent_id" ref="mail.mail_feeds"/>

View File

@ -114,6 +114,88 @@ openerp.mail = function (session) {
}; };
mail.ThreadMessageGlobal = session.web.Widget.extend({
init: function (parent, datasets, options) {
this._super(parent, options);
// record options
this.options = datasets.options || options || {};
// record domain and context
this.domain = datasets.domain || options.domain || [];
this.context = _.extend({
default_model: 'mail.thread',
default_res_id: 0,
default_parent_id: false }, options.context || {});
// data of this message
this.id = datasets.id || -1,
this.last_id = this.id,
this.model = datasets.model || false,
this.res_model = datasets.res_model || false;
this.parent_id = datasets.parent_id || false,
this.res_id = datasets.res_id || false,
this.type = datasets.type || false,
this.is_author = datasets.is_author || false,
this.is_private = datasets.is_private || false,
this.subject = datasets.subject || false,
this.name = datasets.name || false,
this.record_name = datasets.record_name || false,
this.body = datasets.body || false,
this.vote_nb = datasets.vote_nb || 0,
this.has_voted = datasets.has_voted || false,
this.is_favorite = datasets.is_favorite || false,
this.thread_level = datasets.thread_level || 0,
this.to_read = datasets.to_read || false,
this.author_id = datasets.author_id || [this.session.uid],
this.attachment_ids = datasets.attachment_ids || [],
this.partner_ids = datasets.partner_ids || [];
this.nb_messages = datasets.nb_messages;
this._date = datasets.date;
this.formating_data();
// record options and data
this.show_record_name = this.record_name && !this.thread_level && this.model!='res.partner';
this.parent_thread= parent.messages!= undefined ? parent : this.options.root_thread;
this.thread = false;
},
/* Convert date, timerelative and avatar in displayable data. */
formating_data: function () {
//formating and add some fields for render
if (this._date) {
this.date = session.web.format_value(this._date, {type:"datetime"});
this.timerelative = $.timeago(this.date);
}
if (this.type == 'email') {
this.avatar = ('/mail/static/src/img/email_icon.png');
} else {
this.avatar = mail.ChatterUtils.get_image(this.session, 'res.partner', 'image_small', this.author_id[0]);
}
for (var l in this.attachment_ids) {
var attach = this.attachment_ids[l];
attach['url'] = mail.ChatterUtils.get_attachment_url(this.session, attach);
if (attach.filename.match(/[.](jpg|jpg|gif|png|tif|svg)$/i)) {
attach.is_image = true;
attach['url'] = mail.ChatterUtils.get_image(this.session, 'ir.attachment', 'datas', attach.id);
}
}
},
/**
* call on_message_delete on his parent thread
*/
destroy: function () {
this._super();
this.parent_thread.on_message_detroy(this);
}
});
/** /**
* ------------------------------------------------------------ * ------------------------------------------------------------
* ComposeMessage widget * ComposeMessage widget
@ -125,7 +207,7 @@ openerp.mail = function (session) {
* When the user focuses the textarea, the compose message is instantiated. * When the user focuses the textarea, the compose message is instantiated.
*/ */
mail.ThreadComposeMessage = session.web.Widget.extend({ mail.ThreadComposeMessage = mail.ThreadMessageGlobal.extend({
template: 'mail.compose_message', template: 'mail.compose_message',
/** /**
@ -137,33 +219,18 @@ openerp.mail = function (session) {
*/ */
init: function (parent, datasets, options) { init: function (parent, datasets, options) {
var self = this; this._super(parent, datasets, options);
this._super(parent);
this.context = options.context || {};
this.options = options.options;
this.show_compact_message = false; this.show_compact_message = false;
// data of this compose message
this.id = datasets.id;
this.model = datasets.model;
this.res_model = datasets.res_model;
this.is_private = datasets.is_private || false;
this.partner_ids = datasets.partner_ids || [];
this.thread_level = datasets.thread_level;
this.attachment_ids = [];
this.parent_thread= parent.messages!= undefined ? parent : false;
this.avatar = mail.ChatterUtils.get_image(this.session, 'res.users', 'image_small', this.session.uid);
this.ds_attachment = new session.web.DataSetSearch(this, 'ir.attachment');
this.show_delete_attachment = true; this.show_delete_attachment = true;
this.fileupload_id = _.uniqueId('oe_fileupload_temp');
$(window).on(self.fileupload_id, self.on_attachment_loaded);
}, },
start: function () { start: function () {
this._super.apply(this, arguments);
this.ds_attachment = new session.web.DataSetSearch(this, 'ir.attachment');
this.fileupload_id = _.uniqueId('oe_fileupload_temp');
$(window).on(this.fileupload_id, this.on_attachment_loaded);
this.display_attachments(); this.display_attachments();
this.bind_events(); this.bind_events();
}, },
@ -171,8 +238,7 @@ openerp.mail = function (session) {
/* upload the file on the server, add in the attachments list and reload display /* upload the file on the server, add in the attachments list and reload display
*/ */
display_attachments: function () { display_attachments: function () {
this.$(".oe_msg_attachment_list").html( this.$(".oe_msg_attachment_list").html( session.web.qweb.render('mail.thread.message.attachments', {'widget': this}) );
session.web.qweb.render('mail.thread.message.attachments', {'widget': this}) );
// event: delete an attachment // event: delete an attachment
this.$(".oe_msg_attachment_list").on('click', '.oe_mail_attachment_delete', this.on_attachment_delete); this.$(".oe_msg_attachment_list").on('click', '.oe_mail_attachment_delete', this.on_attachment_delete);
}, },
@ -424,30 +490,15 @@ openerp.mail = function (session) {
* - - visible message * - - visible message
* - - expandable * - - expandable
*/ */
mail.ThreadExpandable = session.web.Widget.extend({ mail.ThreadExpandable = mail.ThreadMessageGlobal.extend({
template: 'mail.thread.expandable', template: 'mail.thread.expandable',
init: function (parent, datasets, context) { init: function (parent, datasets, options) {
this._super(parent); this._super(parent, datasets, options);
this.domain = datasets.domain || []; this.type = 'expandable';
this.options = datasets.options; this.max_limit = this.id < 0 || false;
this.context = _.extend({ this.flag_used = false;
default_model: 'mail.thread',
default_res_id: 0,
default_parent_id: false }, context || {});
// data of this expandable message
this.id = datasets.id || -1,
this.model = datasets.model || false,
this.parent_id = datasets.parent_id || false,
this.nb_messages = datasets.nb_messages || 0,
this.thread_level = datasets.thread_level || 0,
this.type = 'expandable',
this.max_limit = this.id < 0 || false,
this.flag_used = false,
this.parent_thread= parent.messages!= undefined ? parent : this.options.root_thread;
}, },
start: function () { start: function () {
this._super.apply(this, arguments); this._super.apply(this, arguments);
@ -489,16 +540,6 @@ openerp.mail = function (session) {
this.parent_thread.message_fetch(this.domain, this.context); this.parent_thread.message_fetch(this.domain, this.context);
return false; return false;
}, },
/**
* call on_message_delete on his parent thread
*/
destroy: function () {
this._super();
this.parent_thread.on_message_detroy(this);
}
}); });
/** /**
@ -519,10 +560,11 @@ openerp.mail = function (session) {
* - - sub message (parent_id = root message) * - - sub message (parent_id = root message)
* - - - sub thread * - - - sub thread
*/ */
mail.ThreadMessage = session.web.Widget.extend({ mail.ThreadMessage = mail.ThreadMessageGlobal.extend({
template: 'mail.thread.message', template: 'mail.thread.message',
/** /**
* INIT :
* @param {Object} parent parent * @param {Object} parent parent
* @param {Array} [domain] * @param {Array} [domain]
* @param {Object} [context] context of the thread. It should * @param {Object} [context] context of the thread. It should
@ -538,71 +580,19 @@ openerp.mail = function (session) {
*... @param {boolean} [show_reply_button] display the reply button *... @param {boolean} [show_reply_button] display the reply button
*... @param {boolean} [show_read_unread_button] display the read/unread button *... @param {boolean} [show_read_unread_button] display the read/unread button
*/ */
init: function (parent, datasets, context) {
this._super(parent);
// record options
this.options = datasets.options || {};
// record domain and context
this.domain = datasets.domain || [];
this.context = _.extend({
default_model: 'mail.thread',
default_res_id: 0,
default_parent_id: false }, context || {});
// data of this message
for (var k in datasets) {
this[k] = datasets[k];
}
this._date = datasets.date;
this.show_record_name = this.record_name && !this.thread_level && this.model!='res.partner';
// record options and data
this.parent_thread= parent.messages!= undefined ? parent : this.options.root_thread;
this.thread = false;
if ( this.id > 0 ) {
this.formating_data();
}
this.ds_notification = new session.web.DataSetSearch(this, 'mail.notification');
this.ds_message = new session.web.DataSetSearch(this, 'mail.message');
this.ds_follow = new session.web.DataSetSearch(this, 'mail.followers');
},
/* Convert date, timerelative and avatar in displayable data. */
formating_data: function () {
//formating and add some fields for render
this.date = session.web.format_value(this._date, {type:"datetime"});
this.timerelative = $.timeago(this.date);
if (this.type == 'email') {
this.avatar = ('/mail/static/src/img/email_icon.png');
} else {
this.avatar = mail.ChatterUtils.get_image(this.session, 'res.partner', 'image_small', this.author_id[0]);
}
for (var l in this.attachment_ids) {
var attach = this.attachment_ids[l];
attach['url'] = mail.ChatterUtils.get_attachment_url(this.session, attach);
if ((attach.filename || attach.name).match(/[.](jpg|jpg|gif|png|tif|svg)$/i)) {
attach.is_image = true;
attach['url'] = mail.ChatterUtils.get_image(this.session, 'ir.attachment', 'datas', attach.id);
}
}
},
start: function () { start: function () {
this._super.apply(this, arguments); this._super.apply(this, arguments);
this.expender(); this.expender();
//this.$el.hide().fadeIn(750, function () {$(this).css('display', '');});
this.resize_img(); this.resize_img();
this.bind_events(); this.bind_events();
if(this.thread_level < this.options.display_indented_thread) { if(this.thread_level < this.options.display_indented_thread) {
this.create_thread(); this.create_thread();
} }
this.$('.oe_msg_attachments, .oe_msg_images').addClass("oe_hidden"); this.$('.oe_msg_attachments, .oe_msg_images').addClass("oe_hidden");
this.ds_notification = new session.web.DataSetSearch(this, 'mail.notification');
this.ds_message = new session.web.DataSetSearch(this, 'mail.message');
}, },
resize_img: function () { resize_img: function () {
@ -835,16 +825,6 @@ openerp.mail = function (session) {
return false; return false;
}, },
/**
* call on_message_delete on his parent thread
*/
destroy: function () {
this._super();
this.parent_thread.on_message_detroy(this);
}
}); });
/** /**
@ -879,7 +859,7 @@ openerp.mail = function (session) {
* use with browse, fetch... [O]= top parent * use with browse, fetch... [O]= top parent
*/ */
init: function (parent, datasets, options) { init: function (parent, datasets, options) {
this._super(parent); this._super(parent, options);
this.domain = options.domain || []; this.domain = options.domain || [];
this.context = _.extend({ this.context = _.extend({
default_model: 'mail.thread', default_model: 'mail.thread',
@ -1124,17 +1104,17 @@ openerp.mail = function (session) {
data.options = _.extend(self.options, data.options); data.options = _.extend(self.options, data.options);
if (data.type=='expandable') { if (data.type=='expandable') {
var message = new mail.ThreadExpandable(self, data, { var message = new mail.ThreadExpandable(self, data, {'context':{
'default_model': data.model || self.context.default_model, 'default_model': data.model || self.context.default_model,
'default_res_id': data.res_id || self.context.default_res_id, 'default_res_id': data.res_id || self.context.default_res_id,
'default_parent_id': self.id, 'default_parent_id': self.id,
}); }});
} else { } else {
var message = new mail.ThreadMessage(self, data, { var message = new mail.ThreadMessage(self, data, {'context':{
'default_model': data.model, 'default_model': data.model,
'default_res_id': data.res_id, 'default_res_id': data.res_id,
'default_parent_id': data.id, 'default_parent_id': data.id,
}); }});
} }
// check if the message is already create // check if the message is already create
@ -1160,7 +1140,7 @@ openerp.mail = function (session) {
insert_message: function (message, dom_insert_after) { insert_message: function (message, dom_insert_after) {
var self=this; var self=this;
if (this.options.show_compact_message) { if (this.options.show_compact_message > this.thread_level) {
this.instantiate_compose_message(); this.instantiate_compose_message();
this.compose_message.do_show_compact(); this.compose_message.do_show_compact();
} }
@ -1332,9 +1312,11 @@ openerp.mail = function (session) {
'domain': message_dom, 'domain': message_dom,
'options': message.options, 'options': message.options,
}, { }, {
'default_model': message.model || this.context.default_model, 'context':{
'default_res_id': message.res_id || this.context.default_res_id, 'default_model': message.model || this.context.default_model,
'default_parent_id': this.id, 'default_res_id': message.res_id || this.context.default_res_id,
'default_parent_id': this.id,
}
}); });
// add object on array and DOM // add object on array and DOM
@ -1489,7 +1471,7 @@ openerp.mail = function (session) {
} }
// create and render Thread widget // create and render Thread widget
this.root = new mail.Widget(this, { this.root = new mail.Widget(this, {
'domain' : (this.options.domain || []).concat([['model', '=', this.view.model], ['res_id', '=', this.view.datarecord.id]]), 'domain' : (this.domain || []).concat([['model', '=', this.view.model], ['res_id', '=', this.view.datarecord.id]]),
'context' : { 'context' : {
'default_res_id': this.view.datarecord.id || false, 'default_res_id': this.view.datarecord.id || false,
'default_model': this.view.model || 'mail.thread', 'default_model': this.view.model || 'mail.thread',
@ -1500,7 +1482,7 @@ openerp.mail = function (session) {
'show_read_unread_button': false, 'show_read_unread_button': false,
'show_compose_message': this.view.is_action_enabled('edit') || (this.getParent().fields.message_is_follower && this.getParent().fields.message_is_follower.get_value()), 'show_compose_message': this.view.is_action_enabled('edit') || (this.getParent().fields.message_is_follower && this.getParent().fields.message_is_follower.get_value()),
'message_ids': this.getParent().fields.message_ids ? this.getParent().fields.message_ids.get_value() : undefined, 'message_ids': this.getParent().fields.message_ids ? this.getParent().fields.message_ids.get_value() : undefined,
'show_compact_message': true, 'show_compact_message': 1,
'no_message': this.node.attrs.help 'no_message': this.node.attrs.help
} }
); );
@ -1532,24 +1514,24 @@ openerp.mail = function (session) {
*/ */
init: function (parent, options) { init: function (parent, options) {
this._super(parent); this._super(parent);
this.options = options || {}; this.domain = options.domain || [];
this.options.domain = options.domain || []; this.context = options.context || {};
this.options.context = options.context || {};
this.options.defaults = {}; this.defaults = {};
for (var key in options.context) { for (var key in options.context) {
if(key.match(/^search_default_/)) { if(key.match(/^search_default_/)) {
this.options.defaults[key.replace(/^search_default_/, '')] = options.context[key]; this.defaults[key.replace(/^search_default_/, '')] = options.context[key];
} }
} }
}, },
start: function () { start: function () {
this._super.apply(this); this._super.apply(this);
this.load_searchview(this.options.defaults, false); this.load_searchview(this.defaults, false);
this.bind_events(); this.bind_events();
if(!this.searchview.has_defaults) { if (!this.searchview.has_defaults) {
this.message_render(); this.message_render();
} }
}, },
@ -1598,8 +1580,8 @@ openerp.mail = function (session) {
*Create the root thread widget and display this object in the DOM *Create the root thread widget and display this object in the DOM
*/ */
message_render: function ( search ) { message_render: function ( search ) {
var domain = this.options.domain.concat(search && search['domain'] ? search['domain'] : []); var domain = this.domain.concat(search && search['domain'] ? search['domain'] : []);
var context = _.extend(this.options.context, search && search['context'] ? search['context'] : {}); var context = _.extend(this.context, search && search['context'] ? search['context'] : {});
this.root = new mail.Widget(this, { this.root = new mail.Widget(this, {
'domain' : domain, 'domain' : domain,
'context' : context, 'context' : context,
@ -1607,7 +1589,7 @@ openerp.mail = function (session) {
'show_reply_button': true, 'show_reply_button': true,
'show_read_unread_button': true, 'show_read_unread_button': true,
'show_compose_message': true, 'show_compose_message': true,
'show_compact_message': false, 'show_compact_message': this.context.view_mailbox ? false : 1,
} }
); );

View File

@ -38,7 +38,7 @@
</div> </div>
</div> </div>
<div t-if="widget.show_compact_message and !widget.show_composer" t-attf-class="oe_msg oe_msg_composer_compact #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''}"> <div t-if="widget.show_compact_message and !widget.show_composer" t-attf-class="oe_msg oe_msg_composer_compact #{widget.thread_level and widget.options.display_indented_thread > -1 ? 'oe_msg_indented' : ''}">
<textarea class="field_text oe_compact" placeholder="Write a reply..."/> <textarea class="field_text oe_compact" placeholder="Write to this group followers..."/>
</div> </div>
<span t-if="!(widget.show_compact_message and !widget.show_composer) and !widget.show_composer" class="oe_placeholder_compose"></span> <span t-if="!(widget.show_compact_message and !widget.show_composer) and !widget.show_composer" class="oe_placeholder_compose"></span>
</t> </t>
@ -72,18 +72,18 @@
<span class="oe_msg_attachments"> <span class="oe_msg_attachments">
<t t-foreach="widget.attachment_ids" t-as="attachment" t-if="!attachment.is_image"> <t t-foreach="widget.attachment_ids" t-as="attachment" t-if="!attachment.is_image">
<div class="oe_attachment"> <div class="oe_attachment">
<span t-if="(attachment.upload and attachment.percent_loaded&lt;100)" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}" t-attf-name="{attachment.name || attachment.filename}"> <span t-if="attachment.upload" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}" t-attf-name="{attachment.name || attachment.filename}">
<div class="oe_upload_in_process"> <div class="oe_upload_in_process">
<span>...Upload in progress...</span> <span>...Upload in progress...</span>
</div> </div>
<t t-raw="attachment.name || attachment.filename"/> <t t-raw="attachment.name || attachment.filename"/>
</span> </span>
<t t-if="(!attachment.upload or attachment.percent_loaded&gt;=100)"> <t t-if="!attachment.upload">
<a t-att-href="attachment.url" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}"> <a t-att-href="attachment.url" t-attf-title="{(attachment.name || attachment.filename) + (attachment.date?' \n('+attachment.date+')':'' )}">
<t t-raw="attachment.name || attachment.filename"/> <t t-raw="attachment.name || attachment.filename"/>
</a> </a>
</t> </t>
<t t-if="(widget.show_delete_attachment and (!attachment.upload or attachment.percent_loaded&gt;=100))"> <t t-if="(widget.show_delete_attachment and !attachment.upload)">
<a class="oe_right oe_mail_attachment_delete oe_e" title="Delete this attachment" t-attf-data-id="{attachment.id}">[</a> <a class="oe_right oe_mail_attachment_delete oe_e" title="Delete this attachment" t-attf-data-id="{attachment.id}">[</a>
</t> </t>
</div> </div>
@ -146,14 +146,14 @@
</td> </td>
<td><div class="oe_view_manager_view_search" t-opentag="true"/></td> <td><div class="oe_view_manager_view_search" t-opentag="true"/></td>
</tr> </tr>
<tr class="oe_header_row"> <tr class="oe_header_row" t-if="widget.context.view_mailbox" >
<td colspan="2"> <td colspan="2">
<button type="button" class="oe_write_full oe_highlight"> <button type="button" class="oe_write_full oe_highlight">
Compose a new message Compose a new message
</button> </button>
<span class='oe_alternative'> <span class='oe_alternative'>
or or
<a href='#' class='oe_write_onwall oe_bold' help='Your followers can read this message'>Write to your followers</a> <a href='#' class='oe_write_onwall oe_bold' help='Your followers can read this message'>Write on this document</a>
</span> </span>
</td> </td>
</tr> </tr>