diff options
| author | Jérome Maes <jem@openerp.com> | 2016-12-26 11:32:25 +0100 |
|---|---|---|
| committer | Jérome Maes <jem@openerp.com> | 2017-06-30 11:06:32 +0200 |
| commit | d79678fdd5faccffff65a439653fa5d639a3d3c4 (patch) | |
| tree | 7cb3f4a849289dbae3983daefea02c6cbbd21467 /addons/website_mail/static | |
| parent | 890e665b882b27184003cc5de6ad365f2910815d (diff) | |
[IMP] website_mail: make the frontend chatter lazy
Message are now fetched and display using javascript
to optimize performance.
Also the chatter is completely rewrite : add pager,
filter possiblity, ...
This commit is mainly technical: posting feature does
not change. Only the display with pager is new.
The goal is also to prepare frontend chatter for a
better integration with rating widget.
The mail.thread mixin is extended to manage the field
website_message_ids (display all messages that can be
seen on frontend by user (employee or not).
When fetching or posting message, a check is done to see
if we need to do it as sudo(). website_mail implement posting
messages with token or sha_in (using in website_quote).
The same verification is now done when fetching messages, via
'_special_access_object' method.
Diffstat (limited to 'addons/website_mail/static')
| -rw-r--r-- | addons/website_mail/static/src/css/website_mail.css | 4 | ||||
| -rw-r--r-- | addons/website_mail/static/src/css/website_mail.less | 38 | ||||
| -rw-r--r-- | addons/website_mail/static/src/js/message_post.js | 80 | ||||
| -rw-r--r-- | addons/website_mail/static/src/js/website_mail.js | 232 | ||||
| -rw-r--r-- | addons/website_mail/static/src/xml/chatter_message.xml | 15 | ||||
| -rw-r--r-- | addons/website_mail/static/src/xml/website_mail.xml | 132 |
6 files changed, 402 insertions, 99 deletions
diff --git a/addons/website_mail/static/src/css/website_mail.css b/addons/website_mail/static/src/css/website_mail.css deleted file mode 100644 index 5678442416c..00000000000 --- a/addons/website_mail/static/src/css/website_mail.css +++ /dev/null @@ -1,4 +0,0 @@ -.js_follow[data-follow='on'] .js_follow_btn , -.js_follow[data-follow='off'] .js_unfollow_btn { - display: none; -}
\ No newline at end of file diff --git a/addons/website_mail/static/src/css/website_mail.less b/addons/website_mail/static/src/css/website_mail.less new file mode 100644 index 00000000000..dda250e65e5 --- /dev/null +++ b/addons/website_mail/static/src/css/website_mail.less @@ -0,0 +1,38 @@ +.js_follow[data-follow='on'] .js_follow_btn , +.js_follow[data-follow='off'] .js_unfollow_btn { + display: none; +} + +.o_website_mail_chatter { + padding: 10px; + + .o_website_mail_avatar{ + width: 45px; + height: 45px; + } + + .o_website_mail_header { + margin-bottom: 15px; + } + + .o_website_mail_composer { + margin-bottom: 15px; + } + + .o_website_mail_messages { + margin-bottom: 15px; + + .o_website_mail_message_title { + p { + font-size:85%; + color:@odoo-main-color-muted; + margin: 0px; + } + } + + } + + .o_website_mail_pager { + text-align: center; + } +} diff --git a/addons/website_mail/static/src/js/message_post.js b/addons/website_mail/static/src/js/message_post.js deleted file mode 100644 index 2e93e8708a0..00000000000 --- a/addons/website_mail/static/src/js/message_post.js +++ /dev/null @@ -1,80 +0,0 @@ -odoo.define('website_mail.thread', function(require) { - 'use strict'; - - var web_editor_base = require('web_editor.base'); - var ajax = require('web.ajax'); - var core = require('web.core'); - var Widget = require('web.Widget'); - - var qweb = core.qweb; - - // load qweb template - ajax.loadXML('/website_mail/static/src/xml/chatter_message.xml', qweb); - - /** - * Widget WebsiteMailThread - * - * Widget sending message to the server, using json request. Used to not refresh all - * the page. Its DOM already exists on the page (The class of root element is - * '.o_website_mail_thread'). - */ - var WebsiteMailThread = Widget.extend({ - events:{ - "click .o_website_chatter_json": "on_click", - }, - on_click: function(e){ - var self = this; - e.preventDefault(); - - var $button = this.$(e.currentTarget); - var $form = this.$('.o_website_chatter_form'); - var $error = this.$('.o_website_chatter_error'); - var action = $form.attr('action'); - var data = this._get_form_data($form); - - data.message = data.message.replace(/\n/g,"<br/>"); - if (data.message) { - // make the 'send' button loading - $button.attr('disabled', true); - var button_bk = $button.html(); - $button.prepend('<i class="fa fa-refresh fa-spin"></i> '); - // post message, shw/hide error message and empty textarea - ajax.jsonRpc(action, 'call', data).then(function (result) { - if (result) { - $error.fadeOut(); - self.prepend_message(result); - $form.find('textarea').val(''); - } else { - $error.fadeIn(); - } - $button.html(button_bk); - $button.attr('disabled', false); - }); - } - }, - prepend_message: function(message_data){ - var msg = qweb.render('website_mail.thread_message', message_data); - var elem = $(msg).hide().prependTo(this.$('.o_website_comments')); - elem.slideToggle(); - return elem; - }, - _get_form_data: function($form){ - var unindexed_array = $form.serializeArray(); - var indexed_array = {}; - $.map(unindexed_array, function(n, i){ - indexed_array[n.name] = n.value; - }); - return indexed_array; - }, - }); - - web_editor_base.ready().then(function(){ - if($('.o_website_mail_thread').length) { - var mail_thread = new WebsiteMailThread($('body')).setElement($('.o_website_mail_thread')); - } - }); - - return { - WebsiteMailThread: WebsiteMailThread, - } -}); diff --git a/addons/website_mail/static/src/js/website_mail.js b/addons/website_mail/static/src/js/website_mail.js new file mode 100644 index 00000000000..ef43190c529 --- /dev/null +++ b/addons/website_mail/static/src/js/website_mail.js @@ -0,0 +1,232 @@ +odoo.define('website_mail.thread', function(require) { + 'use strict'; + + var web_editor_base = require('web_editor.base'); + var ajax = require('web.ajax'); + var core = require('web.core'); + var Widget = require('web.Widget'); + var rpc = require('web.rpc'); + + var qweb = core.qweb; + var _t = core._t; + + /** + * Widget WebsiteMailThread + * + * - Fetch message fron controller + * - Display chatter: pager, total message, composer (according to access right) + * - Provider API to filter displayed messages + */ + var WebsiteMailThread = Widget.extend({ + template: 'website_mail.chatter', + events: { + "click .o_website_mail_pager_btn": '_onClickPager' + }, + + init: function(parent, options){ + this._super.apply(this, arguments); + this.options = _.defaults(options || {}, { + 'allow_composer': true, + 'display_composer': false, + 'csrf_token': odoo.csrf_token, + 'message_count': 0, + 'pager_step': 10, + 'pager_scope': 5, + 'pager_start': 1, + 'is_user_public': true, + 'is_user_publisher': false, + 'domain': [], + }); + this.set('messages', []); + this.set('message_count', this.options['message_count']); + this.set('current_page', this.options['pager_start']); + this.set('pager', {}); + this.set('domain', this.options['domain']); + }, + willStart: function(){ + var self = this; + // load qweb template and init data + return $.when( + rpc.query({ + route: '/website_mail/init', + params: this._messageFetchPrepareParams() + }), this._loadTemplates() + ).then(function(result){ + // bind events + self.on("change:messages", self, self._renderMessages); + self.on("change:message_count", self, function(){ + self._renderMessageCount(); + self.set('pager', self._pager(self.get('current_page'))); + }); + self.on("change:pager", self, self._renderPager); + self.on("change:domain", self, self._onChangeDomain); + self.on("change:current_page", self, self._onChangeCurrentPage); + // set options and parameters + self.options = _.extend(self.options, result['options'] || {}); + self.set('message_count', self.options['message_count']); + self.set('messages', self.preprocessMessages(result['messages'])); + }); + }, + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * Fetch the messages and the message count from the server for the + * current page and current domain. + * + * @param {Array} + * @returns {Deferred} + */ + messageFetch: function(domain){ + var self = this; + return rpc.query({ + route: '/website_mail/fetch', + params: self._messageFetchPrepareParams() + }).then(function(result){ + self.set('messages', self.preprocessMessages(result['messages'])); + self.set('message_count', result['message_count']); + }); + }, + /** + * Update the messages format + * + * @param {Array<Object>} + * @returns {Array} + */ + preprocessMessages: function(messages){ + _.each(messages, function(m){ + m['author_avatar_url'] = _.str.sprintf('/web/image/%s/%s/author_avatar/50x50', 'mail.message', m.id); + m['published_date_str'] = _.str.sprintf(_t('Published on %s'), moment(m.date).format('MMMM Do YYYY, h:mm:ss a')); + }); + return messages; + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @private + * @returns {Deferred} + */ + _loadTemplates: function(){ + return ajax.loadXML('/website_mail/static/src/xml/website_mail.xml', qweb); + }, + _messageFetchPrepareParams: function(){ + var self = this; + var data = { + 'res_model': this.options['res_model'], + 'res_id': this.options['res_id'], + 'limit': this.options['pager_step'], + 'offset': (this.get('current_page')-1) * this.options['pager_step'], + 'allow_composer': this.options['allow_composer'], + }; + // add fields to allow to post comment without being logged + _.each(['token', 'token_field'], function(field){ + if(self.options[field]){ + data[field] = self.options[field]; + } + }); + // add domain + if(this.get('domain')){ + data['domain'] = this.get('domain'); + } + return ajax.jsonRpc('/website_mail/fetch', 'call', data).then(function(result){ + self.set('messages', self.preprocessMessages(result['messages'])); + self.set('message_count', result['message_count']); + }); + }, + /** + * Generate the pager data for the given page number + * + * @private + * @param {Number} page + * @returns {Object} + */ + _pager: function(page){ + var page = page || 1; + var total = this.get('message_count'); + var scope = this.options['pager_scope']; + var step = this.options['pager_step']; + + // Compute Pager + var page_count = Math.ceil(parseFloat(total) / step); + + var page = Math.max(1, Math.min(parseInt(page), page_count)); + scope -= 1; + + var pmin = Math.max(page - parseInt(Math.floor(scope/2)), 1); + var pmax = Math.min(pmin + scope, page_count); + + if(pmax - scope > 0){ + pmin = pmax - scope; + }else{ + pmin = 1; + } + + var pages = []; + _.each(_.range(pmin, pmax+1), function(index){ + pages.push(index); + }); + + return { + "page_count": page_count, + "offset": (page - 1) * step, + "page": page, + "page_start": pmin, + "page_previous": Math.max(pmin, page - 1), + "page_next": Math.min(pmax, page + 1), + "page_end": pmax, + "pages": pages + }; + }, + _renderMessages: function(){ + this.$('.o_website_mail_messages').html(qweb.render("website_mail.chatter_messages", {widget: this})); + }, + _renderMessageCount: function(){ + this.$('.o_message_counter').replaceWith(qweb.render("website_mail.chatter_message_count", {widget: this})); + }, + _renderPager: function(){ + this.$('.o_website_mail_pager').replaceWith(qweb.render("website_mail.pager", {widget: this})); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + _onChangeCurrentPage: function(){ + var self = this; + this.messageFetch().then(function(){ + var p = self.get('current_page'); + self.set('pager', self._pager(p)); + }); + }, + _onChangeDomain: function(){ + this.set('current_page', 1); + }, + /** + * @private + * @param {MouseEvent} event + */ + _onClickPager: function(ev){ + ev.preventDefault(); + var page = $(ev.currentTarget).data('page'); + this.set('current_page', page); + }, + + }); + + web_editor_base.ready().then(function(){ + $('.o_website_mail_thread').each(function(index){ + var $elem = $(this); + var mail_thread = new WebsiteMailThread(null, $elem.data()); + mail_thread.appendTo($elem); + }); + }); + + return { + WebsiteMailThread: WebsiteMailThread, + }; +}); diff --git a/addons/website_mail/static/src/xml/chatter_message.xml b/addons/website_mail/static/src/xml/chatter_message.xml deleted file mode 100644 index 111f9877d7a..00000000000 --- a/addons/website_mail/static/src/xml/chatter_message.xml +++ /dev/null @@ -1,15 +0,0 @@ -<templates id="template" xml:space="preserve"> - <t t-name="website_mail.thread_message"> - <li class="media" t-att-id="'message-' + id"> - <div class="media-body"> - <img class="media-object pull-left img-circle" t-att-src="image_url" style="width: 50px; margin-right: 10px;"/> - <div class="media-body"> - <h5 class="media-heading"> - <span t-esc="author"/> <small>just now</small> - </h5> - <div t-raw="body"/> - </div> - </div> - </li> - </t> -</templates>
\ No newline at end of file diff --git a/addons/website_mail/static/src/xml/website_mail.xml b/addons/website_mail/static/src/xml/website_mail.xml new file mode 100644 index 00000000000..0e26c7e56ab --- /dev/null +++ b/addons/website_mail/static/src/xml/website_mail.xml @@ -0,0 +1,132 @@ +<templates id="template" xml:space="preserve"> + + <t t-name="website_mail.publish_short"> + <t t-if="is_publisher" t-ignore="true"> + <div t-attf-class="pull-right js_publish_management #{object.website_published and 'css_published' or 'css_unpublished'}" t-att-data-id="res_id" t-att-data-object="res_model" t-att-data-controller="publish_controller"> + <button class="btn btn-danger js_publish_btn">Unpublished</button> + <button class="btn btn-success js_publish_btn">Published</button> + </div> + </t> + </t> + + <t t-name="website_mail.chatter_message_count"> + <t t-set="count" t-value="widget.get('message_count')"/> + <div class="o_message_counter"> + <t t-if="count"> + <span class="o_message_count fa fa-comments"> <t t-esc="count"/></span> + <t t-if="count == 1">comment</t> + <t t-else="">comments</t> + </t> + <t t-else=""> + There are no comments for now. + </t> + </div> + </t> + + <t t-name="website_mail.chatter_composer"> + <div class="o_website_mail_composer" t-if="widget.options['allow_composer']"> + <t t-if="!widget.options['display_composer']"> + <h4>Leave a comment</h4> + <p>You must be <a t-attf-href="/web/login?redirect=#{window.encodeURI(window.location.href + '#discussion')}">logged in</a> to post a comment.</p> + </t> + <t t-if="widget.options['display_composer']"> + <div class="media"> + <div class="media-left"> + <img class="o_website_mail_avatar pull-left" t-attf-src="/web/image/res.partner/#{widget.options['partner_id']}/image_small/50x50"/> + </div> + <div class="media-body"> + <form class="o_website_mail_composer_form" t-attf-action="/website_mail/post/post" method="POST"> + <input type="hidden" name="csrf_token" t-att-value="widget.options['csrf_token']"/> + <div class="mb32"> + <textarea rows="4" name="message" class="form-control" placeholder="Write a message..." style="width: 75%"></textarea> + <input type='hidden' name="res_model" t-att-value="widget.options['res_model']"/> + <input type='hidden' name="res_id" t-att-value="widget.options['res_id']"/> + <input type='hidden' name="token" t-att-value="widget.options['token']" t-if="widget.options['token']"/> + <input type='hidden' name="sha_in" t-att-value="widget.options['sha_in']" t-if="widget.options['sha_in']"/> + <input type='hidden' name="sha_time" t-att-value="widget.options['sha_time']" t-if="widget.options['sha_time']"/> + <input type='hidden' name="token_field" t-att-value="widget.options['token_field']" t-if="widget.options['token_field']"/> + <div class="alert alert-danger mt8 mb0 o_website_mail_composer_error" style="display:none;"> + Oops! Something went wrong. Try to reload the page and log in. + </div> + <button t-attf-class="o_website_mail_composer_btn btn btn-primary mt8 o_website_message_post_helper" type="submit">Send</button> + </div> + </form> + </div> + </div> + </t> + <hr/> + </div> + </t> + + <t t-name="website_mail.chatter_messages"> + <div class="o_website_mail_messages"> + <t t-foreach="widget.get('messages')" t-as="message"> + <div class="media o_website_mail_message" t-att-id="'message-' + message.id"> + <div class="media-left"> + <img class="media-object o_website_mail_avatar" t-att-src="message.author_avatar_url" alt="avatar"/> + </div> + <div class="media-body"> + + <t t-call="website_mail.publish_short"> + <t t-set="res_model" t-value="'mail.message'"/> + <t t-set="res_id" t-value="message.id"/> + <t t-set="object" t-value="message"/> + <t t-set="is_publisher" t-value="widget.options['is_user_publisher']"/> + </t> + + <div class="o_website_mail_message_title"> + <h5 class="media-heading"> + <t t-esc="message.author_id[1]"/> + </h5> + <p class="o_website_mail_puslished_date"><t t-esc="message.published_date_str"/></p> + </div> + <t t-raw="message.body"/> + + <div class="o_website_mail_attachments"> + <div class="col-md-2 col-sm-3 text-center" t-foreach='message.attachment_ids' t-as='attachment'> + <a t-attf-href="/web/content/#{attachment.id}?download=true" target="_blank"> + <div class='oe_attachment_embedded o_image' t-att-title="attachment.name" t-att-data-mimetype="attachment.mimetype" t-attf-data-src="/web/image/#{attachment.id}/100x80"/> + <div class='oe_attachment_name'><t t-raw='attachment.name'/></div> + </a> + </div> + </div> + </div> + </div> + </t> + </div> + </t> + + <t t-name="website_mail.pager"> + <div class="o_website_mail_pager"> + <t t-if="!_.isEmpty(widget.get('pager'))"> + <ul class="pagination" t-if="widget.get('pager')['pages'].length > 1"> + <li t-if="widget.get('pager')['page'] != widget.get('pager')['page_previous']" t-att-data-page="widget.get('pager')['page_previous']" class="o_website_mail_pager_btn"> + <a href="#"><i class="fa fa-chevron-left"/></a> + </li> + <t t-foreach="widget.get('pager')['pages']" t-as="page"> + <li t-att-data-page="page" t-att-class="page == widget.get('pager')['page'] ? 'o_website_mail_pager_btn active' : 'o_website_mail_pager_btn'"> + <a href="#"><t t-esc="page"/></a> + </li> + </t> + <li t-if="widget.get('pager')['page'] != widget.get('pager')['page_next']" t-att-data-page="widget.get('pager')['page_next']" class="o_website_mail_pager_btn"> + <a href="#"><i class="fa fa-chevron-right"/></a> + </li> + </ul> + </t> + </div> + </t> + + <t t-name="website_mail.chatter"> + <div class="o_website_mail_chatter"> + <div class="o_website_mail_header"> + <t t-call="website_mail.chatter_message_count"/> + </div> + <hr/> + <t t-call="website_mail.chatter_composer"/> + <t t-call="website_mail.chatter_messages"/> + <div class="o_website_mail_footer"> + <t t-call="website_mail.pager"/> + </div> + </div> + </t> +</templates> |
