[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
This commit is contained in:
committed by
Antony Lesuisse
parent
5f48cb992e
commit
ad081b0da1
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user