From ff870099c588e56e8f041f1d928edd779b1b719c Mon Sep 17 00:00:00 2001 From: David Beguin Date: Thu, 24 Jan 2019 11:02:12 +0000 Subject: [PATCH] [ADD] website_profile : add profile page template for website modules Move Profile part from website_forum into this module in order to be usable in other modules (like website_slides) that will depend of this module. Applies gamification.ranks on website_profile page. Show the current rank and the next rank but also the progression until the next rank. Adds also an 'about rank' page to allow users to know more about available ranks. The goal here is to make the profile page more modular and to allow other modules to add informations or modify easily link or part of profile page in order to be specifically contextualised for the module in question. Task ID : 1922159 PR #30988 --- addons/website_forum/__manifest__.py | 1 - .../static/src/scss/website_forum.scss | 22 - addons/website_profile/__init__.py | 1 + addons/website_profile/__manifest__.py | 3 +- .../website_profile/controllers/__init__.py | 4 + addons/website_profile/controllers/main.py | 136 +++++++ addons/website_profile/models/__init__.py | 1 + addons/website_profile/models/website.py | 10 + .../static/src/scss/website_profile.scss | 189 +++++++++ .../website_profile/views/website_profile.xml | 379 ++++++++++++++++++ 10 files changed, 722 insertions(+), 24 deletions(-) create mode 100644 addons/website_profile/controllers/__init__.py create mode 100644 addons/website_profile/controllers/main.py create mode 100644 addons/website_profile/models/website.py create mode 100644 addons/website_profile/static/src/scss/website_profile.scss create mode 100644 addons/website_profile/views/website_profile.xml diff --git a/addons/website_forum/__manifest__.py b/addons/website_forum/__manifest__.py index ef5b7afefae..4a9131205d9 100644 --- a/addons/website_forum/__manifest__.py +++ b/addons/website_forum/__manifest__.py @@ -14,7 +14,6 @@ Ask questions, get answers, no distractions 'depends': [ 'auth_signup', 'website_mail', - 'website_partner', 'website_profile', ], 'data': [ diff --git a/addons/website_forum/static/src/scss/website_forum.scss b/addons/website_forum/static/src/scss/website_forum.scss index 36b3c72e2ff..85aaa69317c 100644 --- a/addons/website_forum/static/src/scss/website_forum.scss +++ b/addons/website_forum/static/src/scss/website_forum.scss @@ -68,28 +68,6 @@ $bronze: #eea91e; max-width: 60%; } - .badge-gold { - color: $gold; - } - - .bg-gold { - background-color: $gold; - } - - .badge-silver { - color: $silver; - } - .bg-silver { - background-color: $silver; - } - - .badge-bronze { - color: $bronze; - } - .bg-bronze { - background-color: $bronze; - } - .oe_answer_true { color: theme-color('success'); &:hover { diff --git a/addons/website_profile/__init__.py b/addons/website_profile/__init__.py index dc5e6b693d1..8c67f893b37 100644 --- a/addons/website_profile/__init__.py +++ b/addons/website_profile/__init__.py @@ -2,3 +2,4 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import models +from . import controllers diff --git a/addons/website_profile/__manifest__.py b/addons/website_profile/__manifest__.py index 1575856c320..1214d3f03e8 100644 --- a/addons/website_profile/__manifest__.py +++ b/addons/website_profile/__manifest__.py @@ -8,11 +8,12 @@ 'summary': 'Access the website profile of the users', 'description': """Allows to acces the website profile of the users and see their statistics (karma, badges, etc..)""", 'depends': [ - 'website', + 'website_partner', 'gamification' ], 'data': [ 'views/gamification_badge_views.xml', + 'views/website_profile.xml', 'security/ir.model.access.csv', ], 'auto_install': False, diff --git a/addons/website_profile/controllers/__init__.py b/addons/website_profile/controllers/__init__.py new file mode 100644 index 00000000000..5d4b25db9c0 --- /dev/null +++ b/addons/website_profile/controllers/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import main diff --git a/addons/website_profile/controllers/main.py b/addons/website_profile/controllers/main.py new file mode 100644 index 00000000000..3372f6954e9 --- /dev/null +++ b/addons/website_profile/controllers/main.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import base64 +import werkzeug.exceptions +import werkzeug.urls +import werkzeug.wrappers + +from odoo import http +from odoo.http import request +from odoo.osv import expression + + +class WebsiteProfile(http.Controller): + # Profile + # --------------------------------------------------- + def _check_user_profile_access(self, user_id): + user_sudo = request.env['res.users'].sudo().browse(user_id) + if user_sudo.karma == 0 or not user_sudo.website_published or \ + (user_sudo.id != request.session.uid and request.env.user.karma < request.website.karma_profile_min): + return False + return user_sudo + + def _prepare_user_values(self, **kwargs): + values = { + 'user': request.env.user, + 'is_public_user': request.website.is_public_user(), + } + values.update(kwargs) + return values + + def _prepare_user_profile_parameters(self, **post): + return post + + def _prepare_user_profile_values(self, user, **post): + return { + 'uid': request.env.user.id, + 'user': user, + 'main_object': user, + 'is_profile_page': True, + 'edit_button_url_param': '', + } + + @http.route(['/profile/user/'], type='http', auth="public", website=True) + def view_user_profile(self, user_id, **post): + user = self._check_user_profile_access(user_id) + if not user: + return request.render("website_profile.private_profile") + params = self._prepare_user_profile_parameters(**post) + values = self._prepare_user_profile_values(user, **params) + return request.render("website_profile.user_profile_main", values) + + # Edit Profile + # --------------------------------------------------- + @http.route('/profile/edit', type='http', auth="user", website=True) + def view_user_profile_edition(self, **kwargs): + countries = request.env['res.country'].search([]) + values = self._prepare_user_values(searches=kwargs) + values.update({ + 'email_required': kwargs.get('email_required'), + 'countries': countries, + 'notifications': self._get_badge_granted_messages(), + 'url_param': kwargs.get('url_param'), + }) + return request.render("website_profile.user_profile_edit_main", values) + + def _profile_edition_preprocess_values(self, user, **kwargs): + values = { + 'name': kwargs.get('name'), + 'website': kwargs.get('website'), + 'email': kwargs.get('email'), + 'city': kwargs.get('city'), + 'country_id': int(kwargs.get('country')) if kwargs.get('country') else False, + 'website_description': kwargs.get('description'), + } + + if 'clear_image' in kwargs: + values['image'] = False + elif kwargs.get('ufile'): + image = kwargs.get('ufile').read() + values['image'] = base64.b64encode(image) + + if request.uid == user.id: # the controller allows to edit only its own privacy settings; use partner management for other cases + values['website_published'] = kwargs.get('website_published') == 'True' + return values + + @http.route('/profile/user/save', type='http', auth="user", methods=['POST'], website=True) + def save_edited_profile(self, **kwargs): + user = request.env.user + values = self._profile_edition_preprocess_values(user, **kwargs) + whitelisted_values = {key: values[key] for key in type(user).SELF_WRITEABLE_FIELDS if key in values} + user.write(whitelisted_values) + if kwargs.get('url_param'): + 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, + } + return request.render("website_profile.rank_main", values) + + # Badges + # --------------------------------------------------- + def _get_badge_granted_messages(self): + badge_subtype = request.env.ref('gamification.mt_badge_granted', raise_if_not_found=False) + msg = request.env['mail.message'] + if badge_subtype: + msg = msg.search([('subtype_id', '=', badge_subtype.id), ('needaction', '=', True)]) + return msg + + 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]) + return domain + + @http.route('/profile/badge', type='http', auth="public", website=True) + def badges(self, **kwargs): + 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({ + 'badges': badges, + }) + return request.render("website_profile.badge_main", values) diff --git a/addons/website_profile/models/__init__.py b/addons/website_profile/models/__init__.py index 980f26c8bd4..c26e78d1255 100644 --- a/addons/website_profile/models/__init__.py +++ b/addons/website_profile/models/__init__.py @@ -2,3 +2,4 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import gamification_badge +from . import website diff --git a/addons/website_profile/models/website.py b/addons/website_profile/models/website.py new file mode 100644 index 00000000000..76a37eba3d9 --- /dev/null +++ b/addons/website_profile/models/website.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import fields, models + + +class Website(models.Model): + _inherit = 'website' + + karma_profile_min = fields.Integer(string="Minimal karma to see other user's profile", default=150) diff --git a/addons/website_profile/static/src/scss/website_profile.scss b/addons/website_profile/static/src/scss/website_profile.scss new file mode 100644 index 00000000000..c636bd40d92 --- /dev/null +++ b/addons/website_profile/static/src/scss/website_profile.scss @@ -0,0 +1,189 @@ +$gold: #ffcc00; +$silver: #cccccc; +$bronze: #eea91e; + +.o_wprofile_container { + margin: auto; + width: 100%; + max-width: 1140px; + padding: 32px 32px 0px 32px; +} + +.o_wprofile_header { + margin: auto; + background-color: #875A7B !important; + border-bottom: 48px solid #66445e; + .o_wprofile_header_container { + top: 48px; + padding-top: 0px; + .o_wprofile_id { + font-size: 16px; + .o_wprofile_id_info { + margin-right: 36px; + } + } + .o_wprofile_pict { + .o_wprofile_photo { + width: 100%; + max-width: 256px; + } + } + } +} + +.o_wprofile_content { + margin: auto; + background-color: #f2f2f2 !important; + .o_wprofile_content_container{ + position: relative; + top: -80px; + .o_wprofile_navtabs_container { + .o_wprofile_nav_tabs { + border-bottom: 0px; + } + .o_wprofile_navlink { + color: white; + border-radius: 0px; + height: 48px; + font-weight: bold; + padding-top: 12px; + border-left: 2px solid #875A7B; + text-align: center; + width: 120px; + &:hover { + border: 0px; + border-radius: 0px; + background-color: #875A7B; + } + } + .o_wprofile_navlink.active { + color: #212529; + background-color: #f2f2f2; + border: 0px; + } + } + .o_wprofile_detail { + .o_wprofile_content_abstract { + top: 1px; + width: 256px; + max-width: 256px; + margin-left: 15px; + table { + width: 100%; + tr { + line-height: 32px; + } + th { + font-weight: normal; + } + } + .o_wprofile_rank { + background-color: white; + } + .o_wprofile_abstract_info { + background-color: white; + } + } + .o_wprofile_content_detail { + h4 { + border-bottom: 1px solid lightgrey; + color: grey; + margin-bottom: 16px; + } + .o_user_badges { + padding: 8px; + div { + padding: 8px; + } + img { + width: 48px; + height: 48px; + } + } + } + } + } +} + +// Rank +.o_wprofile_ranks_presentation { + background-color: lightgray; + .o_wprofile_rank_tile { + width: 20%; + div { + text-align:center; + img { + height: 128px; + } + } + } +} + +.o_wprofile_next_rank_card { + div { + text-align: center; + img { + width: 128px; + height: 128px; + } + } + border-bottom: 1px solid lightgray; + .o_wprofile_next_rank_progress { + background-color: lightgray; + .o_wprofile_next_rank_progress_bar { + background-color: #00A09D !important; + } + } +} + +// Edit Profile button +.o_wprofile_edit { + position: absolute; + top: 0px; + right: 0px; + padding-left: 16px; + padding-right: 16px; +} + +// Badges +.badge-gold { + color: $gold; +} +.bg-gold { + background-color: $gold; +} + +.badge-silver { + color: $silver; +} +.bg-silver { + background-color: $silver; +} + +.badge-bronze { + color: $bronze; +} +.bg-bronze { + background-color: $bronze; +} + +// Other stuffs +.o_wprofile_bb_grey { + border-bottom: 2px solid lightgrey; +} + +.country_flag { + display: inline-block; + margin-left: 2px; + max-height: 13px; + width: auto !important; +} + +.o_wprofile_img_small { + width: 64px; + height: 64px; +} +.o_wprofile_img_medium { + width: 96px; + height: 96px; +} diff --git a/addons/website_profile/views/website_profile.xml b/addons/website_profile/views/website_profile.xml new file mode 100644 index 00000000000..06562d0e697 --- /dev/null +++ b/addons/website_profile/views/website_profile.xml @@ -0,0 +1,379 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +