diff options
| author | Damien Bouvy <dbo@odoo.com> | 2015-08-06 11:59:28 +0200 |
|---|---|---|
| committer | Antony Lesuisse <al@openerp.com> | 2015-08-07 01:47:06 +0200 |
| commit | ad081b0da128adc5a1454eabf17c5d438eee42fa (patch) | |
| tree | 854df99336b8b554d0387da0a1f6640da6a1b617 /addons | |
| parent | 5f48cb992e75a31ae8d60d7a574b178f5fa01192 (diff) | |
[IMP] website_mail: generic chatter implementation
A new generic chatter template is available in website_mail
This template allows access rights escalation when some kind of token or uuid
is available on the model or if you use the object_shasign function in the
main controller of website_maill to generate a cryptographic signature to allow
commenting on any object.
To use this chatter, you need to make a t-call to website_maill.thread in your
template after having set the following variables:
- chatter_object: the browserecord of the mail_thread object (mandatory)
- token: if you use a token system (optional)
- token_field: name of the field that stores the token on your object (optional)
- sha_in: if you use a shasign to allow public comment (optional)
- nosubscribe: set False if you want the partner to be set as follower of the object (optional)
- message_type, subtype: see message_post in mail_thread.py
Diffstat (limited to 'addons')
| -rw-r--r-- | addons/website_mail/controllers/main.py | 84 | ||||
| -rw-r--r-- | addons/website_mail/static/src/js/message_post.js | 49 | ||||
| -rw-r--r-- | addons/website_mail/static/src/xml/chatter_message.xml | 15 | ||||
| -rw-r--r-- | addons/website_mail/views/website_mail.xml | 107 |
4 files changed, 228 insertions, 27 deletions
diff --git a/addons/website_mail/controllers/main.py b/addons/website_mail/controllers/main.py index d87bdec7ca1..7a12f471c23 100644 --- a/addons/website_mail/controllers/main.py +++ b/addons/website_mail/controllers/main.py @@ -1,11 +1,74 @@ # -*- 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 openerp import SUPERUSER_ID from openerp.addons.web import http from openerp.addons.web.http import request +def object_shasign(record=False, res_model='', res_id=None, **kw): + """ Generate a sha signature using the current time, database secret and the + record object or the res_model and res_id parameters + Return the sha signature and the time of generation in a tuple""" + secret = request.env['ir.config_parameter'].sudo().get_param('database.secret') + shasign = False + timestamp = int(time()) + if record: + shasign = sha1('%s%s%s%s' % (record._model, record.id, secret, timestamp)).hexdigest() + elif res_model and res_id: + shasign = sha1('%s%s%s%s' % (res_model, res_id, secret, timestamp)).hexdigest() + return (shasign, timestamp) + + +def _message_post_helper(res_model='', res_id=None, message='', token='', token_field='token', sha_in='', sha_time=None, nosubscribe=True, **kw): + """ Generic chatter function, allowing to write on *any* object that inherits mail.thread. + If a token or a shasign 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 string sha_in: sha1 hash of the string composed of res_model, res_id and the dabase secret in ir.config_parameter + if you wish to allow public users to write on the object with some security but you don't want + to add a token field on the object, the sha-sign prevents public users from writing to any other + object that the one specified by res_model and res_id + to generate the shasign, you can import the function object_shasign from this file in your controller + :param str sha_time: timestamp of sha signature generation (signatures are valid for 24h) + :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() + """ + res = request.env[res_model].browse(res_id) + author_id = request.env.user.partner_id.id + if token and res and token == getattr(res.sudo(), token_field, None): + res = res.sudo() + if request.env.user == request.env['ir.model.data'].xmlid_to_object('base.public_user'): + author_id = (res.partner_id and res.partner_id.id) if hasattr(res, 'partner_id') else author_id + else: + raise NotFound() + elif sha_in: + timestamp = int(sha_time) + secret = request.env['ir.config_parameter'].sudo().get_param('database.secret') + shasign = sha1('%s%s%s%s' % (res_model, res_id, secret, timestamp)) + if sha_in == shasign.hexdigest() and int(time()) < timestamp + 3600 * 24: + res = res.sudo() + else: + raise NotFound() + return res.with_context({'mail_create_nosubscribe': nosubscribe}).message_post(body=message, + message_type=kw.pop('message_type', False) or "comment", + subtype=kw.pop('subtype', False) or "mt_comment", + author_id=author_id, + **kw) + class WebsiteMail(http.Controller): @http.route(['/website_mail/follow'], type='json', auth="public", website=True) @@ -79,3 +142,24 @@ class WebsiteMail(http.Controller): ('partner_id', '=', partner_id.id) ], context=context)) == 1 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): + res_id = int(res_id) + try: + msg = _message_post_helper(res_model, res_id, message, **kw) + data = { + 'body': msg.body, + 'date': msg.date, + 'author': msg.author_id.name, + 'image_url': request.website.image_url(msg.author_id, 'image_small') + } + return data + except Exception: + return False + + @http.route(['/website_mail/post/post'], type='http', method=['POST'], auth='public', website=True) + def chatter_post(self, res_model='', res_id=None, message='', redirect=None, **kw): + res_id = int(res_id) + _message_post_helper(res_model, res_id, message, **kw) + return request.redirect(request.httprequest.referrer) diff --git a/addons/website_mail/static/src/js/message_post.js b/addons/website_mail/static/src/js/message_post.js new file mode 100644 index 00000000000..177101bcce1 --- /dev/null +++ b/addons/website_mail/static/src/js/message_post.js @@ -0,0 +1,49 @@ +odoo.define('website_mail_post', function(require) { + 'use strict'; + require('website.website'); + var qweb = require('qweb'); + var ajax = require('web.ajax'); + ajax.loadXML('/website_mail/static/src/xml/chatter_message.xml', qweb); + + if(!$('.o_website_chatter_json').length) { + return $.Deferred().reject("DOM doesn't contain '.o_website_chatter_json'"); + } + + $('.o_website_chatter_json').on('click', function(ev) { + ev.preventDefault(); + var $button = $(this); + var $form = $(this).parents().find('.o_website_chatter_form'); + var action = $form.attr('action'); + var data = getFormData($form); + data.message = data.message.replace(/\n/g,"<br/>"); + if (data.message) { + $button.attr('disabled', true); + var button_bk = $button.html(); + $button.prepend('<i class="fa fa-refresh fa-spin"></i> '); + ajax.jsonRpc(action, 'call', data).then(function (result) { + if (result) { + $('.o_website_chatter_error').fadeOut(); + var msg = qweb.render('website_mail.thread_message', result); + var elem = $(msg).hide().prependTo($('.o_website_comments')); + elem.slideToggle(); + $form.find('textarea').val(''); + } else { + $('o_website_chatter_error').fadeIn(); + } + $button.html(button_bk); + $button.attr('disabled', false); + }); + } + + function getFormData($form){ + var unindexed_array = $form.serializeArray(); + var indexed_array = {}; + + $.map(unindexed_array, function(n, i){ + indexed_array[n.name] = n.value; + }); + + return indexed_array; + } + }); +}); diff --git a/addons/website_mail/static/src/xml/chatter_message.xml b/addons/website_mail/static/src/xml/chatter_message.xml new file mode 100644 index 00000000000..dec459c0f18 --- /dev/null +++ b/addons/website_mail/static/src/xml/chatter_message.xml @@ -0,0 +1,15 @@ +<templates id="template" xml:space="preserve"> + <t t-name="website_mail.thread_message"> + <li class="media"> + <div class="media-body"> + <img class="media-object pull-left" 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/views/website_mail.xml b/addons/website_mail/views/website_mail.xml index 5a5d102908e..5dabf414968 100644 --- a/addons/website_mail/views/website_mail.xml +++ b/addons/website_mail/views/website_mail.xml @@ -1,30 +1,83 @@ <?xml version="1.0" encoding="utf-8"?> -<openerp> -<data> +<odoo> + <template id="follow"> + <div class="input-group js_follow" t-att-data-id="object.id" + t-att-data-object="object._name" + t-att-data-follow="object.id and object.message_is_follower and 'on' or 'off'" + t-att-data-unsubscribe="'unsubscribe' if 'unsubscribe' in request.params else None"> + <input + type="email" name="email" + class="js_follow_email form-control" + placeholder="your email..." + groups="base.group_public"/> + <span class="input-group-btn"> + <button href="#" t-attf-class="btn btn-default js_unfollow_btn">Unsubscribe</button> + <button href="#" t-attf-class="btn btn-primary js_follow_btn">Subscribe</button> + </span> + </div> + </template> -<template id="follow"> - <div class="input-group js_follow" t-att-data-id="object.id" - t-att-data-object="object._name" - t-att-data-follow="object.id and object.message_is_follower and 'on' or 'off'" - t-att-data-unsubscribe="'unsubscribe' if 'unsubscribe' in request.params else None"> - <input - type="email" name="email" - class="js_follow_email form-control" - placeholder="your email..." - groups="base.group_public"/> - <span class="input-group-btn"> - <button href="#" t-attf-class="btn btn-default js_unfollow_btn">Unsubscribe</button> - <button href="#" t-attf-class="btn btn-primary js_follow_btn">Subscribe</button> - </span> - </div> -</template> + <template id="head" inherit_id="website.assets_frontend" name="Mail customization"> + <xpath expr="." position="inside"> + <script type="text/javascript" src="/website_mail/static/src/js/follow.js"></script> + <script type="text/javascript" src="/website_mail/static/src/js/message_post.js"></script> + <link rel='stylesheet' href='/website_mail/static/src/css/website_mail.css'/> + </xpath> + </template> -<template id="head" inherit_id="website.assets_frontend" name="Mail customization"> - <xpath expr="." position="inside"> - <script type="text/javascript" src="/website_mail/static/src/js/follow.js"></script> - <link rel='stylesheet' href='/website_mail/static/src/css/website_mail.css'/> - </xpath> -</template> - -</data> -</openerp> + <template id="message_thread"> + <!-- + Generic chatter template for the frontend + 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 + :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') + :sha_in string (optional): if you use the shasign to allow commenting by non-logged user, you need to set it in this variable using the + object_shasign function defined in the main controller of website_portal + :sha_time string (optional): timestamp of the shasign generation + :chatter_mode string: 'json' or 'post' depending on what kind of request you want + --> + <section id="discussion" class="hidden-print oe_website_portal"> + <section class="mb32 css_editable_mode_hidden hidden-print"> + <form class="o_website_chatter_form" t-attf-action="/website_mail/post/#{chatter_mode}" method="POST" t-if="website.env.user != request.env['ir.model.data'].xmlid_to_object('base.public_user') or token or sha_in"> + <img class="img pull-left img-circle" t-att-src="website.image_url(user_id.partner_id, 'image_small')" style="width: 50px; margin-right: 10px;"/> + <div class="pull-left mb32" style="width: 75%%"> + <textarea rows="4" name="message" class="form-control" placeholder="Write a message..."></textarea> + <input type='hidden' name="res_model" t-att-value="object._model"/> + <input type='hidden' name="res_id" t-att-value="object.id"/> + <input type='hidden' name="token" t-att-value="token" t-if="token"/> + <input type='hidden' name="sha_in" t-att-value="sha_in" t-if="sha_in"/> + <input type='hidden' name="sha_time" t-att-value="sha_time" t-if="sha_time"/> + <input type='hidden' name="token_field" t-att-value="token_field" t-if="token_field"/> + <div class="alert alert-danger mt8 mb0 o_website_chatter_error" style="display:none;"> + Oops! Something went wrong. Try to reload the page and to log in. + </div> + <button t-attf-class="o_website_chatter_#{chatter_mode} btn btn-primary mt8 o_website_message_post_helper" type="submit">Send</button> + </div> + </form> + </section> + <div class="clearfix"/> + <ul class="media-list o_website_comments"> + <t t-foreach="object.message_ids" t-as="message"> + <t t-if="message.website_published or website.env.user.sudo(website.env.user.id).has_group('base.group_website_publisher')"> + <li class="media" t-if="message.message_type == 'comment'"> + <div class="media-body"> + <img class="media-object pull-left img-circle" t-att-src="website.image_url(message.author_id, 'image_small')" style="width: 50px; margin-right: 10px;"/> + <div class="media-body"> + <h5 class="media-heading"> + <span t-field="message.author_id"/> <small>on <span t-field="message.date"/></small> + </h5> + <t t-set="object" t-value="message"/> + <t t-call="website.publish_short"/> + <div t-field="message.body"/> + </div> + </div> + </li> + </t> + </t> + </ul> + </section> + </template> +</odoo> |
