diff options
| author | David Beguin <dbe@odoo.com> | 2019-01-24 11:02:12 +0000 |
|---|---|---|
| committer | David Beguin <dbe@odoo.com> | 2019-02-14 08:30:29 +0000 |
| commit | ff870099c588e56e8f041f1d928edd779b1b719c (patch) | |
| tree | 1db6d5f4c57770ba953bb4f1c871ba930f4afa46 /addons | |
| parent | cfbd8d1adaa04901c2792f7a3ec02bd66aab3dbd (diff) | |
[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
Diffstat (limited to 'addons')
| -rw-r--r-- | addons/website_forum/__manifest__.py | 1 | ||||
| -rw-r--r-- | addons/website_forum/static/src/scss/website_forum.scss | 22 | ||||
| -rw-r--r-- | addons/website_profile/__init__.py | 1 | ||||
| -rw-r--r-- | addons/website_profile/__manifest__.py | 3 | ||||
| -rw-r--r-- | addons/website_profile/controllers/__init__.py | 4 | ||||
| -rw-r--r-- | addons/website_profile/controllers/main.py | 136 | ||||
| -rw-r--r-- | addons/website_profile/models/__init__.py | 1 | ||||
| -rw-r--r-- | addons/website_profile/models/website.py | 10 | ||||
| -rw-r--r-- | addons/website_profile/static/src/scss/website_profile.scss | 189 | ||||
| -rw-r--r-- | addons/website_profile/views/website_profile.xml | 379 |
10 files changed, 722 insertions, 24 deletions
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/<int:user_id>'], 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo><data> + <template id="assets_frontend" inherit_id="website.assets_frontend"> + <xpath expr="link[last()]" position="after"> + <link rel="stylesheet" type="text/scss" href="/website_profile/static/src/scss/website_profile.scss"/> + </xpath> + </template> + + <!-- Edit Profile Page --> + <template id="user_profile_edit_main" name="Edit Profile"> + <t t-call="website.layout"> + <div class="container mt16 mb32"> + <t t-call="website_profile.user_profile_edit_content"> + </t> + </div> + </t> + </template> + + <template id="user_profile_edit_content" name="Edit Profile"> + <h1 class="o_page_header">Edit Profile</h1> + <div> + <form t-attf-action="/profile/user/save" method="post" role="form" class="js_website_submit_form row" enctype="multipart/form-data"> + <input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/> + <input type="file" class="d-none o_forum_file_upload" name="ufile" accept="image/*"/> + <input type="hidden" name="url_param" t-att-value="request.params.get('url_param')"/> + <div class="col-3"> + <div class="card o_card_people"> + <div class="card-body"> + <img class="w-100 mb-3" t-attf-src="/web/image/res.users/#{user.id}/image_medium"/> + <div class="text-center"> + <a href="#" class="o_forum_profile_pic_edit btn btn-primary" aria-label="Edit"> + <i class="fa fa-pencil fa-1g float-sm-none float-md-left" title="Edit"></i> + </a> + <a href="#" title="Clear" aria-label="Clear" class="btn border-primary"> + <i class="fa fa-trash-o float-sm-none float-md-right o_forum_profile_pic_clear"></i> + </a> + </div> + <div class="form-group mt-3 mb-0 pt-2 border-top"> + <label class="text-primary" for="user_website_published" t-if="user.id == uid"><b>Public profile</b></label> + <div class=" mb-0 float-right" t-if="user.id == uid"> + <input type="checkbox" class="mt8" name="website_published" id="user_website_published" value="True" t-if="not user.website_published"/> + <input type="checkbox" class="mt8" name="website_published" id="user_website_published" value="True" checked="checked" t-if="user.website_published"/> + </div> + </div> + </div> + </div> + </div> + <div class="col-9 mb-3"> + <div class="card"> + <div class="card-body"> + <div class="row"> + <input name="user_id" t-att-value="user.id" type="hidden"/> + <div class="form-group col-12"> + <label class="text-primary mb-1 d-block" for="user_name"><b>Real name</b></label> + <div> + <input type="text" class="form-control" name="name" id="user_name" required="True" t-attf-value="#{user.name}"/> + </div> + </div> + + <div class="form-group col-6"> + <label class="mb-1 text-primary" for="user_website"><b>Website</b></label> + <div> + <input type="text" class="form-control" name="website" id="user_website" t-attf-value="#{user.partner_id.website or ''}"/> + </div> + </div> + <div class="form-group col-6"> + <div t-if="email_required" class="alert alert-danger alert-dismissable oe_email_required" role="alert"> + <button type="button" class="close" data-dismiss="alert">x</button> + <p>Please enter a valid email address in order to receive notifications from answers or comments.</p> + </div> + <label class="mb-1 text-primary" for="user_email"><b>Email</b></label> + <div> + <input type="text" class="form-control" name="email" id="user_email" required="True" t-attf-value="#{user.partner_id.email}"/> + </div> + </div> + <div class="form-group col-6"> + <label class="mb-1 text-primary" for="user_city"><b>City</b></label> + <div> + <input type="text" class="form-control" name="city" id="user_city" t-attf-value="#{user.partner_id.city or ''}"/> + </div> + </div> + <div class="form-group col-6"> + <label class="mb-1 text-primary"><b>Country</b></label> + <div> + <select class="form-control" name="country"> + <option value="">Country...</option> + <t t-foreach="countries or []" t-as="country"> + <option t-att-value="country.id" t-att-selected="country.id == user.partner_id.country_id.id"><t t-esc="country.name"/></option> + </t> + </select> + </div> + </div> + <div class="form-group col-12"> + <label class="mb-1 text-primary" for="description"><b>Biography</b></label> + <textarea name="description" id="description" style="min-height: 120px" required="True" + class="form-control load_editor"><t t-esc="user.partner_id.website_description"/></textarea> + </div> + <div class="col"> + <button type="submit" class="btn btn-primary btn">Update</button> + </div> + </div> + </div> + </div> + </div> + </form> + </div> + </template> + + <!-- Profile Page --> + <template id="user_profile_main" name="Profile Page"> + <t t-call="website.layout"> + <t t-call="website_profile.user_profile_header"></t> + <t t-call="website_profile.user_profile_content"></t> + </t> + </template> + + <template id="user_profile_header" name="Profile Page Header"> + <div class="row o_wprofile_header"> + <div class="col-lg-12 o_wprofile_container o_wprofile_header_container"> + <div class="row"> + <div class="col o_wprofile_pict"> + <img class="o_wprofile_photo mr-3" t-attf-src="/web/image/res.users/#{user.id}/image"/> + </div> + <div class="col-8 o_wprofile_id"> + <a id="edit_profile_button" class="btn btn-primary o_wprofile_edit" + t-if="request.env.user == user" + t-attf-href="/profile/edit?url_param=#{edit_button_url_param}"><b>EDIT PROFILE</b></a> + <div class="row mb32"> + <h3 class="o_card_people_name text-white"><b t-field="user.name"/></h3> + </div> + <div class="row"> + <div class="o_wprofile_id_info"> + <t t-if="user.company_id"> + <span class="text-white"> + <i class="fa fa-building-o text-white mr-2"/><span t-field="user.company_id"/> + </span> + </t> + </div> + <div class="o_wprofile_id_info"> + <t t-if="user.city or user.country_id"> + <i class="fa fa-map-marker text-white mr-2"/> + <span class="text-white"> + <span t-field="user.city"/><t t-if="user.country_id"> (</t><span t-field="user.country_id"/><t t-if="user.country_id">)</t> + </span> + </t> + </div> + <div class="o_wprofile_id_info"> + <t t-if="user.website"> + <span class="text-white"> + <i class="fa fa-globe text-white mr-2"/><span t-field="user.website"/> + </span> + </t> + </div> + </div> + </div> + </div> + </div> + </div> + </template> + + <template id="user_profile_content" name="Profile Page Content"> + <div class="row o_wprofile_content"> + <div class="col-lg-12 o_wprofile_container o_wprofile_content_container"> + <div class="row"> + <div class="col o_wprofile_navtabs_blank"></div> + <div class="col-8 o_wprofile_navtabs_container"> + <ul class="nav nav-tabs o_wprofile_nav_tabs" role="tablist" id="profile_extra_info_tablist"> + <li class="nav-item" id="profile_nav_tab_about"> + <a role="tab" aria-controls="about" href="#profile_tab_content_about" class="nav-link o_wprofile_navlink active" data-toggle="tab">About</a> + </li> + </ul> + </div> + </div> + <div class="row o_wprofile_detail"> + <div class="col o_wprofile_content_abstract"> + <t t-if="user.rank_id"> + <div class="row"> + <div class="col-lg-12 pt16 o_wprofile_rank"> + <table><tbody><tr> + <th><b class="text-muted">Current <a href="/profile/ranks">Rank</a> : </b></th> + <th><span class="float-right" t-field="user.rank_id"/></th> + </tr></tbody></table> + <t t-call="website_profile.profile_next_rank_card"></t> + </div> + </div> + </t> + <div class="row"> + <div class="col-lg-12 pt16 pb16 o_wprofile_abstract_info o_wprofile_bb_grey"> + <table> + <tbody> + <tr> + <th style="width: 30%"><b class="text-muted">Joined</b></th> + <th style="width: 70%"><span t-field="user.create_date" t-options='{"format": "d MMM Y"}'/></th> + </tr> + <tr> + <th style="width: 30%"><b class="text-muted">Badges</b></th> + <th style="width: 70%"> + <t t-if="user.badge_ids"> + <span t-esc="len(user.badge_ids)"/> + </t> + <t t-else=""> + <span>0</span> + </t> + </th> + </tr> + <t t-if="user.website"> + <tr> + <th style="width: 30%"><b class="text-muted">Website</b></th> + <th style="width: 70%"><span t-field="user.website"/></th> + </tr> + </t> + <t t-if="user.city or user.country_id"> + <tr> + <th style="width: 30%"><b class="text-muted">Location</b></th> + <th style="width: 70%"> + <span t-field="user.city"/><t t-if="user.country_id"> (</t><span t-field="user.country_id"/><t t-if="user.country_id">)</t> + </th> + </tr> + </t> + <tr id="profile_abstract_info_company"> + <th style="width: 30%"><b class="text-muted">Company</b></th> + <th style="width: 70%"><span t-field="user.company_id"/></th> + </tr> + </tbody> + </table> + </div> + </div> + </div> + <div class="col-8 mt24 ml32 o_wprofile_content_detail"> + <div class="tab-content mt16" id="profile_extra_info_tabcontent"> + <div role="tabpanel" class="tab-pane active" id="profile_tab_content_about"> + <div id="profile_about_badge" class="mb32"> + <h4><i class="fa fa-certificate"/> Badges</h4> + <t t-call="website_profile.user_badges"></t> + </div> + <div t-if="user.partner_id.website_description" class="mb32"> + <h4><i class="fa fa-pencil"/> Biography</h4> + <span t-field="user.partner_id.website_description"/> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + </template> + + <template id="profile_next_rank_card" name="Profile Next Rank Card"> + <div class="card-body o_wprofile_next_rank_card mt-2 pt-2"> + <div><img class="o_wprofile_img_medium" t-attf-src="/web/image/gamification.karma.rank/#{user.next_rank_id.id}/image_medium"/></div> + <div><span class="text-center"><b>"<span t-field="user.next_rank_id.name"/>"</b></span></div> + <div><span class="text-center"><b class="text-primary" t-field="user.karma"/>/<b t-field="user.next_rank_id.karma_min"/> xp</span></div> + <div class="progress o_wprofile_next_rank_progress"> + <t t-if="user.next_rank_id.karma_min - user.rank_id.karma_min > 0"> + <div class="progress-bar o_wprofile_next_rank_progress_bar" style="background-color: #00A09D !important" + t-att-style="'width: '+ str(100*(user.karma - user.rank_id.karma_min)/(user.next_rank_id.karma_min - user.rank_id.karma_min))+'%'"></div> + </t> + <t t-else=""> + <div class="progress-bar o_wprofile_next_rank_progress_bar" style="background-color: #00A09D !important" + t-att-style="'width: 0%'"></div> + </t> + </div> + </div> + </template> + + <template id="user_badges" name="User Bagdes"> + <t t-if="user.badge_ids"> + <div class="row"> + <div class="col-lg-3 o_user_badges" t-foreach="user.badge_ids" t-as="badge"> + <div class="card-body o_wprofile_bb_grey"> + <table><tbody><tr> + <th> + <img class="o_wprofile_img_thumb" t-attf-src="/web/image/gamification.badge/#{badge.badge_id.id}/image_small"/> + </th> + <th style=""> + <b t-field="badge.badge_id.name"/> + </th> + </tr></tbody></table> + </div> + </div> + </div> + </t> + <div class="mb16" t-if="not user.badge_ids"> + <b>No badge yet!</b><br/> + <t t-if="badge_category"> + <a t-attf-href="/profile/badge?category=#{badge_category}" class="fa fa-arrow-right"> Check available badges</a> + </t> + <t t-else=""> + <a t-attf-href="/profile/badge" class="fa fa-arrow-right"> Check available badges</a> + </t> + </div> + </template> + + <!-- About Rank Page --> + <template id="rank_main" name="Ranks Page"> + <t t-call="website.layout"> + <div class="container mb32 mt48"> + <h1 class="o_page_header"> + Ranks & Badges + </h1> + <p> + Do not stop learning with Odoo. Collects points on the forum or the eLearning platform. Those points will make you reach new ranks. + </p> + <p> + <b>How do I earn badges?</b> When you finish a course, reach milestones, you're awarded badges. + </p> + <p> + <b>How do I score more points?</b> 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> + <t t-call="website_profile.rank_content"> + </t> + </t> + </template> + + <template id="rank_content" name="Rank Page content"> + <div class="o_wprofile_ranks_presentation mt16 mb32 pt16 pb48"> + <div class="container mb32 mt48"> + <h3>Ranks</h3> + <div class="row"> + <div class="o_wprofile_rank_tile" t-foreach="ranks" t-as="rank"> + <div><img class="mb-3" t-attf-src="/web/image/gamification.karma.rank/#{rank.id}/image"/></div> + <div><b t-field="rank.name"/></div> + <div><span style="color: #00A09D;"><span t-field="rank.karma_min"/> point<span t-if="rank.karma_min">s</span></span></div> + </div> + </div> + </div> + </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> + </div> + </t> + </template> + + <template id="badge_content" name="Badges Page content"> + <h1 class="o_page_header"> + Badges + </h1> + <p> + Besides gaining reputation with your questions and answers, + you receive badges for being especially helpful. Badges + appear on your profile page, and your posts. + </p> + <table class="table mt32 mb64"> + <tr t-foreach="badges" t-as="badge"> + <td> + <span class="badge badge-pill float-left"> + <span t-if="badge.level == 'gold'"><i class="fa fa-trophy badge-gold"/> <b t-field="badge.name" class="text-white bg-gold badge-pill"/></span> + <span t-if="badge.level == 'silver'"><i class="fa fa-trophy badge-silver"/> <b t-field="badge.name" class="text-white bg-silver badge-pill"/></span> + <span t-if="badge.level == 'bronze'"><i class="fa fa-trophy badge-bronze"/> <b t-field="badge.name" class="text-white bg-bronze badge-pill"/></span> + </span> + </td><td> + <b t-esc="badge.stat_count_distinct"/> + <i class="text-muted"> awarded users</i> + </td><td> + <span t-field="badge.description"/> + </td> + </tr> + </table> + </template> + + <!--Private profile--> + <template id="private_profile" name="Private Profile Page"> + <t t-call="website.layout"> + <div class="container mb32 mt48"> + <h1 class="mt32">This profile is private!</h1> + <div id="private_profile_return_link_container"> + <p><a t-attf-href="/">Return to the website.</a></p> + </div> + </div> + </t> + </template> +</data></odoo> |
