1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
|
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import base64
import werkzeug
import werkzeug.exceptions
import werkzeug.urls
import werkzeug.wrappers
import math
from dateutil.relativedelta import relativedelta
from operator import itemgetter
from odoo import _, fields, http, tools
from odoo.http import request
from odoo.osv import expression
class WebsiteProfile(http.Controller):
_users_per_page = 30
_pager_max_pages = 5
# Profile
# ---------------------------------------------------
def _check_avatar_access(self, user_id, **post):
""" Base condition to see user avatar independently form access rights
is to see published users having karma, meaning they participated to
frontend applications like forum or elearning. """
try:
user = request.env['res.users'].sudo().browse(user_id).exists()
except:
return False
if user:
return user.website_published and user.karma > 0
return False
def _check_user_profile_access(self, user_id):
""" Takes a user_id and returns:
- (user record, False) when the user is granted access
- (False, str) when the user is denied access
Raises a Not Found Exception when the profile does not exist
"""
user_sudo = request.env['res.users'].sudo().browse(user_id)
# User can access - no matter what - his own profile
if user_sudo.id == request.env.user.id:
return user_sudo, False
# Profile being published is more specific than general karma requirement (check it first!)
if not user_sudo.website_published:
return False, _('This profile is private!')
elif not user_sudo.exists():
raise request.not_found()
elif request.env.user.karma < request.website.karma_profile_min:
return False, _("Not have enough karma to view other users' profile.")
return user_sudo, False
def _prepare_user_values(self, **kwargs):
kwargs.pop('edit_translations', None) # avoid nuking edit_translations
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),
}
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/avatar/<int:user_id>',
], type='http', auth="public", website=True, sitemap=False)
def get_user_profile_avatar(self, user_id, field='avatar_256', width=0, height=0, crop=False, **post):
if field not in ('image_128', 'image_256', 'avatar_128', 'avatar_256'):
return werkzeug.exceptions.Forbidden()
if (int(width), int(height)) == (0, 0):
width, height = tools.image.image_guess_size_from_field_name(field)
can_sudo = self._check_avatar_access(int(user_id), **post)
return request.env['ir.binary']._get_image_stream_from(
request.env['res.users'].sudo(can_sudo).browse(int(user_id)),
field_name=field, width=int(width), height=int(height), crop=crop
).get_response()
@http.route('/profile/user/<int:user_id>', type='http', auth='public', website=True)
def view_user_profile(self, user_id, **post):
user_sudo, denial_reason = self._check_user_profile_access(user_id)
if denial_reason:
return request.render('website_profile.profile_access_denied', {'denial_reason': denial_reason})
values = self._prepare_user_values(**post)
params = self._prepare_user_profile_parameters(**post)
values.update(self._prepare_user_profile_values(user_sudo, **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):
user_id = int(kwargs.get('user_id', 0))
countries = request.env['res.country'].search([])
if user_id and request.env.user.id != user_id and request.env.user._is_admin():
user = request.env['res.users'].browse(user_id)
values = self._prepare_user_values(searches=kwargs, user=user, is_public_user=False)
else:
values = self._prepare_user_values(searches=kwargs)
values.update({
'email_required': kwargs.get('email_required'),
'countries': countries,
'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_1920'] = False
elif kwargs.get('ufile'):
image = kwargs.get('ufile').read()
values['image_1920'] = 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_id = int(kwargs.get('user_id', 0))
if user_id and request.env.user.id != user_id and request.env.user._is_admin():
user = request.env['res.users'].browse(user_id)
else:
user = request.env.user
values = self._profile_edition_preprocess_values(user, **kwargs)
whitelisted_values = {key: values[key] for key in user.SELF_WRITEABLE_FIELDS if key in values}
user.write(whitelisted_values)
if kwargs.get('url_param'):
return request.redirect("/profile/user/%d?%s" % (user.id, kwargs['url_param']))
else:
return request.redirect("/profile/user/%d" % user.id)
# 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 'badge_category' in kwargs:
domain = expression.AND([[('challenge_ids.challenge_category', '=', kwargs.get('badge_category'))], domain])
return domain
def _prepare_ranks_badges_values(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 = badges.sorted("granted_users_count", reverse=True)
values = self._prepare_user_values(searches={'badges': True})
values.update({
'ranks': ranks,
'badges': badges,
'user': request.env.user,
})
return values
@http.route('/profile/ranks_badges', type='http', auth="public", website=True, sitemap=True)
def view_ranks_badges(self, **kwargs):
values = self._prepare_ranks_badges_values(**kwargs)
return request.render("website_profile.rank_badge_main", values)
# All Users Page
# ---------------------------------------------------
def _prepare_all_users_values(self, users):
user_values = []
for user in users:
user_values.append({
'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),
'website_published': user.website_published
})
return user_values
@http.route(['/profile/users',
'/profile/users/page/<int:page>'], type='http', auth="public", website=True, sitemap=True)
def view_all_users_page(self, page=1, **kwargs):
User = request.env['res.users']
dom = [('karma', '>', 1), ('website_published', '=', True)]
# Searches
search_term = kwargs.get('search')
group_by = kwargs.get('group_by', False)
render_values = {
'search': search_term,
'group_by': group_by or 'all',
}
if search_term:
dom = expression.AND([['|', ('name', 'ilike', search_term), ('partner_id.commercial_company_name', 'ilike', search_term)], dom])
user_count = User.sudo().search_count(dom)
my_user = request.env.user
current_user_values = False
if user_count:
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,
url_args=kwargs)
users = User.sudo().search(dom, limit=self._users_per_page, offset=pager['offset'], order='karma DESC')
user_values = self._prepare_all_users_values(users)
# Get karma position for users (only website_published)
position_domain = [('karma', '>', 1), ('website_published', '=', True)]
position_map = self._get_position_map(position_domain, users, group_by)
max_position = max([user_data['karma_position'] for user_data in position_map.values()], default=1)
for user in user_values:
user_data = position_map.get(user['id'], dict())
user['position'] = user_data.get('karma_position', max_position + 1)
user['karma_gain'] = user_data.get('karma_gain_total', 0)
user_values.sort(key=itemgetter('position'))
if my_user.website_published and my_user.karma and my_user.id not in users.ids:
# Need to keep the dom to search only for users that appear in the ranking page
current_user = User.sudo().search(expression.AND([[('id', '=', my_user.id)], dom]))
if current_user:
current_user_values = self._prepare_all_users_values(current_user)[0]
user_data = self._get_position_map(position_domain, current_user, group_by).get(current_user.id, {})
current_user_values['position'] = user_data.get('karma_position', 0)
current_user_values['karma_gain'] = user_data.get('karma_gain_total', 0)
else:
user_values = []
pager = {'page_count': 0}
render_values.update({
'top3_users': user_values[:3] if not search_term and page == 1 else [],
'users': user_values,
'my_user': current_user_values,
'pager': pager,
})
return request.render("website_profile.users_page_main", render_values)
def _get_position_map(self, position_domain, users, group_by):
if group_by:
position_map = self._get_user_tracking_karma_gain_position(position_domain, users.ids, group_by)
else:
position_results = users._get_karma_position(position_domain)
position_map = dict((user_data['user_id'], dict(user_data)) for user_data in position_results)
return position_map
def _get_user_tracking_karma_gain_position(self, domain, user_ids, group_by):
""" Helper method computing boundaries to give to _get_tracking_karma_gain_position.
See that method for more details. """
to_date = fields.Date.today()
if group_by == 'week':
from_date = to_date - relativedelta(weeks=1)
elif group_by == 'month':
from_date = to_date - relativedelta(months=1)
else:
from_date = None
results = request.env['res.users'].browse(user_ids)._get_tracking_karma_gain_position(domain, from_date=from_date, to_date=to_date)
return dict((item['user_id'], dict(item)) for item in results)
# 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
|