From cfbd8d1adaa04901c2792f7a3ec02bd66aab3dbd Mon Sep 17 00:00:00 2001 From: David Beguin Date: Mon, 11 Feb 2019 09:12:56 +0000 Subject: [PATCH 1/6] [IMP] gamification, website_forum : apply various improvements - add menu to configure ranks in gamification tools because was missing - move admin karma data in gamification : karma linked to gamification and not forum anymore - fix rank computation : next_rank_id could never been recomputed correctly if rank are created in a karma ascending order. Task ID : 1922159 PR #30988 --- addons/gamification/models/res_users.py | 6 ++++-- addons/gamification/views/gamification_karma_rank_views.xml | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/addons/gamification/models/res_users.py b/addons/gamification/models/res_users.py index 40bc7442b69..8f59f6c7591 100644 --- a/addons/gamification/models/res_users.py +++ b/addons/gamification/models/res_users.py @@ -74,8 +74,10 @@ class Users(models.Model): old_rank = user.rank_id for i in range(0, len(ranks)): if user.karma >= ranks[i]['karma_min']: - user.rank_id = ranks[i]['rank'].id - user.next_rank_id = ranks[i - 1]['rank'].id if i > 0 else False + user.write({ + 'rank_id': ranks[i]['rank'].id, + 'next_rank_id': ranks[i - 1]['rank'].id if i > 0 else False + }) break if old_rank != user.rank_id: user._rank_changed() diff --git a/addons/gamification/views/gamification_karma_rank_views.xml b/addons/gamification/views/gamification_karma_rank_views.xml index 1e12a283cce..f09d516ca88 100644 --- a/addons/gamification/views/gamification_karma_rank_views.xml +++ b/addons/gamification/views/gamification_karma_rank_views.xml @@ -67,5 +67,7 @@ + + From ff870099c588e56e8f041f1d928edd779b1b719c Mon Sep 17 00:00:00 2001 From: David Beguin Date: Thu, 24 Jan 2019 11:02:12 +0000 Subject: [PATCH 2/6] [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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 336a90e530062b02f3dc66880b0076171758d66e Mon Sep 17 00:00:00 2001 From: David Beguin Date: Thu, 24 Jan 2019 15:07:11 +0000 Subject: [PATCH 3/6] [REF] website_forum : use website_profile templates and adds forum specific information Adds forum specific information into the website_profile template page. Removes everything linked to profile that is already in website_profile module to avoid duplicates. As there can be more than one forum, the profile page shows everything linked to every forum, except if the forum id is given in url arguments. The old route (forum/forum-1/user/user_id) have been kept for backward compatibility reasons. Adds the 'Go to forum' button in the 'new rank reached' mail to encourage the users to continue to be active on the forum as well, to gain more karma point and improve there rank. Image rpc calls have been reviewed for profile page part to use only the standard way to get image -> web/image/model_name/id/image_size Task ID : 1922159 PR #30988 --- addons/website_forum/__manifest__.py | 1 + addons/website_forum/controllers/main.py | 196 ++---- addons/website_forum/models/forum.py | 2 +- addons/website_forum/models/res_users.py | 8 + .../static/src/scss/website_forum.scss | 3 - addons/website_forum/views/website_forum.xml | 583 ++++-------------- .../views/website_forum_profile.xml | 145 +++++ 7 files changed, 340 insertions(+), 598 deletions(-) create mode 100644 addons/website_forum/views/website_forum_profile.xml diff --git a/addons/website_forum/__manifest__.py b/addons/website_forum/__manifest__.py index 4a9131205d9..02a5fa87fa2 100644 --- a/addons/website_forum/__manifest__.py +++ b/addons/website_forum/__manifest__.py @@ -21,6 +21,7 @@ Ask questions, get answers, no distractions 'views/forum.xml', 'views/res_users_views.xml', 'views/website_forum.xml', + 'views/website_forum_profile.xml', 'views/ir_qweb.xml', 'security/ir.model.access.csv', 'data/badges_question.xml', diff --git a/addons/website_forum/controllers/main.py b/addons/website_forum/controllers/main.py index 0bc02133bbe..ea85c1de782 100644 --- a/addons/website_forum/controllers/main.py +++ b/addons/website_forum/controllers/main.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -import base64 import json import lxml import requests @@ -10,40 +9,30 @@ import werkzeug.wrappers from datetime import datetime -from odoo import http, modules, SUPERUSER_ID, tools, _ +from odoo import http, tools, _ from odoo.addons.http_routing.models.ir_http import slug from odoo.addons.website.models.ir_http import sitemap_qs2dom +from odoo.addons.website_profile.controllers.main import WebsiteProfile from odoo.http import request -class WebsiteForum(http.Controller): +class WebsiteForum(WebsiteProfile): _post_per_page = 10 _user_per_page = 30 - def _get_notifications(self): - badge_subtype = request.env.ref('gamification.mt_badge_granted') - if badge_subtype: - msg = request.env['mail.message'].search([('subtype_id', '=', badge_subtype.id), ('needaction', '=', True)]) - else: - msg = list() - return msg - - def _prepare_forum_values(self, forum=None, **kwargs): - values = { - 'user': request.env.user, - 'is_public_user': request.env.user.id == request.website.user_id.id, - 'notifications': self._get_notifications(), + def _prepare_user_values(self, **kwargs): + values = super(WebsiteForum, self)._prepare_user_values(**kwargs) + values['forum_welcome_message'] = request.httprequest.cookies.get('forum_welcome_message', False) + values.update({ 'header': kwargs.get('header', dict()), 'searches': kwargs.get('searches', dict()), - 'forum_welcome_message': request.httprequest.cookies.get('forum_welcome_message', False), 'validation_email_sent': request.session.get('validation_email_sent', False), 'validation_email_done': request.session.get('validation_email_done', False), - } - if forum: - values['forum'] = forum + }) + if kwargs.get('forum'): + values['forum'] = kwargs.get('forum') elif kwargs.get('forum_id'): values['forum'] = request.env['forum.forum'].browse(kwargs.pop('forum_id')) - values.update(kwargs) return values # User and validation @@ -165,7 +154,7 @@ class WebsiteForum(http.Controller): question_ids = Post.search(domain, limit=self._post_per_page, offset=pager['offset'], order=sorting) - values = self._prepare_forum_values(forum=forum, searches=post, header={'ask_hide': not forum.active}) + values = self._prepare_user_values(forum=forum, searches=post, header={'ask_hide': not forum.active}) values.update({ 'main_object': tag or forum, 'question_ids': question_ids, @@ -180,7 +169,7 @@ class WebsiteForum(http.Controller): @http.route(['''/forum//faq'''], type='http', auth="public", website=True) def forum_faq(self, forum, **post): - values = self._prepare_forum_values(forum=forum, searches=dict(), header={'is_guidelines': True}, **post) + values = self._prepare_user_values(forum=forum, searches=dict(), header={'is_guidelines': True}, **post) return request.render("website_forum.faq", values) @http.route('/forum/get_tags', type='http', auth="public", methods=['GET'], website=True, sitemap=False) @@ -210,7 +199,7 @@ class WebsiteForum(http.Controller): order_by = 'posts_count DESC' tags = request.env['forum.tag'].search(domain, limit=None, order=order_by) # prepare values and render template - values = self._prepare_forum_values(forum=forum, searches={'tags': True}, **post) + values = self._prepare_user_values(forum=forum, searches={'tags': True}, **post) values.update({ 'tags': tags, @@ -256,7 +245,7 @@ class WebsiteForum(http.Controller): redirect_url = "/forum/%s/question/%s" % (slug(forum), slug(question.parent_id)) return werkzeug.utils.redirect(redirect_url, 301) filters = 'question' - values = self._prepare_forum_values(forum=forum, searches=post) + values = self._prepare_user_values(forum=forum, searches=post) values.update({ 'main_object': question, 'question': question, @@ -284,7 +273,7 @@ class WebsiteForum(http.Controller): def question_ask_for_close(self, forum, question, **post): reasons = request.env['forum.post.reason'].search([('reason_type', '=', 'basic')]) - values = self._prepare_forum_values(**post) + values = self._prepare_user_values(**post) values.update({ 'question': question, 'forum': forum, @@ -327,7 +316,7 @@ class WebsiteForum(http.Controller): user = request.env.user if not user.email or not tools.single_email_re.match(user.email): return werkzeug.utils.redirect("/forum/%s/user/%s/edit?email_required=1" % (slug(forum), request.session.uid)) - values = self._prepare_forum_values(forum=forum, searches={}, header={'ask_hide': True}) + values = self._prepare_user_values(forum=forum, searches={}, header={'ask_hide': True}) return request.render("website_forum.new_question", values) @http.route(['/forum//new', @@ -390,7 +379,7 @@ class WebsiteForum(http.Controller): def post_edit(self, forum, post, **kwargs): tags = [dict(id=tag.id, name=tag.name) for tag in post.tag_ids] tags = json.dumps(tags) - values = self._prepare_forum_values(forum=forum) + values = self._prepare_user_values(forum=forum) values.update({ 'tags': tags, 'post': post, @@ -458,7 +447,7 @@ class WebsiteForum(http.Controller): domain = [('forum_id', '=', forum.id), ('state', '=', 'pending')] posts_to_validate_ids = Post.search(domain) - values = self._prepare_forum_values(forum=forum) + values = self._prepare_user_values(forum=forum) values.update({ 'posts_ids': posts_to_validate_ids, 'queue_type': 'validation', @@ -478,7 +467,7 @@ class WebsiteForum(http.Controller): domain += [('name', 'ilike', kwargs.get('spam_post'))] flagged_posts_ids = Post.search(domain, order='write_date DESC') - values = self._prepare_forum_values(forum=forum) + values = self._prepare_user_values(forum=forum) values.update({ 'posts_ids': flagged_posts_ids, 'queue_type': 'flagged', @@ -497,7 +486,7 @@ class WebsiteForum(http.Controller): domain = [('forum_id', '=', forum.id), ('state', '=', 'offensive'), ('active', '=', False)] offensive_posts_ids = Post.search(domain, order='write_date DESC') - values = self._prepare_forum_values(forum=forum) + values = self._prepare_user_values(forum=forum) values.update({ 'posts_ids': offensive_posts_ids, 'queue_type': 'offensive', @@ -530,7 +519,7 @@ class WebsiteForum(http.Controller): def post_ask_for_mark_as_offensive(self, forum, post, **kwargs): offensive_reasons = request.env['forum.post.reason'].search([('reason_type', '=', 'offensive')]) - values = self._prepare_forum_values(forum=forum) + values = self._prepare_user_values(forum=forum) values.update({ 'question': post, 'forum': forum, @@ -551,7 +540,6 @@ class WebsiteForum(http.Controller): # User # -------------------------------------------------- - @http.route(['''/forum//users''', '''/forum//users/page/'''], type='http', auth="public", website=True) @@ -568,11 +556,11 @@ class WebsiteForum(http.Controller): users = User.sudo().search(dom, limit=step, offset=pager['offset'], order='karma DESC') searches['users'] = 'True' - values = self._prepare_forum_values(forum=forum, searches=searches) + values = self._prepare_user_values(forum=forum, searches=searches) values .update({ 'users': users, 'main_object': forum, - 'notifications': self._get_notifications(), + 'notifications': self._get_badge_granted_messages(), 'pager': pager, }) @@ -586,77 +574,69 @@ class WebsiteForum(http.Controller): return werkzeug.utils.redirect("/forum/%s/user/%d" % (slug(forum), partner.user_ids[0].id)) return werkzeug.utils.redirect("/forum/%s" % slug(forum)) - @http.route(['/forum/user//avatar'], type='http', auth="public", website=True, sitemap=False) - def user_avatar(self, user_id=0, **post): - status, headers, content = request.env['ir.http'].sudo().binary_content( - model='res.users', id=user_id, field='image_medium', default_mimetype='image/png') - - if not content: - img_path = modules.get_module_resource('web', 'static/src/img', 'placeholder.png') - with open(img_path, 'rb') as f: - image = f.read() - content = base64.b64encode(image) - if status == 304: - return werkzeug.wrappers.Response(status=304) - image_base64 = base64.b64decode(content) - headers.append(('Content-Length', len(image_base64))) - response = request.make_response(image_base64, headers) - response.status = str(status) - return response - + # Profile + # ----------------------------------- @http.route(['/forum//user/'], type='http', auth="public", website=True) - def open_user(self, forum, user_id=0, **post): - User = request.env['res.users'] + def view_user_forum_profile(self, forum, user_id, **post): + return werkzeug.utils.redirect('/profile/user/' + str(user_id) + '?forum_id=' + str(forum.id)) + + def _prepare_user_profile_values(self, user, **post): + values = super(WebsiteForum, self)._prepare_user_profile_values(user, **post) + if not post.get('no_forum'): + if post.get('forum'): + forums = post['forum'] + elif post.get('forum_id'): + forums = request.env['forum.forum'].browse(int(post['forum_id'])) + values.update({'edit_button_url_param': 'forum_id=' + str(post['forum_id'])}) + else: + forums = request.env['forum.forum'].search([]) + + values.update(self._prepare_user_values(forum=forums[0] if len(forums) == 1 else True, **post)) + if forums: + values.update(self._prepare_open_forum_user(user, forums)) + return values + + def _prepare_open_forum_user(self, user, forums, **kwargs): Post = request.env['forum.post'] Vote = request.env['forum.post.vote'] Activity = request.env['mail.message'] Followers = request.env['mail.followers'] Data = request.env["ir.model.data"] - user = User.sudo().search([('id', '=', user_id)]) - current_user = request.env.user.sudo() - - # Users with high karma can see users with karma <= 0 for - # moderation purposes, IFF they have posted something (see below) - if (not user or (user.karma < 1 and current_user.karma < forum.karma_unlink_all)): - return werkzeug.utils.redirect("/forum/%s" % slug(forum)) - values = self._prepare_forum_values(forum=forum, **post) - # questions and answers by user user_question_ids = Post.search([ ('parent_id', '=', False), - ('forum_id', '=', forum.id), ('create_uid', '=', user.id)], + ('forum_id', 'in', forums.ids), ('create_uid', '=', user.id)], order='create_date desc') count_user_questions = len(user_question_ids) - - if (user_id != request.session.uid and not - (user.website_published or - (count_user_questions and current_user.karma > forum.karma_unlink_all))): - return request.render("website_forum.private_profile", values, status=404) + min_karma_unlink = min(forums.mapped('karma_unlink_all')) # limit length of visible posts by default for performance reasons, except for the high # karma users (not many of them, and they need it to properly moderate the forum) post_display_limit = None - if current_user.karma < forum.karma_unlink_all: + if request.env.user.karma < min_karma_unlink: post_display_limit = 20 user_questions = user_question_ids[:post_display_limit] user_answer_ids = Post.search([ ('parent_id', '!=', False), - ('forum_id', '=', forum.id), ('create_uid', '=', user.id)], + ('forum_id', 'in', forums.ids), ('create_uid', '=', user.id)], order='create_date desc') count_user_answers = len(user_answer_ids) user_answers = user_answer_ids[:post_display_limit] # showing questions which user following - post_ids = [follower.res_id for follower in Followers.sudo().search([('res_model', '=', 'forum.post'), ('partner_id', '=', user.partner_id.id)])] - followed = Post.search([('id', 'in', post_ids), ('forum_id', '=', forum.id), ('parent_id', '=', False)]) + post_ids = [follower.res_id for follower in Followers.sudo().search( + [('res_model', '=', 'forum.post'), ('partner_id', '=', user.partner_id.id)])] + followed = Post.search([('id', 'in', post_ids), ('forum_id', 'in', forums.ids), ('parent_id', '=', False)]) # showing Favourite questions of user. - favourite = Post.search([('favourite_ids', '=', user.id), ('forum_id', '=', forum.id), ('parent_id', '=', False)]) + favourite = Post.search( + [('favourite_ids', '=', user.id), ('forum_id', 'in', forums.ids), ('parent_id', '=', False)]) # votes which given on users questions and answers. - data = Vote.read_group([('forum_id', '=', forum.id), ('recipient_id', '=', user.id)], ["vote"], groupby=["vote"]) + data = Vote.read_group([('forum_id', 'in', forums.ids), ('recipient_id', '=', user.id)], ["vote"], + groupby=["vote"]) up_votes, down_votes = 0, 0 for rec in data: if rec['vote'] == '1': @@ -669,8 +649,10 @@ class WebsiteForum(http.Controller): # activity by user. model, comment = Data.get_object_reference('mail', 'mt_comment') - activities = Activity.search([('res_id', 'in', (user_question_ids + user_answer_ids).ids), ('model', '=', 'forum.post'), ('subtype_id', '!=', comment)], - order='date DESC', limit=100) + activities = Activity.search( + [('res_id', 'in', (user_question_ids + user_answer_ids).ids), ('model', '=', 'forum.post'), + ('subtype_id', '!=', comment)], + order='date DESC', limit=100) posts = {} for act in activities: @@ -680,15 +662,15 @@ class WebsiteForum(http.Controller): # TDE CLEANME MASTER: couldn't it be rewritten using a 'menu' key instead of one key for each menu ? if user == request.env.user: - post['my_profile'] = True + kwargs['my_profile'] = True else: - post['users'] = True + kwargs['users'] = True - values.update({ + values = { 'uid': request.env.user.id, 'user': user, 'main_object': user, - 'searches': post, + 'searches': kwargs, 'questions': user_questions, 'count_questions': count_user_questions, 'answers': user_answers, @@ -700,55 +682,11 @@ class WebsiteForum(http.Controller): 'activities': activities, 'posts': posts, 'vote_post': vote_ids, - }) - return request.render("website_forum.user_detail_full", values) - - @http.route('/forum//user//edit', type='http', auth="user", website=True) - def edit_profile(self, forum, user, **kwargs): - countries = request.env['res.country'].search([]) - values = self._prepare_forum_values(forum=forum, searches=kwargs) - values.update({ - 'email_required': kwargs.get('email_required'), - 'countries': countries, - 'notifications': self._get_notifications(), - }) - return request.render("website_forum.edit_profile", values) - - @http.route('/forum//user//save', type='http', auth="user", methods=['POST'], website=True) - def save_edited_profile(self, forum, 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'), + 'is_profile_page': True, + 'badge_category': 'forum', } - 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' - user.write(values) - return werkzeug.utils.redirect("/forum/%s/user/%d" % (slug(forum), user.id)) - - # Badges - # -------------------------------------------------- - - @http.route('''/forum//badge''', type='http', auth="public", website=True) - def badges(self, forum, **searches): - Badge = request.env['gamification.badge'] - badges = Badge.sudo().search([('challenge_ids.category', '=', 'forum')]) - badges = sorted(badges, key=lambda b: b.stat_count_distinct, reverse=True) - values = self._prepare_forum_values(forum=forum, searches={'badges': True}) - values.update({ - 'badges': badges, - }) - return request.render("website_forum.badge", values) + return values # Messaging # -------------------------------------------------- diff --git a/addons/website_forum/models/forum.py b/addons/website_forum/models/forum.py index f515f022961..68d48b8534f 100644 --- a/addons/website_forum/models/forum.py +++ b/addons/website_forum/models/forum.py @@ -397,7 +397,7 @@ class Post(models.Model): res = super(Post, self)._default_website_meta() res['default_opengraph']['og:title'] = res['default_twitter']['twitter:title'] = self.name res['default_opengraph']['og:description'] = res['default_twitter']['twitter:description'] = self.plain_content - res['default_opengraph']['og:image'] = res['default_twitter']['twitter:image'] = "/forum/user/%s/avatar" % (self.create_uid.id) + res['default_opengraph']['og:image'] = res['default_twitter']['twitter:image'] = "/web/image/res.users/%s/image" % (self.create_uid.id) res['default_twitter']['twitter:card'] = 'summary' return res diff --git a/addons/website_forum/models/res_users.py b/addons/website_forum/models/res_users.py index 7482d4a4a9d..b374e5f72ca 100644 --- a/addons/website_forum/models/res_users.py +++ b/addons/website_forum/models/res_users.py @@ -86,3 +86,11 @@ class Users(models.Model): @api.multi def open_website_url(self): return self.mapped('partner_id').open_website_url() + + def get_gamification_redirection_data(self): + res = super(Users, self).get_gamification_redirection_data() + res.append({ + 'url': '/forum', + 'label': 'See our Forum' + }) + return res diff --git a/addons/website_forum/static/src/scss/website_forum.scss b/addons/website_forum/static/src/scss/website_forum.scss index 85aaa69317c..8d645e26030 100644 --- a/addons/website_forum/static/src/scss/website_forum.scss +++ b/addons/website_forum/static/src/scss/website_forum.scss @@ -169,9 +169,6 @@ $bronze: #eea91e; border-left: 3px solid; } -.o_wforum_people_photo_container { - width: 33%; -} .o_profile_main { overflow: hidden; } diff --git a/addons/website_forum/views/website_forum.xml b/addons/website_forum/views/website_forum.xml index 43d2a11442f..0b67c78076d 100644 --- a/addons/website_forum/views/website_forum.xml +++ b/addons/website_forum/views/website_forum.xml @@ -46,8 +46,111 @@
-
-