diff options
Diffstat (limited to 'addons/website_mail')
| -rw-r--r-- | addons/website_mail/__manifest__.py | 5 | ||||
| -rw-r--r-- | addons/website_mail/controllers/main.py | 95 | ||||
| -rw-r--r-- | addons/website_mail/models/__init__.py | 1 | ||||
| -rw-r--r-- | addons/website_mail/models/mail_message.py | 15 | ||||
| -rw-r--r-- | addons/website_mail/models/mail_thread.py | 12 | ||||
| -rw-r--r-- | addons/website_mail/security/website_mail_security.xml | 2 | ||||
| -rw-r--r-- | addons/website_mail/static/src/css/website_mail.less | 34 | ||||
| -rw-r--r-- | addons/website_mail/static/src/js/website_mail.js | 231 | ||||
| -rw-r--r-- | addons/website_mail/static/src/xml/website_mail.xml | 125 | ||||
| -rw-r--r-- | addons/website_mail/views/website_mail_templates.xml | 17 |
10 files changed, 24 insertions, 513 deletions
diff --git a/addons/website_mail/__manifest__.py b/addons/website_mail/__manifest__.py index d0c73141faa..45571be05b3 100644 --- a/addons/website_mail/__manifest__.py +++ b/addons/website_mail/__manifest__.py @@ -6,7 +6,10 @@ 'category': 'Hidden', 'summary': 'Website Module for Mail', 'version': '0.1', - 'description': """Glue module holding mail improvements for website.""", + 'description': """ +Module holding mail improvements for website. +It is responsible of comments moderation for published documents (forum, slides, blog, ...) +""", 'depends': ['website', 'mail'], 'data': [ 'views/website_mail_templates.xml', diff --git a/addons/website_mail/controllers/main.py b/addons/website_mail/controllers/main.py index c10525fd5e0..ac73c0653c1 100644 --- a/addons/website_mail/controllers/main.py +++ b/addons/website_mail/controllers/main.py @@ -1,56 +1,8 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -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. - If a token is specified, all logged in users will be able to write a message regardless - of access rights; if the user is the public user, the message will be posted under the name - of the partner_id of the object (or the public user if there is no partner_id on the object). - - :param string res_model: model name of the object - :param int res_id: id of the object - :param string message: content of the message - - optional keywords arguments: - :param string token: access token if the object's model uses some kind of public access - using tokens (usually a uuid4) to bypass access rules - :param string token_field: name of the field that contains the token on the object (defaults to 'token') - :param bool nosubscribe: set False if you want the partner to be set as follower of the object when posting (default to True) - - The rest of the kwargs are passed on to message_post() - """ - 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_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: - 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"), - subtype=kw.pop('subtype', "mt_comment"), - author_id=author_id, - **kw) class WebsiteMail(http.Controller): @@ -108,50 +60,3 @@ class WebsiteMail(http.Controller): ('partner_id', '=', partner.id) ])) return values - - @http.route(['/website_mail/post/post'], type='http', methods=['POST'], auth='public', website=True) - def website_chatter_post(self, res_model, res_id, message, **kw): - url = request.httprequest.referrer - if message: - _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)] - # 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/__init__.py b/addons/website_mail/models/__init__.py index e90621e2bd2..75ea63bd6d8 100644 --- a/addons/website_mail/models/__init__.py +++ b/addons/website_mail/models/__init__.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from . import mail_thread from . import mail_message from . import update diff --git a/addons/website_mail/models/mail_message.py b/addons/website_mail/models/mail_message.py index f1ebde86c2a..34609160f1d 100644 --- a/addons/website_mail/models/mail_message.py +++ b/addons/website_mail/models/mail_message.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import api, fields, models, SUPERUSER_ID, _ +from odoo import api, fields, models, _ from odoo.osv import expression from odoo.tools import html2plaintext from odoo.exceptions import AccessError @@ -58,13 +58,6 @@ class MailMessage(models.Model): 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 + def _portal_message_format(self, fields_list): + fields_list += ['website_published'] + return super(MailMessage, self)._portal_message_format(fields_list) diff --git a/addons/website_mail/models/mail_thread.py b/addons/website_mail/models/mail_thread.py deleted file mode 100644 index 08cd54aa301..00000000000 --- a/addons/website_mail/models/mail_thread.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import api, fields, models - - -class MailThread(models.AbstractModel): - _inherit = 'mail.thread' - - website_message_ids = fields.One2many('mail.message', 'res_id', string='Website Messages', - domain=lambda self: [('model', '=', self._name), ('message_type', '=', 'comment')], auto_join=True, - help="Website communication history") diff --git a/addons/website_mail/security/website_mail_security.xml b/addons/website_mail/security/website_mail_security.xml index 0144b11e5ed..d814fff16ef 100644 --- a/addons/website_mail/security/website_mail_security.xml +++ b/addons/website_mail/security/website_mail_security.xml @@ -4,7 +4,7 @@ <record id="mail_message_rule_public" model="ir.rule"> <field name="name">mail.message: portal/public: read published messages</field> - <field name="model_id" ref="model_mail_message"/> + <field name="model_id" ref="mail.model_mail_message"/> <field name="domain_force">[('website_published', '=', True)]</field> <field name="groups" eval="[(4, ref('base.group_portal')), (4, ref('base.group_public'))]"/> </record> diff --git a/addons/website_mail/static/src/css/website_mail.less b/addons/website_mail/static/src/css/website_mail.less index dda250e65e5..6be6bf9007c 100644 --- a/addons/website_mail/static/src/css/website_mail.less +++ b/addons/website_mail/static/src/css/website_mail.less @@ -2,37 +2,3 @@ .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/website_mail.js b/addons/website_mail/static/src/js/website_mail.js index 3e373c4dce0..711df6ebbae 100644 --- a/addons/website_mail/static/src/js/website_mail.js +++ b/addons/website_mail/static/src/js/website_mail.js @@ -1,237 +1,20 @@ -odoo.define('website_mail.thread', function (require) { +odoo.define('website_mail.thread', function(require) { 'use strict'; -var 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; + +var PortalChatter = require('portal.chatter').PortalChatter; /** - * Widget WebsiteMailThread - * - * - Fetch message fron controller - * - Display chatter: pager, total message, composer (according to access right) - * - Provider API to filter displayed messages + * Extends Frontend Chatter to handle rating */ -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('pager', {}); - this.set('domain', this.options['domain']); - this._current_page = this.options['pager_start']; - }, - 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._current_page)); - }); - self.on("change:pager", self, self._renderPager); - self.on("change:domain", self, self._onChangeDomain); - // 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'])); - return result; - }); - }, - - //-------------------------------------------------------------------------- - // 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; +PortalChatter.include({ + _loadTemplates: function(){ + return $.when(this._super(), ajax.loadXML('/website_mail/static/src/xml/website_mail.xml', qweb)); }, - - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- - - /** - * Change the current page by refreshing current domain - * - * @private - * @param {Number} page - * @param {Array} domain - */ - _changeCurrentPage: function (page, domain){ - this._current_page = page; - var d = domain ? domain : _.clone(this.get('domain')); - this.set('domain', d); // trigger fetch message - }, - /** - * @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._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 data; - }, - /** - * Generate the pager data for the given page number - * - * @private - * @param {Number} page - * @returns {Object} - */ - _pager: function (page) { - 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); - - 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 - //-------------------------------------------------------------------------- - - _onChangeDomain: function () { - var self = this; - this.messageFetch().then(function () { - var p = self.get('current_page'); - self.set('pager', self._pager(p)); - }); - }, - /** - * @private - * @param {MouseEvent} event - */ - _onClickPager: function (ev) { - ev.preventDefault(); - var page = $(ev.currentTarget).data('page'); - this._changeCurrentPage(page); - }, -}); - -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/website_mail.xml b/addons/website_mail/static/src/xml/website_mail.xml index 01eefeb5d8b..b80e2eb8af8 100644 --- a/addons/website_mail/static/src/xml/website_mail.xml +++ b/addons/website_mail/static/src/xml/website_mail.xml @@ -9,124 +9,15 @@ </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-extend="portal.chatter_messages"> + <t t-jquery=".o_portal_chatter_message_title" t-operation="before"> + <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> - <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..."></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 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> diff --git a/addons/website_mail/views/website_mail_templates.xml b/addons/website_mail/views/website_mail_templates.xml index d703dad5e79..50efe171a5d 100644 --- a/addons/website_mail/views/website_mail_templates.xml +++ b/addons/website_mail/views/website_mail_templates.xml @@ -25,21 +25,4 @@ </xpath> </template> - <!-- - Generic chatter template for the frontend - This template provide the container of the chatter. The rest is done in js. - To use this template, you need to call it after setting the following variable in your template or in your controller: - :object browserecord : the mail_thread object - :message_per_page int (optional): number of message per chatter page - :token string (optional): if you want your chatter to be available for non-logged user, - you can use a token to verify the identity of the user; - the message will be posted with the identity of the partner_id of the object - :token_field string (optional): name of the field that contains the token on the object (default to 'token') - --> - <template id="message_thread"> - <div id="discussion" class="hidden-print o_website_mail_thread" - t-att-data-token="token" t-att-public-user="website.env.user == request.website.user_id" t-att-data-res_model="object._name" t-att-data-res_id="object.id" t-att-data-token_field="token_field" t-att-data-pager_step="message_per_page or 10" t-att-data-allow_composer="'0' if disable_composer else '1'"> - </div> - </template> - </odoo> |
