summaryrefslogtreecommitdiff
path: root/addons/website_profile
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2019-03-25 10:05:27 +0000
committerChristophe Simonis <chs@odoo.com>2019-03-25 11:13:41 +0000
commit5df4746c9aa144e6e3fce44fea43dac1089a5c51 (patch)
treef4ce932a76563e26b8d131805d49b405fcf9ad8f /addons/website_profile
parent39e71353c5d58f7af6b3270911a24d98b73cad4e (diff)
parent230ad8c381e927025efabff0f5f8c2debc432249 (diff)
[MERGE] forward port branch saas-12.2 up to 230ad8c381e
closes odoo/odoo#32088 Signed-off-by: Christophe Simonis <chs@odoo.com>
Diffstat (limited to 'addons/website_profile')
-rw-r--r--addons/website_profile/__manifest__.py1
-rw-r--r--addons/website_profile/controllers/main.py97
-rw-r--r--addons/website_profile/data/profile_data.xml106
-rw-r--r--addons/website_profile/i18n/website_profile.pot425
-rw-r--r--addons/website_profile/models/__init__.py1
-rw-r--r--addons/website_profile/models/res_users.py58
-rw-r--r--addons/website_profile/static/src/js/website_profile.js45
-rw-r--r--addons/website_profile/static/src/scss/website_profile.scss25
-rw-r--r--addons/website_profile/views/website_profile.xml227
9 files changed, 875 insertions, 110 deletions
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..9c6d31c2efc 100644
--- a/addons/website_profile/controllers/main.py
+++ b/addons/website_profile/controllers/main.py
@@ -6,6 +6,7 @@ import werkzeug
import werkzeug.exceptions
import werkzeug.urls
import werkzeug.wrappers
+import math
from odoo import http, modules, tools
from odoo.http import request
@@ -13,8 +14,9 @@ from odoo.osv import expression
class WebsiteProfile(http.Controller):
- PAGER_MAX_PAGE = 5
-
+ _users_per_page = 30
+ _pager_max_pages = 5
+
# Profile
# ---------------------------------------------------
@@ -57,6 +59,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 +115,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
@@ -158,48 +163,45 @@ class WebsiteProfile(http.Controller):
return werkzeug.utils.redirect("/profile/user/%d?%s" % (user.id, kwargs['url_param']))
else:
return werkzeug.utils.redirect("/profile/user/%d" % user.id)
- # Ranks
- # ---------------------------------------------------
- @http.route('/profile/ranks', type='http', auth="public", website=True)
- def ranks(self, **kwargs):
- Rank = request.env['gamification.karma.rank']
- ranks = Rank.sudo().search([])
- ranks = ranks.sorted(key=lambda b: b.karma_min)
- values = {
- 'ranks': ranks,
- 'user': request.env.user,
- }
- return request.render("website_profile.rank_main", values)
- # Badges
+ # Ranks and Badges
# ---------------------------------------------------
def _prepare_badges_domain(self, **kwargs):
"""
Hook for other modules to restrict the badges showed on profile page, depending of the context
"""
domain = [('website_published', '=', True)]
- if 'category' in kwargs:
- domain = expression.AND([[('challenge_ids.category', '=', kwargs.get('category'))], domain])
+ if 'badge_category' in kwargs:
+ domain = expression.AND([[('challenge_ids.category', '=', kwargs.get('badge_category'))], domain])
return domain
- @http.route('/profile/badge', type='http', auth="public", website=True)
- def badges(self, **kwargs):
+ @http.route('/profile/ranks_badges', type='http', auth="public", website=True)
+ def view_ranks_badges(self, **kwargs):
+ ranks = []
+ if 'badge_category' not in kwargs:
+ Rank = request.env['gamification.karma.rank']
+ ranks = Rank.sudo().search([], order='karma_min DESC')
+
Badge = request.env['gamification.badge']
badges = Badge.sudo().search(self._prepare_badges_domain(**kwargs))
badges = sorted(badges, key=lambda b: b.stat_count_distinct, reverse=True)
values = self._prepare_user_values(searches={'badges': True})
+
values.update({
+ 'ranks': ranks,
'badges': badges,
+ 'user': request.env.user,
})
- return request.render("website_profile.badge_main", values)
+ return request.render("website_profile.rank_badge_main", values)
# All Users Page
# ---------------------------------------------------
- def _prepare_all_users_values(self, user, position):
+ def _prepare_all_users_values(self, user):
return {
- 'position': position,
+ 'position': user.karma_position,
'id': user.id,
'name': user.name,
+ 'company_name': user.company_id.name,
'rank': user.rank_id.name,
'karma': user.karma,
'badge_count': len(user.badge_ids),
@@ -211,26 +213,45 @@ class WebsiteProfile(http.Controller):
User = request.env['res.users']
dom = [('karma', '>', 1), ('website_published', '=', True)]
- # Get the Top 3 users
- top3_users = User.sudo().search(dom, limit=3, order='karma DESC')
- top3_user_values = [self._prepare_all_users_values(user, position+1) for position, user in enumerate(top3_users)]
+ # Searches
+ search_term = searches.get('search')
+ if search_term:
+ dom = expression.AND([['|', ('name', 'ilike', search_term), ('company_id.name', 'ilike', search_term)], dom])
- # Get the other users
- if top3_users:
- dom += [('id', 'not in', top3_users.ids)]
- step = 30
user_count = User.sudo().search_count(dom)
- pager = request.website.pager(url="/profile/users", total=user_count, page=page, step=step, scope=step)
+ page_count = math.ceil(user_count / self._users_per_page)
+ pager = request.website.pager(url="/profile/users", total=user_count, page=page, step=self._users_per_page,
+ scope=page_count if page_count < self._pager_max_pages else self._pager_max_pages)
- if searches.get('user'):
- dom += [('name', 'ilike', searches.get('user'))]
+ users = User.sudo().search(dom, limit=self._users_per_page, offset=pager['offset'], order='karma DESC')
+ user_values = [self._prepare_all_users_values(user) for user in users]
- users = User.sudo().search(dom, limit=step, offset=pager['offset'], order='karma DESC')
-
- user_values = [self._prepare_all_users_values(user, position + 4 + ((page-1) * step)) for position, user in enumerate(users)]
values = {
- 'top3_users': top3_user_values,
- 'users': user_values,
+ 'top3_users': user_values[:3] if not search_term and page == 1 else None,
+ 'users': user_values[3:] if not search_term and page == 1 else user_values,
'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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <data noupdate="1">
+
+ <!-- Email template for email validation (for karma purpose) -->
+ <record id="validation_email" model="mail.template">
+ <field name="name">Profile: Email Verification</field>
+ <field name="model_id" ref="base.model_res_users"/>
+ <field name="subject">${object.company_id.name} Profile validation</field>
+ <field name="email_from">${('&lt;%s&gt;' % (object.env.user.company_id.email or user.email)) | safe}</field>
+ <field name="email_to">${object.email|safe}</field>
+ <field name="body_html" type="html">
+<table border="0" cellpadding="0" cellspacing="0" style="padding-top: 16px; background-color: #F1F1F1; color: #454748; width: 100%; border-collapse:separate;"><tr><td align="center">
+<table border="0" cellpadding="0" cellspacing="0" width="590" style="padding: 16px; background-color: white; color: #454748; border-collapse:separate;">
+<tbody>
+ <!-- HEADER -->
+ <tr>
+ <td align="center" style="min-width: 590px;">
+ <table border="0" cellpadding="0" cellspacing="0" width="590" style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;">
+ <tr>
+ <td valign="middle">
+ <span style="font-size: 20px; font-weight: bold;">
+ ${object.company_id.name} Profile validation
+ </span>
+ </td>
+ <td valign="middle" align="right">
+ <img src="/logo.png?company=${user.company_id.id}" style="padding: 0px; margin: 0px; height: auto; width: 80px;" alt="${user.company_id.name}"/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2" style="text-align:center;">
+ <hr width="100%" style="background-color:rgb(204,204,204);border:medium none;clear:both;display:block;font-size:0px;min-height:1px;line-height:0; margin:16px 0px 16px 0px;"/>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <!-- CONTENT -->
+ <tr>
+ <td align="center" style="min-width: 590px;">
+ <table border="0" cellpadding="0" cellspacing="0" width="590" style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;">
+ <tr>
+ <td valign="top" style="font-size: 13px;">
+ <p style="margin: 0px; padding: 0px; font-size: 13px;">
+ Hello ${object.name},<br /><br />
+ 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:
+ <div style="margin: 16px 0px 16px 0px;">
+ <a href="${ctx.get('token_url')}"
+ style="background-color: #875A7B; padding: 8px 16px 8px 16px; text-decoration: none; color: #fff; border-radius: 5px; font-size:13px;">
+ Validate my account
+ </a>
+ </div>
+ Thanks for your participation!
+ </p>
+ </td>
+ </tr>
+ <tr>
+ <td style="text-align:center;">
+ <hr width="100%" style="background-color:rgb(204,204,204);border:medium none;clear:both;display:block;font-size:0px;min-height:1px;line-height:0; margin: 16px 0px 16px 0px;"/>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <!-- FOOTER -->
+ <tr>
+ <td align="center" style="min-width: 590px;">
+ <table border="0" cellpadding="0" cellspacing="0" width="590" style=" font-family: 'Verdana Regular'; color: #454748; min-width: 590px; background-color: white; font-size: 11px; padding: 0px 8px 0px 8px; border-collapse:separate;">
+ <tr>
+ <td valign="middle" align="left">
+ ${user.company_id.name}
+ </td>
+ <td valign="middle" align="right" style="opacity: 0.7;">
+ ${user.company_id.phone}
+ % if user.company_id.email:
+ | <a href="'mailto:%s' % ${user.company_id.email}" style="text-decoration:none; color: #454748;">
+ ${user.company_id.email}
+ </a>
+ % endif
+ % if user.company_id.website:
+ | <a href="${user.company_id.website}" style="text-decoration:none; color: #454748;">
+ ${user.company_id.website}
+ </a>
+ % endif
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+</tbody>
+</table>
+</td></tr>
+<!-- POWERED BY -->
+<tr><td align="center" style="min-width: 590px;">
+ <table border="0" cellpadding="0" cellspacing="0" width="590" style="min-width: 590px; background-color: #F1F1F1; color: #454748; padding: 8px; border-collapse:separate;">
+ <tr><td style="text-align: center; font-size: 13px;">
+ Powered by <a target="_blank" href="https://www.odoo.com?utm_source=db&amp;utm_medium=forum" style="color: #875A7B;">Odoo</a>
+ </td></tr>
+ </table>
+</td></tr>
+</table>
+ </field>
+ </record>
+ </data>
+</odoo>
diff --git a/addons/website_profile/i18n/website_profile.pot b/addons/website_profile/i18n/website_profile.pot
new file mode 100644
index 00000000000..7237f491fd4
--- /dev/null
+++ b/addons/website_profile/i18n/website_profile.pot
@@ -0,0 +1,425 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * website_profile
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server saas~12.2\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2019-03-20 14:19+0000\n"
+"PO-Revision-Date: 2019-03-20 14:19+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: website_profile
+#: model:mail.template,subject:website_profile.validation_email
+msgid "${object.company_id.name} Profile validation"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.email_validation_banner
+msgid "&amp;times;"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_header
+msgid "(not verified)"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.rank_badge_main
+msgid ". Collect points on the forum or on the eLearning platform. Those points will make you reach new ranks."
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_content
+msgid "40%"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_badges
+msgid "<i class=\"fa fa-arrow-right mr-1\"/>Check all available badges"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_badges
+msgid "<i class=\"fa fa-arrow-right\"/> Check available badges"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_edit_content
+msgid "<i class=\"fa fa-pencil fa-1g float-sm-none float-md-left\" title=\"Edit\"/>"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_header
+msgid "<i class=\"fa fa-pencil mr-1\"/>EDIT"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_header
+msgid "<i class=\"fa fa-pencil mr-2\"/>EDIT PROFILE"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.badge_content
+msgid "<i class=\"text-muted\"> awarded users</i>"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_content
+msgid "<small class=\"font-weight-bold mr-2\">Rank:</small>"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_content
+msgid "<small class=\"font-weight-bold mt-1\">Next rank:</small>"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_content
+msgid "<small class=\"font-weight-bold\">Badges</small>"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_content
+msgid "<small class=\"font-weight-bold\">Joined</small>"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_edit_content
+msgid "<span class=\"font-weight-bold\">Biography</span>"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_edit_content
+msgid "<span class=\"font-weight-bold\">City</span>"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_edit_content
+msgid "<span class=\"font-weight-bold\">Country</span>"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_edit_content
+msgid "<span class=\"font-weight-bold\">Email</span>"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_edit_content
+msgid "<span class=\"font-weight-bold\">Public profile</span>"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_edit_content
+msgid "<span class=\"font-weight-bold\">Real name</span>"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_edit_content
+msgid "<span class=\"font-weight-bold\">Website</span>"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.all_user_card
+msgid "<span class=\"text-muted small font-weight-bold\">Badges</span>"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.all_user_card
+msgid "<span class=\"text-muted small font-weight-bold\">XP</span>"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.top3_user_card
+msgid "<span class=\"text-muted\">Badges</span>"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.top3_user_card
+msgid "<span class=\"text-muted\">XP</span>"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.email_validation_banner
+msgid "<span id=\"email_validated_message\">Congratulations! Your email has just been validated.</span>"
+msgstr ""
+
+#. module: website_profile
+#: model:mail.template,body_html:website_profile.validation_email
+msgid "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"padding-top: 16px; background-color: #F1F1F1; color: #454748; width: 100%; border-collapse:separate;\"><tr><td align=\"center\">\n"
+"<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"590\" style=\"padding: 16px; background-color: white; color: #454748; border-collapse:separate;\">\n"
+"<tbody>\n"
+" <!-- HEADER -->\n"
+" <tr>\n"
+" <td align=\"center\" style=\"min-width: 590px;\">\n"
+" <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"590\" style=\"min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;\">\n"
+" <tr>\n"
+" <td valign=\"middle\">\n"
+" <span style=\"font-size: 20px; font-weight: bold;\">\n"
+" ${object.company_id.name} Profile validation\n"
+" </span>\n"
+" </td>\n"
+" <td valign=\"middle\" align=\"right\">\n"
+" <img src=\"/logo.png?company=${user.company_id.id}\" style=\"padding: 0px; margin: 0px; height: auto; width: 80px;\" alt=\"${user.company_id.name}\"/>\n"
+" </td>\n"
+" </tr>\n"
+" <tr>\n"
+" <td colspan=\"2\" style=\"text-align:center;\">\n"
+" <hr width=\"100%\" style=\"background-color:rgb(204,204,204);border:medium none;clear:both;display:block;font-size:0px;min-height:1px;line-height:0; margin:16px 0px 16px 0px;\"/>\n"
+" </td>\n"
+" </tr>\n"
+" </table>\n"
+" </td>\n"
+" </tr>\n"
+" <!-- CONTENT -->\n"
+" <tr>\n"
+" <td align=\"center\" style=\"min-width: 590px;\">\n"
+" <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"590\" style=\"min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;\">\n"
+" <tr>\n"
+" <td valign=\"top\" style=\"font-size: 13px;\">\n"
+" <p style=\"margin: 0px; padding: 0px; font-size: 13px;\">\n"
+" Hello ${object.name},<br/><br/>\n"
+" You have been invited to validate your email in order to get access to \"${object.company_id.name}\" website.\n"
+" To validate your email, please click on the following link:\n"
+" <div style=\"margin: 16px 0px 16px 0px;\">\n"
+" <a href=\"${ctx.get('token_url')}\" style=\"background-color: #875A7B; padding: 8px 16px 8px 16px; text-decoration: none; color: #fff; border-radius: 5px; font-size:13px;\">\n"
+" Validate my account\n"
+" </a>\n"
+" </div>\n"
+" Thanks for your participation!\n"
+" </p>\n"
+" </td>\n"
+" </tr>\n"
+" <tr>\n"
+" <td style=\"text-align:center;\">\n"
+" <hr width=\"100%\" style=\"background-color:rgb(204,204,204);border:medium none;clear:both;display:block;font-size:0px;min-height:1px;line-height:0; margin: 16px 0px 16px 0px;\"/>\n"
+" </td>\n"
+" </tr>\n"
+" </table>\n"
+" </td>\n"
+" </tr>\n"
+" <!-- FOOTER -->\n"
+" <tr>\n"
+" <td align=\"center\" style=\"min-width: 590px;\">\n"
+" <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"590\" style=\" font-family: 'Verdana Regular'; color: #454748; min-width: 590px; background-color: white; font-size: 11px; padding: 0px 8px 0px 8px; border-collapse:separate;\">\n"
+" <tr>\n"
+" <td valign=\"middle\" align=\"left\">\n"
+" ${user.company_id.name}\n"
+" </td>\n"
+" <td valign=\"middle\" align=\"right\" style=\"opacity: 0.7;\">\n"
+" ${user.company_id.phone}\n"
+" % if user.company_id.email:\n"
+" | <a href=\"'mailto:%s' % ${user.company_id.email}\" style=\"text-decoration:none; color: #454748;\">\n"
+" ${user.company_id.email}\n"
+" </a>\n"
+" % endif\n"
+" % if user.company_id.website:\n"
+" | <a href=\"${user.company_id.website}\" style=\"text-decoration:none; color: #454748;\">\n"
+" ${user.company_id.website}\n"
+" </a>\n"
+" % endif\n"
+" </td>\n"
+" </tr>\n"
+" </table>\n"
+" </td>\n"
+" </tr>\n"
+"</tbody>\n"
+"</table>\n"
+"</td></tr>\n"
+"<!-- POWERED BY -->\n"
+"<tr><td align=\"center\" style=\"min-width: 590px;\">\n"
+" <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"590\" style=\"min-width: 590px; background-color: #F1F1F1; color: #454748; padding: 8px; border-collapse:separate;\">\n"
+" <tr><td style=\"text-align: center; font-size: 13px;\">\n"
+" Powered by <a target=\"_blank\" href=\"https://www.odoo.com?utm_source=db&amp;utm_medium=forum\" style=\"color: #875A7B;\">Odoo</a>\n"
+" </td></tr>\n"
+" </table>\n"
+"</td></tr>\n"
+"</table>\n"
+" "
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_content
+msgid "About"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.users_page_header
+msgid "All Users"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.badge_content
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_content
+msgid "Badges"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.badge_content
+msgid "Besides gaining reputation with your questions and answers,\n"
+" you receive badges for being especially helpful.<br class=\"d-none d-lg-inline-block\"/>Badges\n"
+" appear on your profile page, and your posts."
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_content
+msgid "Biography"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_edit_content
+msgid "Clear"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.email_validation_banner
+msgid "Close"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_edit_content
+msgid "Country..."
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_edit_content
+msgid "Edit"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_edit_content
+msgid "Edit Profile"
+msgstr ""
+
+#. module: website_profile
+#: model:ir.model,name:website_profile.model_gamification_badge
+msgid "Gamification Badge"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.users_page_header
+msgid "Home"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.rank_badge_main
+msgid "How do I earn badges?"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.rank_badge_main
+msgid "How do I score more points?"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.email_validation_banner
+msgid "It appears your email has not been verified."
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.rank_badge_main
+msgid "Keep learning with"
+msgstr ""
+
+#. module: website_profile
+#: model:ir.model.fields,field_description:website_profile.field_website__karma_profile_min
+msgid "Minimal karma to see other user's profile"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_content
+msgid "More info"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_badges
+msgid "No badge yet"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_edit_content
+msgid "Please enter a valid email address in order to receive notifications from answers or comments."
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.rank_badge_main
+msgid "Rank badge"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.rank_badge_main
+msgid "Ranks"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.private_profile
+msgid "Return to the website."
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.users_page_header
+msgid "Search"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.users_page_header
+msgid "Search in Users"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.private_profile
+msgid "This profile is private!"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.user_profile_edit_content
+msgid "Update"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.top3_user_card
+msgid "User rank"
+msgstr ""
+
+#. module: website_profile
+#: model:ir.model,name:website_profile.model_res_users
+#: model_terms:ir.ui.view,arch_db:website_profile.users_page_header
+msgid "Users"
+msgstr ""
+
+#. module: website_profile
+#: model:ir.model,name:website_profile.model_website
+msgid "Website"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.rank_badge_main
+msgid "When you finish a course or reach milestones, you're awarded badges."
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.rank_badge_main
+msgid "You can score more points by answering quizzes at the end of each course content. Points can also be earned on the forum. Follow this link to the guidelines of the forum."
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.users_page_header
+msgid "breadcrumb"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.rank_badge_main
+msgid "point"
+msgstr ""
+
+#. module: website_profile
+#: model_terms:ir.ui.view,arch_db:website_profile.profile_next_rank_card
+msgid "xp"
+msgstr ""
+
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..b1638f4b30e
--- /dev/null
+++ b/addons/website_profile/static/src/js/website_profile.js
@@ -0,0 +1,45 @@
+odoo.define('website_profile.website_profile', function (require) {
+'use strict';
+
+var publicWidget = require('web.public.widget');
+publicWidget.registry.websiteProfile = publicWidget.Widget.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 publicWidget.registry.websiteProfile;
+
+});
diff --git a/addons/website_profile/static/src/scss/website_profile.scss b/addons/website_profile/static/src/scss/website_profile.scss
index 224b46b1c84..ad1771c5d62 100644
--- a/addons/website_profile/static/src/scss/website_profile.scss
+++ b/addons/website_profile/static/src/scss/website_profile.scss
@@ -1,5 +1,5 @@
$owprofile-color-bg: desaturate(map-get($grays, "200"), 80%);
-$owprofile-tabs-height: 37px;
+$owprofile-tabs-height: 36px;
.o_wprofile_wrap {
background-color: $owprofile-color-bg;
@@ -118,10 +118,25 @@ $owprofile-tabs-height: 37px;
}
// All Users Page
-.o_wprofile_all_users_header {
- @include media-breakpoint-up(sm) {
- min-height: 200px;
- margin-bottom: -60px;
+.o_wprofile_all_users_nav {
+ border-width: 1px 0;
+
+ &, .o_wprofile_course_nav_search {
+ background-color: rgba(white, 0.05);
+ border-color: rgba(white, 0.1);
+ border-style: solid;
+ }
+
+ .o_wprofile_course_nav_search {
+ border-width: 0 1px;
+ }
+
+ .breadcrumb-item.active a, .breadcrumb-item a:hover {
+ color: white;
+ }
+
+ .breadcrumb-item a, .breadcrumb-item + .breadcrumb-item::before, .o_wprofile_course_nav_search input::placeholder {
+ color: rgba(white, 0.8);
}
}
diff --git a/addons/website_profile/views/website_profile.xml b/addons/website_profile/views/website_profile.xml
index 2a108ef8906..1a457a19958 100644
--- a/addons/website_profile/views/website_profile.xml
+++ b/addons/website_profile/views/website_profile.xml
@@ -4,8 +4,79 @@
<xpath expr="link[last()]" position="after">
<link rel="stylesheet" type="text/scss" href="/website_profile/static/src/scss/website_profile.scss"/>
</xpath>
+ <xpath expr="script[last()]" position="after">
+ <script type="text/javascript" src="/website_profile/static/src/js/website_profile.js"/>
+ </xpath>
</template>
+ <!-- Sub nav -->
+ <template id="user_profile_sub_nav" name="User profile subnav">
+ <div class="o_wprofile_all_users_nav">
+ <div class="container">
+ <div class="row align-items-center justify-content-between">
+ <!-- Desktop Mode -->
+ <nav aria-label="breadcrumb" class="col d-none d-md-flex">
+ <ol class="breadcrumb bg-transparent mb-0 pl-0 py-0">
+ <li class="breadcrumb-item">
+ <a t-att-href="home_url or '/'">Home</a>
+ </li>
+ <li t-attf-class="breadcrumb-item #{'active' if not view_user else ''}">
+ <a href="/profile/users">Users</a>
+ </li>
+ <li t-if="view_user" class="breadcrumb-item active">
+ <a><t t-esc="view_user"/></a>
+ </li>
+ </ol>
+ </nav>
+
+ <div class="col d-none d-md-flex flex-row align-items-center justify-content-end">
+ <!-- search -->
+ <form t-attf-action="/profile/users" role="search" method="get">
+ <div class="input-group o_wprofile_course_nav_search ml-1 position-relative">
+ <span class="input-group-prepend">
+ <button class="btn btn-link text-white rounded-0 pr-1" type="submit" aria-label="Search" title="Search">
+ <i class="fa fa-search"></i>
+ </button>
+ </span>
+ <input type="text" class="form-control border-0 rounded-0 bg-transparent text-white" name="search" placeholder="Search users"/>
+ </div>
+ </form>
+ </div>
+
+ <!-- Mobile Mode -->
+ <div class="col d-md-none py-1">
+ <div class="btn-group w-100 position-relative" role="group" aria-label="Mobile sub-nav">
+ <div class="btn-group w-100 ml-2">
+ <a class="btn bg-black-25 text-white dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Nav</a>
+
+ <ul class="dropdown-menu">
+ <a class="dropdown-item" t-att-href="home_url or '/'">Home</a>
+ <a class="dropdown-item" href="/profile/users">&#9492; Users</a>
+ <a t-if="view_user" class="dropdown-item">&#9492; <t t-esc="view_user"/></a>
+ </ul>
+ </div>
+
+ <div class="btn-group ml-1 position-static mr-2">
+ <a class="btn bg-black-25 text-white dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-search"></i></a>
+ <div class="dropdown-menu dropdown-menu-right w-100" style="right: 10px;">
+ <form class="px-3" t-attf-action="/profile/users" role="search" method="get">
+ <div class="input-group">
+ <input type="text" class="form-control" name="search" placeholder="Search courses"/>
+ <span class="input-group-append">
+ <button class="btn btn-primary" type="submit" aria-label="Search" title="Search">
+ <i class="fa fa-search"/>
+ </button>
+ </span>
+ </div>
+ </form>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </template>
<!--
Single User Profile Page
@@ -123,14 +194,18 @@
</template>
<template id="user_profile_header" name="Profile Page Header">
- <div class="o_wprofile_header o_wprofile_gradient position-relative text-white pb-3 pb-md-0 pt-2 pt-md-5">
- <div class="container">
+ <div class="o_wprofile_header o_wprofile_gradient position-relative text-white">
+ <t t-call="website_profile.user_profile_sub_nav">
+ <t t-set="view_user"><t t-esc="user.name"/></t>
+ </t>
+
+ <div class="container pb-3 pb-md-0 pt-2 pt-md-5">
<div class="row">
<!-- ==== Header Left ==== -->
<div class="col-12 col-md-4 col-lg-3">
<div t-attf-class="d-flex align-items-start #{'justify-content-between' if (request.env.user == user) else 'justify-content-around' }">
<div class="o_wprofile_pict d-inline-block mb-3 mb-md-0" t-attf-style="background-image:url(/web/image/res.users/#{user.id}/image)"/>
- <a class="btn btn-primary d-inline-block d-md-none" t-if="request.env.user == user" t-attf-href="/profile/edit?url_param=#{edit_button_url_param}">
+ <a class="btn btn-primary d-inline-block d-md-none" t-if="request.env.user == user and validation_email_done" t-attf-href="/profile/edit?url_param=#{edit_button_url_param}">
<i class="fa fa-pencil mr-1"/>EDIT
</a>
</div>
@@ -139,8 +214,10 @@
<!-- ==== Header Right ==== -->
<div class="col-12 col-md-8 col-lg-9 d-flex flex-column">
<div class="d-flex justify-content-between align-items-start">
- <h1 class="o_card_people_name" t-field="user.name"/>
- <a class="btn btn-primary d-none d-md-inline-block" t-if="request.env.user == user" t-attf-href="/profile/edit?url_param=#{edit_button_url_param}">
+ <h1 class="o_card_people_name">
+ <span t-field="user.name"/><small t-if="user.karma == 0"> (not verified)</small>
+ </h1>
+ <a class="btn btn-primary d-none d-md-inline-block" t-if="request.env.user == user and validation_email_done" t-attf-href="/profile/edit?url_param=#{edit_button_url_param}">
<i class="fa fa-pencil mr-2"/>EDIT PROFILE
</a>
</div>
@@ -164,17 +241,17 @@
</template>
<template id="user_profile_content" name="Profile Page Content">
- <div class="o_wprofile_content container">
+ <div class="container">
<div class="row">
<!-- ========== SIDEBAR ========== -->
<div class="col-12 col-md-4 col-lg-3 mt-3 mt-md-0">
<div class="bg-white px-3 py-2 py-md-3 mb-3 mb-md-5">
- <div class="o_wprofile_sidebar_top d-flex justify-content-between">
+ <div class="d-flex justify-content-between">
<div t-if="user.rank_id" class="d-flex align-items-center">
<small class="font-weight-bold mr-2">Rank:</small>
<img t-attf-src="/web/image/gamification.karma.rank/#{user.rank_id.id}/image_small" height="16" alt="" class="mr-1"/>
- <a href="/profile/ranks" t-field="user.rank_id"/>
+ <a href="/profile/ranks_badges" t-field="user.rank_id"/>
</div>
<button class="btn btn-sm d-md-none bg-white border" type="button" data-toggle="collapse" data-target="#o_wprofile_sidebar_collapse" aria-expanded="false" aria-controls="o_wprofile_sidebar_collapse">More info</button>
</div>
@@ -212,6 +289,12 @@
</ul>
<div class="tab-content py-4 o_wprofile_tabs_content mb-4" id="profile_extra_info_tabcontent">
<div role="tabpanel" class="tab-pane active" id="profile_tab_content_about">
+ <div class="o_wprofile_email_validation_container mb16 mt16">
+ <t t-call="website_profile.email_validation_banner">
+ <t t-set="redirect_url" t-value="'/profile/user/%s' % user.id"/>
+ <t t-set="send_validation_email_message" t-value="'Click here to send a verification email.'"/>
+ </t>
+ </div>
<div id="profile_about_badge" class="mb32">
<h5 class="border-bottom pb-1">Badges</h5>
<t t-call="website_profile.user_badges"></t>
@@ -277,18 +360,18 @@
<p class="text-muted">No badge yet</p>
</div>
<div class="text-right">
- <a t-if="not user.badge_ids and badge_category" t-attf-href="/profile/badge?category=#{badge_category}"><i class="fa fa-arrow-right"/> Check available badges</a>
- <a t-else="" href="/profile/badge" class="btn btn-link btn-sm"><i class="fa fa-arrow-right mr-1"/>Check all available badges</a>
+ <a t-if="not user.badge_ids and badge_category" t-attf-href="/profile/ranks_badges?badge_category=#{badge_category}"><i class="fa fa-arrow-right"/> Check available badges</a>
+ <a t-else="" href="/profile/ranks_badges" class="btn btn-link btn-sm"><i class="fa fa-arrow-right mr-1"/>Check all available badges</a>
</div>
</template>
- <!-- About Rank Page -->
- <template id="rank_main" name="Ranks Page">
+ <!-- About Ranks and badges Page -->
+ <template id="rank_badge_main" name="Ranks Page">
<t t-call="website.layout">
<div class="container mb32 mt48">
- <div class="row justify-content-between">
- <div class="col-12 col-md-6">
- <h1>Ranks &amp; Badges</h1>
+ <div class="row justify-content-between" t-if="ranks">
+ <div class="col-12 col-md-6 col-lg-5">
+ <h1>Ranks</h1>
<p class="lead mb-4">Keep learning with <t t-esc="user.company_id.name"/>. Collect points on the forum or on the eLearning platform. Those points will make you reach new ranks.</p>
<h5>How do I earn badges?</h5>
<p>When you finish a course or reach milestones, you're awarded badges.</p>
@@ -296,64 +379,55 @@
<p>You can score more points by answering quizzes at the end of each course content. Points can also be earned on the forum. Follow this link to the guidelines of the forum.</p>
</div>
<div class="col-12 col-md-5 col-lg-4">
- <t t-call="website_profile.rank_content"/>
- </div>
- </div>
- </div>
- </t>
- </template>
-
- <template id="rank_content" name="Rank Page content">
- <h5 class="text-muted my-2">Ranks</h5>
- <div class="row flex-column">
- <t t-foreach="ranks" t-as="rank">
- <t t-set="order_inverse" t-value="6 - rank.id"/>
-
- <div t-attf-class="col order-#{order_inverse}">
- <div class="media align-items-center py-2 border-top">
- <img height="50" t-attf-src="/web/image/gamification.karma.rank/#{rank.id}/image" class="mr-2" alt="Rank badge"/>
- <div class="media-body">
- <h5 class="mt-0 mb-0" t-field="rank.name"/>
- <span class="badge badge-success"><span t-field="rank.karma_min"/></span> point<span t-if="rank.karma_min">s</span>
+ <div class="card">
+ <div class="card-header border-bottom-0">Ranks</div>
+ <ul class="list-group list-group-flush">
+ <t t-foreach="ranks" t-as="rank">
+ <li t-attf-class="list-group-item">
+ <div class="media align-items-center">
+ <img height="40" t-attf-src="/web/image/gamification.karma.rank/#{rank.id}/image" class="mr-2" alt="Rank badge"/>
+ <div class="media-body">
+ <h5 class="mt-0 mb-0" t-field="rank.name"/>
+ <span class="badge badge-success"><span t-field="rank.karma_min"/></span> point<span t-if="rank.karma_min">s</span>
+ </div>
+ </div>
+ </li>
+ </t>
+ </ul>
</div>
</div>
</div>
- </t>
- </div>
- </template>
-
- <!-- About Badge Page -->
- <template id="badge_main" name="Badges Page">
- <t t-call="website.layout">
- <div class="container mb32 mt48">
- <t t-call="website_profile.badge_content">
- </t>
+ <t t-call="website_profile.badge_content"/>
</div>
</t>
</template>
<template id="badge_content" name="Badges Page content">
- <h1>Badges</h1>
- <p class="lead">
- Besides gaining reputation with your questions and answers,
- you receive badges for being especially helpful.<br/>Badges
- appear on your profile page, and your posts.
- </p>
- <table class="table table-sm mt32 mb64">
- <tr t-foreach="badges" t-as="badge">
- <td class="align-middle">
- <img t-attf-src="/website_profile/static/src/img/badge_#{badge.level}.svg" class="my-1" style="height:2.5em" alt=""/>
- <h6 t-field="badge.name" class="d-inline my-0"/>
- </td>
- <td class="align-middle d-none d-md-table-cell">
- <b t-esc="badge.stat_count_distinct"/>
- <i class="text-muted"> awarded users</i>
- </td>
- <td class="align-middle">
- <span t-field="badge.description"/>
- </td>
- </tr>
- </table>
+ <div class="row">
+ <div class="col-12">
+ <h1 class="mt-4 mt-lg-2">Badges</h1>
+ <p class="lead">
+ Besides gaining reputation with your questions and answers,
+ you receive badges for being especially helpful.<br class="d-none d-lg-inline-block"/>Badges
+ appear on your profile page, and your posts.
+ </p>
+ </div>
+ </div>
+ <table class="table table-sm mb64">
+ <tr t-foreach="badges" t-as="badge">
+ <td class="align-middle">
+ <img t-attf-src="/website_profile/static/src/img/badge_#{badge.level}.svg" class="my-1" style="height:2.5em" t-att-alt="badge.level"/>
+ <h6 t-field="badge.name" class="d-inline my-0"/>
+ </td>
+ <td class="align-middle d-none d-md-table-cell">
+ <b t-esc="badge.stat_count_distinct"/>
+ <i class="text-muted"> awarded users</i>
+ </td>
+ <td class="align-middle">
+ <span t-field="badge.description"/>
+ </td>
+ </tr>
+ </table>
</template>
<!--Private profile-->
@@ -381,9 +455,10 @@
</template>
<template id="users_page_header" name="Users Page Header">
- <div class="o_wprofile_all_users_header o_wprofile_gradient text-white">
+ <div class="o_wprofile_all_users_header o_wprofile_gradient mb-n5 pb-5">
+ <t t-call="website_profile.user_profile_sub_nav"/>
<div class="container">
- <h1 class="py-5">All Users</h1>
+ <h1 class="py-4 text-white">All Users</h1>
</div>
</div>
</template>
@@ -463,4 +538,22 @@
</ul>
</template>
+ <template id="email_validation_banner">
+ <t t-set="send_alert_classes" t-value="send_alert_classes if send_alert_classes else 'alert alert-danger alert-dismissable'"/>
+ <t t-set="done_alert_classes" t-value="done_alert_classes if done_alert_classes else 'alert alert-success alert-dismissable'"/>
+
+ <div t-if="not validation_email_sent and not is_public_user and user.karma == 0" t-att-class="send_alert_classes" role="alert">
+ <button type="button" class="close validation_email_close" data-dismiss="alert" aria-label="Close">&amp;times;</button>
+ It appears your email has not been verified.<br/>
+ <a class="send_validation_email alert-link" href="#" t-att-data-redirect_url="redirect_url">
+ <span t-esc="send_validation_email_message"/>
+ </a>
+ </div>
+ <div t-if="validation_email_done" t-att-class="done_alert_classes" role="status">
+ <button type="button" class="close validated_email_close" data-dismiss="alert" aria-label="Close">&amp;times;</button>
+ <span id="email_validated_message">Congratulations! Your email has just been validated.</span>
+ <span t-esc="additional_validated_email_message"/>
+ </div>
+ </template>
+
</data></odoo>