From 85a807a5153d357e83a0f01d4940b5c6c2582403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 23 Apr 2013 17:36:45 +0200 Subject: [PATCH 1/3] [REF] Chatter: refactored system for read more / read less, now based on html_email_clean instead of the expander library. bzr revid: tde@openerp.com-20130423153645-k8vcb9hiuapzf7sg --- addons/mail/__openerp__.py | 1 - addons/mail/mail_message.py | 7 +- .../lib/jquery.expander/jquery.expander.js | 385 ------------------ addons/mail/static/src/js/mail.js | 16 +- addons/mail/static/src/xml/mail.xml | 8 +- 5 files changed, 17 insertions(+), 400 deletions(-) delete mode 100644 addons/mail/static/lib/jquery.expander/jquery.expander.js diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py index 63cd0888986..db0d8c8098a 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 defc12a2f73..ec6a8db526e 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -362,15 +362,16 @@ 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) + body_short = html_email_clean(message.body, remove_unwanted=True, use_max_length=True, max_length=50) 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(''); - - } 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, - '' - ].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/js/mail.js b/addons/mail/static/src/js/mail.js index b1a579ba057..5ab6d2380a0 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -224,6 +224,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, @@ -939,7 +940,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(); @@ -962,6 +962,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); }, /* Call the on_compose_message on the thread of this message. */ @@ -972,15 +974,9 @@ openerp.mail = function (session) { return false; }, - expender: function () { - this.$('.oe_msg_body:first').expander({ - slicePoint: this.options.truncate_limit, - expandText: 'read more', - userCollapseText: 'read less', - detailClass: 'oe_msg_tail', - moreClass: 'oe_mail_expand', - lessClass: 'oe_mail_reduce', - }); + on_expand: function (event) { + this.$('.oe_msg_body_short:first').toggle(); + this.$('.oe_msg_body_long:first').toggle(); }, /** diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 7e7d772397a..9958cb5e595 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -254,7 +254,13 @@
- + + + + + + +