diff --git a/addons/website_mail/__manifest__.py b/addons/website_mail/__manifest__.py index 95891d2f4ff18..4097c774b6e7f 100644 --- a/addons/website_mail/__manifest__.py +++ b/addons/website_mail/__manifest__.py @@ -13,7 +13,7 @@ 'data/mail_channel_data.xml', ], 'qweb': [ - 'static/src/xml/chatter_message.xml' + 'static/src/xml/website_mail.xml' ], 'installable': True, 'auto_install': True, diff --git a/addons/website_mail/controllers/main.py b/addons/website_mail/controllers/main.py index 546def6890a4f..c0e8354610d04 100644 --- a/addons/website_mail/controllers/main.py +++ b/addons/website_mail/controllers/main.py @@ -1,11 +1,18 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from hashlib import sha1 -from time import time -from werkzeug.exceptions import NotFound + +from werkzeug.exceptions import NotFound, Forbidden from odoo import http from odoo.http import request +from odoo.tools import consteq + + +def _special_access_object(res_model, res_id, token='', token_field=''): + record = request.env[res_model].browse(res_id).sudo() + if token and record and getattr(record, token_field, None) and consteq(getattr(record, token_field), token): + return True + return False def _message_post_helper(res_model='', res_id=None, message='', token='', token_field='token', nosubscribe=True, **kw): """ Generic chatter function, allowing to write on *any* object that inherits mail.thread. @@ -27,13 +34,17 @@ def _message_post_helper(res_model='', res_id=None, message='', token='', token_ """ record = request.env[res_model].browse(res_id) author_id = request.env.user.partner_id.id if request.env.user.partner_id else False - if token and record and token == getattr(record.sudo(), token_field, None): - record = record.sudo() - if request.env.user == request.env.ref('base.public_user'): - author_id = record.partner_id.id if hasattr(record, 'partner_id') else author_id + if token_field and token: + access_as_sudo = _special_access_object(res_model, res_id, token=token, token_field=token_field) + if access_as_sudo: + record = record.sudo() + if request.env.user == request.env.ref('base.public_user'): + author_id = record.partner_id.id if hasattr(record, 'partner_id') else author_id + else: + if not author_id: + raise NotFound() else: - if not author_id: - raise NotFound() + raise Forbidden() kw.pop('csrf_token', None) return record.with_context(mail_create_nosubscribe=nosubscribe).message_post(body=message, message_type=kw.pop('message_type', "comment"), @@ -98,25 +109,52 @@ def is_follower(self, model, res_id, **post): ])) return values - @http.route(['/website_mail/post/json'], type='json', auth='public', website=True) - def chatter_json(self, res_model='', res_id=None, message='', **kw): - try: - msg = _message_post_helper(res_model, int(res_id), message, **kw) - except Exception: - return False - data = { - 'id': msg.id, - 'body': msg.body, - 'date': msg.date, - 'author': msg.author_id.name, - 'image_url': '/mail/%s/%s/avatar/%s' % (msg.model, msg.res_id, msg.author_id.id) - } - return data - @http.route(['/website_mail/post/post'], type='http', methods=['POST'], auth='public', website=True) - def chatter_post(self, res_model='', res_id=None, message='', redirect=None, **kw): + def website_chatter_post(self, res_model, res_id, message, **kw): url = request.httprequest.referrer if message: - message = _message_post_helper(res_model, int(res_id), message, **kw) - url = url + "#message-%s" % (message.id,) + _message_post_helper(res_model, int(res_id), message, **kw) + url = url + "#discussion" return request.redirect(url) + + @http.route('/website_mail/init', type='json', auth='public', website=True) + def website_chatter_init(self, res_model, res_id, domain=False, limit=False, **kwargs): + is_user_public = bool(request.env.user == request.website.user_id) + message_data = self.website_message_fetch(res_model, res_id, domain=domain, limit=limit, **kwargs) + display_composer = False + if kwargs.get('allow_composer'): + display_composer = kwargs.get('token') or not is_user_public + return { + 'messages': message_data['messages'], + 'options': { + 'message_count': message_data['message_count'], + 'is_user_public': is_user_public, + 'is_user_publisher': request.env.user.has_group('website.group_website_publisher'), + 'display_composer': display_composer, + 'partner_id': request.env.user.partner_id.id + } + } + + @http.route('/website_mail/fetch', type='json', auth='public', website=True) + def website_message_fetch(self, res_model, res_id, domain=False, limit=10, offset=0, **kw): + if not domain: + domain = [] + # Only search into website_message_ids, so apply the same domain to perform only one search + # extract domain from the 'website_message_ids' field + field_domain = request.env[res_model]._fields['website_message_ids'].domain + domain += field_domain(request.env[res_model]) if callable(field_domain) else field_domain + domain += [('res_id', '=', res_id)] + # None published can see only the published messages + if not request.env.user.has_group('website.group_website_publisher'): + domain += [('website_published', '=', True)] + # Check access + Message = request.env['mail.message'] + if kw.get('token'): + access_as_sudo = _special_access_object(res_model, res_id, token=kw.get('token'), token_field=kw.get('token_field')) + if not access_as_sudo: # if token is not correct, raise Forbidden + raise Forbidden() + Message = request.env['mail.message'].sudo() + return { + 'messages': Message.search(domain, limit=limit, offset=offset).website_message_format(), + 'message_count': Message.search_count(domain) + } diff --git a/addons/website_mail/models/mail_message.py b/addons/website_mail/models/mail_message.py index d5e12e41b0b8b..f1ebde86c2a61 100644 --- a/addons/website_mail/models/mail_message.py +++ b/addons/website_mail/models/mail_message.py @@ -56,3 +56,15 @@ def check_access_rule(self, operation): if self.env.cr.fetchall(): raise AccessError(_('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % (self._description, operation)) return super(MailMessage, self).check_access_rule(operation=operation) + + @api.multi + def website_message_format(self): + message_values = self.read([ + 'id', 'body', 'date', 'author_id', 'email_from', # base message fields + 'message_type', 'subtype_id', 'subject', # message specific + 'model', 'res_id', 'record_name', # document related + 'website_published', + ]) + message_tree = dict((m.id, m) for m in self.sudo()) + self._message_read_dict_postprocess(message_values, message_tree) + return message_values 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 5678442416c30..0000000000000 --- 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 0000000000000..dda250e65e5a1 --- /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 2e93e8708a093..0000000000000 --- 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,"
"); - if (data.message) { - // make the 'send' button loading - $button.attr('disabled', true); - var button_bk = $button.html(); - $button.prepend(' '); - // 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 0000000000000..ef43190c52930 --- /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} + * @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 111f9877d7af7..0000000000000 --- a/addons/website_mail/static/src/xml/chatter_message.xml +++ /dev/null @@ -1,15 +0,0 @@ - - -
  • -
    - -
    -
    - just now -
    -
    -
    -
    -
  • -
    -
    \ 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 0000000000000..0e26c7e56abb1 --- /dev/null +++ b/addons/website_mail/static/src/xml/website_mail.xml @@ -0,0 +1,132 @@ + + + + +
    + + +
    +
    +
    + + + +
    + + + comment + comments + + + There are no comments for now. + +
    +
    + + +
    + +

    Leave a comment

    +

    You must be logged in to post a comment.

    +
    + +
    +
    + +
    +
    +
    + +
    + + + + + + + + + +
    +
    +
    +
    +
    +
    +
    +
    + + +
    + +
    +
    + avatar +
    +
    + + + + + + + + +
    +
    + +
    +

    +
    + + +
    + +
    +
    + +
    +
    + + +
    + +
      +
    • + +
    • + +
    • + +
    • +
      +
    • + +
    • +
    +
    +
    +
    + + +
    +
    + +
    +
    + + + +
    +
    + diff --git a/addons/website_mail/views/website_mail_templates.xml b/addons/website_mail/views/website_mail_templates.xml index 824abeb07278a..d703dad5e79b0 100644 --- a/addons/website_mail/views/website_mail_templates.xml +++ b/addons/website_mail/views/website_mail_templates.xml @@ -20,77 +20,26 @@ + +