diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py
index 8f072dd9082..1b9111d3040 100644
--- a/addons/mail/__openerp__.py
+++ b/addons/mail/__openerp__.py
@@ -83,7 +83,6 @@ Main Features
'static/src/css/mail_group.css',
],
'js': [
- 'static/lib/jquery.expander/jquery.expander.js',
'static/src/js/mail.js',
'static/src/js/mail_followers.js',
'static/src/js/many2many_tags_email.js',
diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py
index a5d8f348a2b..f90baa5a320 100644
--- a/addons/mail/mail_message.py
+++ b/addons/mail/mail_message.py
@@ -395,15 +395,21 @@ class mail_message(osv.Model):
has_voted = uid in [user.id for user in message.vote_user_ids]
try:
- body_html = html_email_clean(message.body)
+ if parent_id:
+ max_length = 300
+ else:
+ max_length = 100
+ body_short = html_email_clean(message.body, remove=False, shorten=True, max_length=max_length)
+
except Exception:
- body_html = '
Encoding Error :
Unable to convert this message (id: %s).
' % message.id
+ body_short = 'Encoding Error :
Unable to convert this message (id: %s).
' % message.id
_logger.exception(Exception)
return {'id': message.id,
'type': message.type,
'subtype': message.subtype_id.name if message.subtype_id else False,
- 'body': body_html,
+ 'body': message.body,
+ 'body_short': body_short,
'model': message.model,
'res_id': message.res_id,
'record_name': message.record_name,
diff --git a/addons/mail/static/lib/jquery.expander/jquery.expander.js b/addons/mail/static/lib/jquery.expander/jquery.expander.js
deleted file mode 100644
index 91cc03db918..00000000000
--- a/addons/mail/static/lib/jquery.expander/jquery.expander.js
+++ /dev/null
@@ -1,385 +0,0 @@
-/*!
- * jQuery Expander Plugin v1.4.2
- *
- * Date: Fri Mar 16 14:29:56 2012 EDT
- * Requires: jQuery v1.3+
- *
- * Copyright 2011, Karl Swedberg
- * Dual licensed under the MIT and GPL licenses (just like jQuery):
- * http://www.opensource.org/licenses/mit-license.php
- * http://www.gnu.org/licenses/gpl.html
- *
- *
- *
- *
-*/
-
-(function($) {
- $.expander = {
- version: '1.4.2',
- defaults: {
- // the number of characters at which the contents will be sliced into two parts.
- slicePoint: 100,
-
- // whether to keep the last word of the summary whole (true) or let it slice in the middle of a word (false)
- preserveWords: true,
-
- // a threshold of sorts for whether to initially hide/collapse part of the element's contents.
- // If after slicing the contents in two there are fewer words in the second part than
- // the value set by widow, we won't bother hiding/collapsing anything.
- widow: 4,
-
- // text displayed in a link instead of the hidden part of the element.
- // clicking this will expand/show the hidden/collapsed text
- expandText: 'read more',
- expandPrefix: '… ',
-
- expandAfterSummary: false,
-
- // class names for summary element and detail element
- summaryClass: 'summary',
- detailClass: 'details',
-
- // class names for around "read-more" link and "read-less" link
- moreClass: 'read-more',
- lessClass: 'read-less',
-
- // number of milliseconds after text has been expanded at which to collapse the text again.
- // when 0, no auto-collapsing
- collapseTimer: 0,
-
- // effects for expanding and collapsing
- expandEffect: 'fadeIn',
- expandSpeed: 250,
- collapseEffect: 'fadeOut',
- collapseSpeed: 200,
-
- // allow the user to re-collapse the expanded text.
- userCollapse: true,
-
- // text to use for the link to re-collapse the text
- userCollapseText: 'read less',
- userCollapsePrefix: ' ',
-
-
- // all callback functions have the this keyword mapped to the element in the jQuery set when .expander() is called
-
- onSlice: null, // function() {}
- beforeExpand: null, // function() {},
- afterExpand: null, // function() {},
- onCollapse: null // function(byUser) {}
- }
- };
-
- $.fn.expander = function(options) {
- var meth = 'init';
-
- if (typeof options == 'string') {
- meth = options;
- options = {};
- }
-
- var opts = $.extend({}, $.expander.defaults, options),
- rSelfClose = /^<(?:area|br|col|embed|hr|img|input|link|meta|param).*>$/i,
- rAmpWordEnd = opts.wordEnd || /(&(?:[^;]+;)?|[a-zA-Z\u00C0-\u0100]+)$/,
- rOpenCloseTag = /<\/?(\w+)[^>]*>/g,
- rOpenTag = /<(\w+)[^>]*>/g,
- rCloseTag = /<\/(\w+)>/g,
- rLastCloseTag = /(<\/[^>]+>)\s*$/,
- rTagPlus = /^<[^>]+>.?/,
- delayedCollapse;
-
- var methods = {
- init: function() {
- this.each(function() {
- var i, l, tmp, newChar, summTagless, summOpens, summCloses,
- lastCloseTag, detailText, detailTagless,
- $thisDetails, $readMore,
- openTagsForDetails = [],
- closeTagsForsummaryText = [],
- defined = {},
- thisEl = this,
- $this = $(this),
- $summEl = $([]),
- o = $.meta ? $.extend({}, opts, $this.data()) : opts,
- hasDetails = !!$this.find('.' + o.detailClass).length,
- hasBlocks = !!$this.find('*').filter(function() {
- var display = $(this).css('display');
- return (/^block|table|list/).test(display);
- }).length,
- el = hasBlocks ? 'div' : 'span',
- detailSelector = el + '.' + o.detailClass,
- moreSelector = 'span.' + o.moreClass,
- expandSpeed = o.expandSpeed || 0,
- allHtml = $.trim( $this.html() ),
- allText = $.trim( $this.text() ),
- summaryText = allHtml.slice(0, o.slicePoint);
-
- // bail out if we've already set up the expander on this element
- if ( $.data(this, 'expander') ) {
- return;
- }
-
- $.data(this, 'expander', true);
-
- // determine which callback functions are defined
- $.each(['onSlice','beforeExpand', 'afterExpand', 'onCollapse'], function(index, val) {
- defined[val] = $.isFunction(o[val]);
- });
-
- // back up if we're in the middle of a tag or word
- summaryText = backup(summaryText);
-
- // summary text sans tags length
- summTagless = summaryText.replace(rOpenCloseTag, '').length;
-
- // add more characters to the summary, one for each character in the tags
- while (summTagless < o.slicePoint) {
- newChar = allHtml.charAt(summaryText.length);
- if (newChar == '<') {
- newChar = allHtml.slice(summaryText.length).match(rTagPlus)[0];
- }
- summaryText += newChar;
- summTagless++;
- }
-
- summaryText = backup(summaryText, o.preserveWords);
-
- // separate open tags from close tags and clean up the lists
- summOpens = summaryText.match(rOpenTag) || [];
- summCloses = summaryText.match(rCloseTag) || [];
-
- // filter out self-closing tags
- tmp = [];
- $.each(summOpens, function(index, val) {
- if ( !rSelfClose.test(val) ) {
- tmp.push(val);
- }
- });
- summOpens = tmp;
-
- // strip close tags to just the tag name
- l = summCloses.length;
- for (i = 0; i < l; i++) {
- summCloses[i] = summCloses[i].replace(rCloseTag, '$1');
- }
-
- // tags that start in summary and end in detail need:
- // a). close tag at end of summary
- // b). open tag at beginning of detail
- $.each(summOpens, function(index, val) {
- var thisTagName = val.replace(rOpenTag, '$1');
- var closePosition = $.inArray(thisTagName, summCloses);
- if (closePosition === -1) {
- openTagsForDetails.push(val);
- closeTagsForsummaryText.push('' + thisTagName + '>');
-
- } else {
- summCloses.splice(closePosition, 1);
- }
- });
-
- // reverse the order of the close tags for the summary so they line up right
- closeTagsForsummaryText.reverse();
-
- // create necessary summary and detail elements if they don't already exist
- if ( !hasDetails ) {
-
- // end script if there is no detail text or if detail has fewer words than widow option
- detailText = allHtml.slice(summaryText.length);
- detailTagless = $.trim( detailText.replace(rOpenCloseTag, '') );
-
- if ( detailTagless === '' || detailTagless.split(/\s+/).length < o.widow ) {
- return;
- }
- // otherwise, continue...
- lastCloseTag = closeTagsForsummaryText.pop() || '';
- summaryText += closeTagsForsummaryText.join('');
- detailText = openTagsForDetails.join('') + detailText;
-
- } else {
- // assume that even if there are details, we still need readMore/readLess/summary elements
- // (we already bailed out earlier when readMore el was found)
- // but we need to create els differently
-
- // remove the detail from the rest of the content
- detailText = $this.find(detailSelector).remove().html();
-
- // The summary is what's left
- summaryText = $this.html();
-
- // allHtml is the summary and detail combined (this is needed when content has block-level elements)
- allHtml = summaryText + detailText;
-
- lastCloseTag = '';
- }
- o.moreLabel = $this.find(moreSelector).length ? '' : buildMoreLabel(o);
-
- if (hasBlocks) {
- detailText = allHtml;
- }
- summaryText += lastCloseTag;
-
- // onSlice callback
- o.summary = summaryText;
- o.details = detailText;
- o.lastCloseTag = lastCloseTag;
-
- if (defined.onSlice) {
- // user can choose to return a modified options object
- // one last chance for user to change the options. sneaky, huh?
- // but could be tricky so use at your own risk.
- tmp = o.onSlice.call(thisEl, o);
-
- // so, if the returned value from the onSlice function is an object with a details property, we'll use that!
- o = tmp && tmp.details ? tmp : o;
- }
-
- // build the html with summary and detail and use it to replace old contents
- var html = buildHTML(o, hasBlocks);
-
- $this.html( html );
-
- // set up details and summary for expanding/collapsing
- $thisDetails = $this.find(detailSelector);
- $readMore = $this.find(moreSelector);
- $thisDetails.hide();
- $readMore.find('a').unbind('click.expander').bind('click.expander', expand);
-
- $summEl = $this.find('div.' + o.summaryClass);
-
- if ( o.userCollapse && !$this.find('span.' + o.lessClass).length ) {
- $this
- .find(detailSelector)
- .append('' + o.userCollapsePrefix + '' + o.userCollapseText + '');
- }
-
- $this
- .find('span.' + o.lessClass + ' a')
- .unbind('click.expander')
- .bind('click.expander', function(event) {
- event.preventDefault();
- clearTimeout(delayedCollapse);
- var $detailsCollapsed = $(this).closest(detailSelector);
- reCollapse(o, $detailsCollapsed);
- if (defined.onCollapse) {
- o.onCollapse.call(thisEl, true);
- }
- });
-
- function expand(event) {
- event.preventDefault();
- $readMore.hide();
- $summEl.hide();
- if (defined.beforeExpand) {
- o.beforeExpand.call(thisEl);
- }
-
- $thisDetails.stop(false, true)[o.expandEffect](expandSpeed, function() {
- $thisDetails.css({zoom: ''});
- if (defined.afterExpand) {o.afterExpand.call(thisEl);}
- delayCollapse(o, $thisDetails, thisEl);
- });
- }
-
- }); // this.each
- },
- destroy: function() {
- if ( !this.data('expander') ) {
- return;
- }
- this.removeData('expander');
- this.each(function() {
- var $this = $(this),
- o = $.meta ? $.extend({}, opts, $this.data()) : opts,
- details = $this.find('.' + o.detailClass).contents();
-
- $this.find('.' + o.moreClass).remove();
- $this.find('.' + o.summaryClass).remove();
- $this.find('.' + o.detailClass).after(details).remove();
- $this.find('.' + o.lessClass).remove();
-
- });
- }
- };
-
- // run the methods (almost always "init")
- if ( methods[meth] ) {
- methods[ meth ].call(this);
- }
-
- // utility functions
- function buildHTML(o, blocks) {
- var el = 'span',
- summary = o.summary;
- if ( blocks ) {
- el = 'div';
- // if summary ends with a close tag, tuck the moreLabel inside it
- if ( rLastCloseTag.test(summary) && !o.expandAfterSummary) {
- summary = summary.replace(rLastCloseTag, o.moreLabel + '$1');
- } else {
- // otherwise (e.g. if ends with self-closing tag) just add moreLabel after summary
- // fixes #19
- summary += o.moreLabel;
- }
-
- // and wrap it in a div
- summary = '' + summary + '
';
- } else {
- summary += o.moreLabel;
- }
-
- return [
- summary,
- '<',
- el + ' class="' + o.detailClass + '"',
- '>',
- o.details,
- '' + el + '>'
- ].join('');
- }
-
- function buildMoreLabel(o) {
- var ret = '' + o.expandPrefix;
- ret += '' + o.expandText + '';
- return ret;
- }
-
- function backup(txt, preserveWords) {
- if ( txt.lastIndexOf('<') > txt.lastIndexOf('>') ) {
- txt = txt.slice( 0, txt.lastIndexOf('<') );
- }
- if (preserveWords) {
- txt = txt.replace(rAmpWordEnd,'');
- }
-
- return $.trim(txt);
- }
-
- function reCollapse(o, el) {
- el.stop(true, true)[o.collapseEffect](o.collapseSpeed, function() {
- var prevMore = el.prev('span.' + o.moreClass).show();
- if (!prevMore.length) {
- el.parent().children('div.' + o.summaryClass).show()
- .find('span.' + o.moreClass).show();
- }
- });
- }
-
- function delayCollapse(option, $collapseEl, thisEl) {
- if (option.collapseTimer) {
- delayedCollapse = setTimeout(function() {
- reCollapse(option, $collapseEl);
- if ( $.isFunction(option.onCollapse) ) {
- option.onCollapse.call(thisEl, false);
- }
- }, option.collapseTimer);
- }
- }
-
- return this;
- };
-
- // plugin defaults
- $.fn.expander.defaults = $.expander.defaults;
-})(jQuery);
diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css
index c6f7c911f8a..a4beca607ba 100644
--- a/addons/mail/static/src/css/mail.css
+++ b/addons/mail/static/src/css/mail.css
@@ -118,6 +118,9 @@
text-overflow:ellipsis;
word-wrap: break-word;
}
+.openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body .oe_mail_cleaned {
+ display: none;
+}
/* a) Indented Messages */
diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js
index d0af3127404..62e9241cf4f 100644
--- a/addons/mail/static/src/js/mail.js
+++ b/addons/mail/static/src/js/mail.js
@@ -225,6 +225,7 @@ openerp.mail = function (session) {
this.name = datasets.name || false,
this.record_name = datasets.record_name || false,
this.body = datasets.body || '',
+ this.body_short = datasets.body_short || '',
this.vote_nb = datasets.vote_nb || 0,
this.has_voted = datasets.has_voted || false,
this.is_favorite = datasets.is_favorite || false,
@@ -944,7 +945,6 @@ openerp.mail = function (session) {
start: function () {
this._super.apply(this, arguments);
- this.expender();
this.bind_events();
if(this.thread_level < this.options.display_indented_thread) {
this.create_thread();
@@ -967,6 +967,8 @@ openerp.mail = function (session) {
this.$('.oe_reply').on('click', this.on_message_reply);
this.$('.oe_star').on('click', this.on_star);
this.$('.oe_msg_vote').on('click', this.on_vote);
+ this.$('.oe_mail_expand').on('click', this.on_expand);
+ this.$('.oe_mail_reduce').on('click', this.on_expand);
this.$('.oe_mail_action_model').on('click', this.on_record_clicked);
},
@@ -995,15 +997,11 @@ openerp.mail = function (session) {
return false;
},
- expender: function () {
- this.$('.oe_msg_body:first').expander({
- slicePoint: this.options.truncate_limit,
- expandText: _t('read more'),
- userCollapseText: _t('read less'),
- detailClass: 'oe_msg_tail',
- moreClass: 'oe_mail_expand',
- lessClass: 'oe_mail_reduce',
- });
+ on_expand: function (event) {
+ event.stopPropagation();
+ this.$('.oe_msg_body_short:first').toggle();
+ this.$('.oe_msg_body_long:first').toggle();
+ return false;
},
/**
diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml
index 94ee1ff452f..1be9754e1c7 100644
--- a/addons/mail/static/src/xml/mail.xml
+++ b/addons/mail/static/src/xml/mail.xml
@@ -266,7 +266,13 @@