From fb40c6bf6018f7f208201e1d4a4d413fd2ce3080 Mon Sep 17 00:00:00 2001 From: David Beguin Date: Wed, 20 Feb 2019 16:54:36 +0000 Subject: [PATCH] [IMP] website(_profile,_forum,_slides): move validation email to profile and use in forum and elearning Email validation was necessary on the forum to be able to begin to use the forum (ask or answer questions, vote, etc..) As the new elearning also uses karma since 705376a982d64fb41800f148a167ef0fc28ce4c6, the email validation is now also necessary in the eLearning platform. This is why this commit is moving the email validation process to website_profile and extend website_slides (eLearning) and website_forum to use this feature. In function of where the user asked to send him the validation email, the user is redirected on the forum or on the elearning when he clicks on 'Validate my account' in the received 'email validation' email. Task ID : 1943788 PR #31321 --- addons/website_forum/controllers/main.py | 37 +++--- addons/website_forum/data/forum_data.xml | 102 ----------------- addons/website_forum/models/forum.py | 10 -- addons/website_forum/models/res_users.py | 51 --------- .../static/src/js/website_forum.js | 30 +---- addons/website_forum/views/website_forum.xml | 22 +--- addons/website_profile/__manifest__.py | 1 + addons/website_profile/controllers/main.py | 28 ++++- addons/website_profile/data/profile_data.xml | 106 ++++++++++++++++++ addons/website_profile/models/__init__.py | 1 + addons/website_profile/models/res_users.py | 58 ++++++++++ .../static/src/js/website_profile.js | 47 ++++++++ .../website_profile/views/website_profile.xml | 39 ++++++- addons/website_slides/controllers/main.py | 14 ++- .../website_slides_templates_homepage.xml | 16 ++- 15 files changed, 321 insertions(+), 241 deletions(-) create mode 100644 addons/website_profile/data/profile_data.xml create mode 100644 addons/website_profile/models/res_users.py create mode 100644 addons/website_profile/static/src/js/website_profile.js diff --git a/addons/website_forum/controllers/main.py b/addons/website_forum/controllers/main.py index f31b59e430f..ed2cb30df2e 100644 --- a/addons/website_forum/controllers/main.py +++ b/addons/website_forum/controllers/main.py @@ -3,6 +3,7 @@ import json import lxml import requests +import logging import werkzeug.exceptions import werkzeug.urls import werkzeug.wrappers @@ -15,6 +16,8 @@ from odoo.addons.website.models.ir_http import sitemap_qs2dom from odoo.addons.website_profile.controllers.main import WebsiteProfile from odoo.http import request +_logger = logging.getLogger(__name__) + class WebsiteForum(WebsiteProfile): _post_per_page = 10 @@ -26,8 +29,6 @@ class WebsiteForum(WebsiteProfile): values.update({ 'header': kwargs.get('header', dict()), 'searches': kwargs.get('searches', dict()), - 'validation_email_sent': request.session.get('validation_email_sent', False), - 'validation_email_done': request.session.get('validation_email_done', False), }) if kwargs.get('forum'): values['forum'] = kwargs.get('forum') @@ -37,32 +38,21 @@ class WebsiteForum(WebsiteProfile): # User and validation # -------------------------------------------------- - + # TODO DBE : Those 3 are deprecated. To remove in v13. @http.route('/forum/send_validation_email', type='json', auth='user', website=True) - def send_validation_email(self, forum_id=None, **kwargs): - if request.env.uid != request.website.user_id.id: - request.env.user._send_forum_validation_email(forum_id=forum_id) - request.session['validation_email_sent'] = True - return True + def send_forum_validation_email(self, forum_id=None, **kwargs): + _logger.warning("The method send_forum_validation_email() is deprecated and should be removed in v13.") + return self.send_forum_validation_email(forum_id=forum_id, **kwargs) @http.route('/forum/validate_email', type='http', auth='public', website=True, sitemap=False) - def validate_email(self, token, id, email, forum_id=None, **kwargs): - if forum_id: - try: - forum_id = int(forum_id) - except ValueError: - forum_id = None - done = request.env['res.users'].sudo().browse(int(id)).process_forum_validation_token(token, email, forum_id=forum_id)[0] - if done: - request.session['validation_email_done'] = True - if forum_id: - return request.redirect("/forum/%s" % int(forum_id)) - return request.redirect('/forum') + def forum_validate_email(self, token, user_id, email, forum_id=None, **kwargs): + _logger.warning("The method forum_validate_email() is deprecated and should be removed in v13.") + return self.forum_validate_email(token=token, user_id=user_id, email=email, forum_id=forum_id, **kwargs) @http.route('/forum/validate_email/close', type='json', auth='public', website=True) - def validate_email_done(self): - request.session['validation_email_done'] = False - return True + def forum_validate_email_done(self, **kwargs): + _logger.warning("The method forum_validate_email_done() is deprecated and should be removed in v13.") + return self.validate_email_done(**kwargs) # Forum # -------------------------------------------------- @@ -550,6 +540,7 @@ class WebsiteForum(WebsiteProfile): # Profile # ----------------------------------- + @http.route(['/forum//user/'], type='http', auth="public", website=True) def view_user_forum_profile(self, forum, user_id, **post): return werkzeug.utils.redirect('/profile/user/' + str(user_id) + '?forum_id=' + str(forum.id)) diff --git a/addons/website_forum/data/forum_data.xml b/addons/website_forum/data/forum_data.xml index fe5fb2b8ea2..4196804495a 100644 --- a/addons/website_forum/data/forum_data.xml +++ b/addons/website_forum/data/forum_data.xml @@ -129,107 +129,5 @@ offensive - - - Forum: Email Verification - - ${object.company_id.name} Forums validation - ${('<%s>' % (object.env.user.company_id.email or user.email)) | safe} - ${object.email|safe} - - - - -
- - - - - - - - - - - - - - - -
- - - - - - - - -
- Your Forum
- - ${object.company_id.name} Forum validation - -
- ${user.company_id.name} -
-
-
-
- - - - - - - -
-

- Hello ${object.name},

- You have been invited to validate your email in order to get access to "${object.company_id.name}" Q/A Forums. - To validate your email, please click on the following link: -

- Thanks for your participation! -

-
-
-
-
- - - - - -
- ${user.company_id.name} - - ${user.company_id.phone} - % if user.company_id.email: - | - ${user.company_id.email} - - % endif - % if user.company_id.website: - | - ${user.company_id.website} - - % endif -
-
-
- - -
- Powered by Odoo -
-
-
-
- diff --git a/addons/website_forum/models/forum.py b/addons/website_forum/models/forum.py index 68d48b8534f..4dc172a90a0 100644 --- a/addons/website_forum/models/forum.py +++ b/addons/website_forum/models/forum.py @@ -4,7 +4,6 @@ import logging import math import re -import uuid from datetime import datetime from odoo.addons.gamification.models.gamification_karma_rank import KarmaError @@ -21,15 +20,6 @@ class Forum(models.Model): _description = 'Forum' _inherit = ['mail.thread', 'website.seo.metadata', 'website.multi.mixin'] - @api.model_cr - def init(self): - """ Add forum uuid for user email validation. - - TDE TODO: move me somewhere else, auto_init ? """ - forum_uuids = self.env['ir.config_parameter'].search([('key', '=', 'website_forum.uuid')]) - if not forum_uuids: - forum_uuids.set_param('website_forum.uuid', str(uuid.uuid4())) - @api.model def _get_default_faq(self): with misc.file_open('website_forum/data/forum_default_faq.html', 'r') as f: diff --git a/addons/website_forum/models/res_users.py b/addons/website_forum/models/res_users.py index 43791f3538c..67326175ac9 100644 --- a/addons/website_forum/models/res_users.py +++ b/addons/website_forum/models/res_users.py @@ -32,57 +32,6 @@ class Users(models.Model): domain = [('parent_id', '=', False), ('state', '=', 'pending'), ('create_uid', '=', user.id)] user.forum_waiting_posts_count = Post.search_count(domain) - @api.model - def _generate_forum_token(self, user_id, email): - """Return a token for email validation. This token is valid for the day - and is a hash based on a (secret) uuid generated by the forum module, - the user_id, the email and currently the day (to be updated if necessary). """ - forum_uuid = self.env['ir.config_parameter'].sudo().get_param('website_forum.uuid') - return hashlib.sha256((u'%s-%s-%s-%s' % ( - datetime.now().replace(hour=0, minute=0, second=0, microsecond=0), - forum_uuid, - user_id, - email - )).encode('utf-8')).hexdigest() - - @api.one - def _send_forum_validation_email(self, forum_id=None): - if not self.email: - return False - token = self._generate_forum_token(self.id, self.email) - activation_template = self.env.ref('website_forum.validation_email') - if activation_template: - params = { - 'token': token, - 'id': self.id, - 'email': self.email} - if forum_id: - params['forum_id'] = forum_id - base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') - token_url = base_url + '/forum/validate_email?%s' % urls.url_encode(params) - with self._cr.savepoint(): - activation_template.sudo().with_context(token_url=token_url).send_mail( - self.id, force_send=True, raise_exception=True) - return True - - @api.one - def process_forum_validation_token(self, token, email, forum_id=None, context=None): - validation_token = self._generate_forum_token(self.id, email) - if token == validation_token and self.karma == 0: - karma = 3 - forum = None - if forum_id: - forum = self.env['forum.forum'].browse(forum_id) - else: - forum_ids = self.env['forum.forum'].search([], limit=1) - if forum_ids: - forum = forum_ids[0] - if forum: - # karma gained: karma to ask a question and have 2 downvotes - karma = forum.karma_ask + (-2 * forum.karma_gen_question_downvote) - return self.write({'karma': karma}) - return False - # Wrapper for call_kw with inherits @api.multi def open_website_url(self): diff --git a/addons/website_forum/static/src/js/website_forum.js b/addons/website_forum/static/src/js/website_forum.js index 613163a045a..cfca0be8454 100644 --- a/addons/website_forum/static/src/js/website_forum.js +++ b/addons/website_forum/static/src/js/website_forum.js @@ -6,6 +6,7 @@ var Wysiwyg = require('web_editor.wysiwyg.root'); var sAnimations = require('website.content.snippets.animation'); var session = require('web.session'); var qweb = core.qweb; +var WebsiteProfile = require('website_profile.website_profile'); var _t = core._t; @@ -29,8 +30,6 @@ sAnimations.registry.websiteForum = sAnimations.Class.extend({ 'click .favourite_question': '_onFavoriteQuestionClick', 'click .comment_delete': '_onDeleteCommentClick', 'click .notification_close': '_onCloseNotificationClick', - 'click .send_validation_email': '_onSendValidationEmailClick', - 'click .validated_email_close': '_onCloseValidatedEmailClick', 'click .js_close_intro': '_onCloseIntroClick', }, @@ -465,32 +464,6 @@ sAnimations.registry.websiteForum = sAnimations.Class.extend({ }); } }, - /** - * @private - * @param {Event} ev - */ - _onSendValidationEmailClick: function (ev) { - ev.preventDefault(); - var $link = $(ev.currentTarget); - this._rpc({ - route: '/forum/send_validation_email', - params: { - forum_id: $link.attr('forum-id'), - }, - }).then(function (data) { - if (data) { - $('button.validation_email_close').click(); - } - }); - }, - /** - * @private - */ - _onCloseValidatedEmailClick: function () { - this._rpc({ - route: '/forum/validate_email/close', - }); - }, /** * @private * @param {Event} ev @@ -579,4 +552,5 @@ sAnimations.registry.websiteForumSpam = sAnimations.Class.extend({ }); }, }); + }); diff --git a/addons/website_forum/views/website_forum.xml b/addons/website_forum/views/website_forum.xml index 482a7d9541b..1c4755f7092 100644 --- a/addons/website_forum/views/website_forum.xml +++ b/addons/website_forum/views/website_forum.xml @@ -52,22 +52,12 @@
-
- -
- -
-

Congratulations! Your email has just been validated. You may now participate to our forums.

-
-
+
+ + + + +
diff --git a/addons/website_profile/__manifest__.py b/addons/website_profile/__manifest__.py index 1214d3f03e8..fbb57d126fd 100644 --- a/addons/website_profile/__manifest__.py +++ b/addons/website_profile/__manifest__.py @@ -12,6 +12,7 @@ 'gamification' ], 'data': [ + 'data/profile_data.xml', 'views/gamification_badge_views.xml', 'views/website_profile.xml', 'security/ir.model.access.csv', diff --git a/addons/website_profile/controllers/main.py b/addons/website_profile/controllers/main.py index 4d0e58efbe8..b29ba2077fb 100644 --- a/addons/website_profile/controllers/main.py +++ b/addons/website_profile/controllers/main.py @@ -57,6 +57,8 @@ class WebsiteProfile(http.Controller): values = { 'user': request.env.user, 'is_public_user': request.website.is_public_user(), + 'validation_email_sent': request.session.get('validation_email_sent', False), + 'validation_email_done': request.session.get('validation_email_done', False), } values.update(kwargs) return values @@ -111,8 +113,9 @@ class WebsiteProfile(http.Controller): user = self._check_user_profile_access(user_id) if not user: return request.render("website_profile.private_profile") + values = self._prepare_user_values(**post) params = self._prepare_user_profile_parameters(**post) - values = self._prepare_user_profile_values(user, **params) + values.update(self._prepare_user_profile_values(user, **params)) return request.render("website_profile.user_profile_main", values) # Edit Profile @@ -234,3 +237,26 @@ class WebsiteProfile(http.Controller): 'pager': pager } return request.render("website_profile.users_page_main", values) + + # User and validation + # -------------------------------------------------- + + @http.route('/profile/send_validation_email', type='json', auth='user', website=True) + def send_validation_email(self, **kwargs): + if request.env.uid != request.website.user_id.id: + request.env.user._send_profile_validation_email(**kwargs) + request.session['validation_email_sent'] = True + return True + + @http.route('/profile/validate_email', type='http', auth='public', website=True, sitemap=False) + def validate_email(self, token, user_id, email, **kwargs): + done = request.env['res.users'].sudo().browse(int(user_id))._process_profile_validation_token(token, email) + if done: + request.session['validation_email_done'] = True + url = kwargs.get('redirect_url', '/') + return request.redirect(url) + + @http.route('/profile/validate_email/close', type='json', auth='public', website=True) + def validate_email_done(self, **kwargs): + request.session['validation_email_done'] = False + return True diff --git a/addons/website_profile/data/profile_data.xml b/addons/website_profile/data/profile_data.xml new file mode 100644 index 00000000000..5331fa23e8e --- /dev/null +++ b/addons/website_profile/data/profile_data.xml @@ -0,0 +1,106 @@ + + + + + + + Profile: Email Verification + + ${object.company_id.name} Profile validation + ${('<%s>' % (object.env.user.company_id.email or user.email)) | safe} + ${object.email|safe} + + + + +
+ + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + ${object.company_id.name} Profile validation + + + ${user.company_id.name} +
+
+
+
+ + + + + + + +
+

+ Hello ${object.name},

+ You have been invited to validate your email in order to get access to "${object.company_id.name}" website. + To validate your email, please click on the following link: +

+ Thanks for your participation! +

+
+
+
+
+ + + + + +
+ ${user.company_id.name} + + ${user.company_id.phone} + % if user.company_id.email: + | + ${user.company_id.email} + + % endif + % if user.company_id.website: + | + ${user.company_id.website} + + % endif +
+
+
+ + +
+ Powered by Odoo +
+
+
+
+
+
diff --git a/addons/website_profile/models/__init__.py b/addons/website_profile/models/__init__.py index c26e78d1255..e5d082ad113 100644 --- a/addons/website_profile/models/__init__.py +++ b/addons/website_profile/models/__init__.py @@ -3,3 +3,4 @@ from . import gamification_badge from . import website +from . import res_users diff --git a/addons/website_profile/models/res_users.py b/addons/website_profile/models/res_users.py new file mode 100644 index 00000000000..5bf49be0a78 --- /dev/null +++ b/addons/website_profile/models/res_users.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import hashlib +import uuid + +from datetime import datetime +from werkzeug import urls +from odoo import api, models + +VALIDATION_KARMA_GAIN = 3 + + +class Users(models.Model): + _inherit = 'res.users' + + @api.model + def _generate_profile_token(self, user_id, email): + """Return a token for email validation. This token is valid for the day + and is a hash based on a (secret) uuid generated by the forum module, + the user_id, the email and currently the day (to be updated if necessary). """ + profile_uuid = self.env['ir.config_parameter'].sudo().get_param('website_profile.uuid') + if not profile_uuid: + profile_uuid = str(uuid.uuid4()) + self.env['ir.config_parameter'].sudo().set_param('website_profile.uuid', profile_uuid) + return hashlib.sha256((u'%s-%s-%s-%s' % ( + datetime.now().replace(hour=0, minute=0, second=0, microsecond=0), + profile_uuid, + user_id, + email + )).encode('utf-8')).hexdigest() + + def _send_profile_validation_email(self, **kwargs): + if not self.email: + return False + token = self._generate_profile_token(self.id, self.email) + activation_template = self.env.ref('website_profile.validation_email') + if activation_template: + params = { + 'token': token, + 'user_id': self.id, + 'email': self.email + } + params.update(kwargs) + base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') + token_url = base_url + '/profile/validate_email?%s' % urls.url_encode(params) + with self._cr.savepoint(): + activation_template.sudo().with_context(token_url=token_url).send_mail( + self.id, force_send=True, raise_exception=True) + return True + + @api.multi + def _process_profile_validation_token(self, token, email): + self.ensure_one() + validation_token = self._generate_profile_token(self.id, email) + if token == validation_token and self.karma == 0: + return self.write({'karma': VALIDATION_KARMA_GAIN}) + return False diff --git a/addons/website_profile/static/src/js/website_profile.js b/addons/website_profile/static/src/js/website_profile.js new file mode 100644 index 00000000000..a3cc18f53f5 --- /dev/null +++ b/addons/website_profile/static/src/js/website_profile.js @@ -0,0 +1,47 @@ +odoo.define('website_profile.website_profile', function (require) { +'use strict'; + +var sAnimations = require('website.content.snippets.animation'); + +//TODO DBE : to change into publicWidget = require('web.public.widget'); after forward port in master +sAnimations.registry.websiteProfile = sAnimations.Class.extend({ + selector: '.o_wprofile_email_validation_container', + read_events: { + 'click .send_validation_email': '_onSendValidationEmailClick', + 'click .validated_email_close': '_onCloseValidatedEmailClick', + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + /** + * @private + * @param {Event} ev + */ + _onSendValidationEmailClick: function (ev) { + ev.preventDefault(); + var self = this; + var $element = $(ev.currentTarget); + this._rpc({ + route: '/profile/send_validation_email', + params: {'redirect_url': $element.data('redirect_url')}, + }).then(function (data) { + if (data) { + self.$('button.validation_email_close').click(); + } + }); + }, + + /** + * @private + */ + _onCloseValidatedEmailClick: function () { + this._rpc({ + route: '/profile/validate_email/close', + }); + }, +}); + +return sAnimations.registry.websiteProfile; + +}); diff --git a/addons/website_profile/views/website_profile.xml b/addons/website_profile/views/website_profile.xml index 2a108ef8906..fc9e6aae113 100644 --- a/addons/website_profile/views/website_profile.xml +++ b/addons/website_profile/views/website_profile.xml @@ -4,6 +4,9 @@ + +