From d7cc1520f43e2a8c4825c0bd5b6e7448fc20c9bf Mon Sep 17 00:00:00 2001
From: David Beguin <dbe@odoo.com>
Date: Tue, 26 Feb 2019 09:49:22 +0000
Subject: [PATCH] [IMP] website_profile, gamification, : add search bar in all
 users page

This commit adds the search bar in 'All users' page in order to filter on name or company name.
To be able to keep the position, a non stored computed field has been added on res_users
to get the position depending on the user's karma.

The podium (top 3 users) is now displayed only if there is no search applied and if the page = 1
because it has no sens anymore in other cases.

Special thanks to @jem-odoo who helped me finding smart solution for position computing.

Task ID : 1943788
PR #31321
---
 addons/gamification/models/res_users.py       | 24 +++++++++++
 addons/website_profile/controllers/main.py    | 37 ++++++++--------
 .../static/src/scss/website_profile.scss      | 43 +++++++++++++++++++
 .../website_profile/views/website_profile.xml | 34 +++++++++++++++
 addons/website_slides/controllers/main.py     | 18 ++++----
 .../controllers/slides.py                     |  4 +-
 6 files changed, 130 insertions(+), 30 deletions(-)

diff --git a/addons/gamification/models/res_users.py b/addons/gamification/models/res_users.py
index f556dcc514ad..6a188f2003ae 100644
--- a/addons/gamification/models/res_users.py
+++ b/addons/gamification/models/res_users.py
@@ -8,6 +8,7 @@ class Users(models.Model):
     _inherit = 'res.users'
 
     karma = fields.Integer('Karma', default=0)
+    karma_position = fields.Integer('Karma position', compute="_compute_karma_position", store=False)
     badge_ids = fields.One2many('gamification.badge.user', 'user_id', string='Badges', copy=False)
     gold_badge = fields.Integer('Gold badges count', compute="_get_user_badge_level")
     silver_badge = fields.Integer('Silver badges count', compute="_get_user_badge_level")
@@ -15,6 +16,29 @@ class Users(models.Model):
     rank_id = fields.Many2one('gamification.karma.rank', 'Rank', index=False)
     next_rank_id = fields.Many2one('gamification.karma.rank', 'Next Rank', index=False)
 
+    @api.depends('karma')
+    def _compute_karma_position(self):
+        where_query = self._where_calc([])
+        self._apply_ir_rules(where_query, 'read')
+        from_clause, where_clause, where_clause_params = where_query.get_sql()
+
+        query = """
+            SELECT sub.id, sub.karma_position
+            FROM (
+                SELECT id, row_number() OVER (ORDER BY res_users.karma DESC) AS karma_position
+                FROM {from_clause}
+                WHERE {where_clause}
+            ) sub
+            WHERE sub.id IN %s
+            """.format(from_clause=from_clause, where_clause=where_clause)
+
+        self.env.cr.execute(query, where_clause_params + [tuple(self.ids)])
+
+        position_map = {item['id']: item['karma_position'] for item in self.env.cr.dictfetchall()}
+
+        for user in self:
+            user.karma_position = position_map.get(user.id, 0)
+
     @api.multi
     @api.depends('badge_ids')
     def _get_user_badge_level(self):
diff --git a/addons/website_profile/controllers/main.py b/addons/website_profile/controllers/main.py
index b29ba2077fbe..de56318e7c6e 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
     # ---------------------------------------------------
 
@@ -198,11 +200,12 @@ class WebsiteProfile(http.Controller):
 
     # 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),
@@ -214,26 +217,22 @@ 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)
-
-        if searches.get('user'):
-            dom += [('name', 'ilike', searches.get('user'))]
+        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)
 
-        users = User.sudo().search(dom, limit=step, offset=pager['offset'], order='karma DESC')
+        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]
 
-        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)
diff --git a/addons/website_profile/static/src/scss/website_profile.scss b/addons/website_profile/static/src/scss/website_profile.scss
index 224b46b1c849..a20b1ea2eef2 100644
--- a/addons/website_profile/static/src/scss/website_profile.scss
+++ b/addons/website_profile/static/src/scss/website_profile.scss
@@ -118,11 +118,54 @@ $owprofile-tabs-height: 37px;
 }
 
 // All Users Page
+.o_wprofile_all_users_nav {
+    margin: auto;
+    background: linear-gradient(90deg, #875A7B 20%, #62495B 80%) !important;
+    a {
+        color: #FFFFFF;
+    }
+    nav {
+        .breadcrumb-item::before {
+            font-weight: bold;
+            color: #FFFFFF;
+            opacity: 0.8;
+        }
+        .breadcrumb-item {
+            a {
+                font-weight: bold;
+                color: #FFFFFF;
+                opacity: 0.8;
+                transition: opacity linear .3s;
+            }
+            &.active {
+                a {
+                    opacity: 1.0;
+                }
+            }
+        }
+        ol {
+            background-color: transparent;
+        }
+    }
+}
+
 .o_wprofile_all_users_header {
     @include media-breakpoint-up(sm)  {
         min-height: 200px;
         margin-bottom: -60px;
     }
+    margin: auto;
+    background: linear-gradient(90deg, #875A7B 20%, #62495B 80%) !important;
+    > div:first-child {
+        margin: auto;
+        width: 100%;
+        max-width: 1140px;
+        padding: 32px 32px 0px 32px;
+        color: white;
+        top: 48px;
+        padding-top: 0px;
+        height: 250px;
+    }
 }
 
 .o_wprofile_top3_card_footer div {
diff --git a/addons/website_profile/views/website_profile.xml b/addons/website_profile/views/website_profile.xml
index fc9e6aae1137..b115d553e8af 100644
--- a/addons/website_profile/views/website_profile.xml
+++ b/addons/website_profile/views/website_profile.xml
@@ -392,6 +392,40 @@
     </template>
 
     <template id="users_page_header" name="Users Page Header">
+        <div class="o_wprofile_all_users_nav">
+            <div class="container">
+                <div class="row align-items-center justify-content-between">
+                    <!-- nav -->
+                    <nav aria-label="breadcrumb" class="col-md-8 col-xs-12">
+                        <ol class="breadcrumb">
+                            <li class="breadcrumb-item">
+                                <a t-att-href="home_url or '/'">Home</a>
+                            </li>
+                            <li t-att-class="'breadcrumb-item active'">
+                                <a href="#">Users</a>
+                            </li>
+                        </ol>
+                    </nav>
+                    <div class="col-md-4 col-xs-12 d-flex flex-row align-items-center">
+                        <!-- karma / profile -->
+                        <a t-if="not is_public_user" t-att-href="'/profile/user/%s' % user_id.id" class="font-weight-bold mr-3">
+                            <i class="fa fa-diamond" /> <t t-esc="user_id.karma"/>
+                        </a>
+                        <!-- search -->
+                        <form t-attf-action="/profile/users" role="search" method="get">
+                            <div class="input-group">
+                                <input type="text" class="form-control" name="search" placeholder="Search in Users" t-att-value="search"/>
+                                <span class="input-group-append">
+                                    <button class="btn btn-primary" type="submit" aria-label="Search" title="Search">
+                                        <i class="fa fa-search"></i>
+                                    </button>
+                                </span>
+                            </div>
+                        </form>
+                    </div>
+                </div>
+            </div>
+        </div>
         <div class="o_wprofile_all_users_header o_wprofile_gradient text-white">
             <div class="container">
                 <h1 class="py-5">All Users</h1>
diff --git a/addons/website_slides/controllers/main.py b/addons/website_slides/controllers/main.py
index 26533550f2d3..c876881497bb 100644
--- a/addons/website_slides/controllers/main.py
+++ b/addons/website_slides/controllers/main.py
@@ -20,9 +20,9 @@ _logger = logging.getLogger(__name__)
 
 
 class WebsiteSlides(WebsiteProfile):
-    SLIDES_PER_PAGE = 12
-    SLIDES_PER_ASIDE = 20
-    SLIDES_PER_CATEGORY = 4
+    _slides_per_page = 12
+    _slides_per_aside = 20
+    _slides_per_category = 4
     _channel_order_by_criterion = {
         'vote': 'total_votes desc',
         'view': 'total_views desc',
@@ -73,8 +73,8 @@ class WebsiteSlides(WebsiteProfile):
         if slide.channel_id.channel_type == 'documentation':
             related_domain = expression.AND([base_domain, [('category_id', '=', slide.category_id.id)]])
 
-            most_viewed_slides = request.env['slide.slide'].search(base_domain, limit=self.SLIDES_PER_ASIDE, order='total_views desc')
-            related_slides = request.env['slide.slide'].search(related_domain, limit=self.SLIDES_PER_ASIDE)
+            most_viewed_slides = request.env['slide.slide'].search(base_domain, limit=self._slides_per_aside, order='total_views desc')
+            related_slides = request.env['slide.slide'].search(related_domain, limit=self._slides_per_aside)
             category_data = []
             uncategorized_slides = request.env['slide.slide']
         else:
@@ -348,10 +348,10 @@ class WebsiteSlides(WebsiteProfile):
         pager_args['sorting'] = actual_sorting
 
         slide_count = request.env['slide.slide'].sudo().search_count(domain)
-        page_count = math.ceil(slide_count / self.SLIDES_PER_PAGE)
+        page_count = math.ceil(slide_count / self._slides_per_page)
         pager = request.website.pager(url=pager_url, total=slide_count, page=page,
-                                      step=self.SLIDES_PER_PAGE, url_args=pager_args,
-                                      scope=page_count if page_count < self.PAGER_MAX_PAGE else self.PAGER_MAX_PAGE)
+                                      step=self._slides_per_page, url_args=pager_args,
+                                      scope=page_count if page_count < self._pager_max_pages else self._pager_max_pages)
 
         values = {
             'channel': channel,
@@ -397,7 +397,7 @@ class WebsiteSlides(WebsiteProfile):
         values['category_data'] = channel._get_categorized_slides(
             domain, order,
             force_void=True,
-            limit=self.SLIDES_PER_CATEGORY if channel.channel_type == 'documentation' else False,
+            limit=self._slides_per_category if channel.channel_type == 'documentation' else False,
             offset=pager['offset'])
         values['channel_progress'] = self._get_channel_progress(channel, include_quiz=True)
 
diff --git a/addons/website_slides_survey/controllers/slides.py b/addons/website_slides_survey/controllers/slides.py
index b65b3c3333b1..9396de502b8d 100644
--- a/addons/website_slides_survey/controllers/slides.py
+++ b/addons/website_slides_survey/controllers/slides.py
@@ -60,8 +60,8 @@ class WebsiteSlides(WebsiteSlides):
 
     # All Users Page
     # ---------------------------------------------------
-    def _prepare_all_users_values(self, user, position):
-        result = super(WebsiteSlides, self)._prepare_all_users_values(user, position)
+    def _prepare_all_users_values(self, user):
+        result = super(WebsiteSlides, self)._prepare_all_users_values(user)
         result.update({
             'certification_count': len(self._get_user_certificates(user))
         })
-- 
GitLab