Skip to content
Permalink
Browse files

[MERGE][IMP] website_slides: improve inclusion of gamification and ranks

Purpose of this merge is to improve inclusion of gamification and ranking
in elearning new main pages [1][2]. Ranking and profile information, links
and templates are added.

Some fixes are included in this merge, notably statistics computation, better
slide image fetch and demo data. See sub commits for more details.

Commit linked to task ID 1941250.

closes #31133
  • Loading branch information...
robodoo committed Feb 15, 2019
2 parents b2149bc + 5822e30 commit f34a7edddefbadb282178192247fcab77b891d32
@@ -13,9 +13,8 @@
<record id="rank_newbie" model="gamification.karma.rank">
<field name="name">Newbie</field>
<field name="description" type="html"><p>You just began the adventure! Welcome!</p></field>
<field name="description_reach_next" type="html"><p>Keep going !</p></field>
<field name="karma_min">1</field>
<field name="image" type="base64" file="gamification/static/img/rank_newbie_badge.svg"/>
</record>
</data>
</odoo>
</odoo>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data>
<odoo><data noupdate="1">
<!-- Set demo karma for test tour as demo user-->
<record id="base.user_demo" model="res.users">
<field name="karma">20</field>
@@ -12,31 +12,47 @@
<record id="rank_student" model="gamification.karma.rank">
<field name="name">Student</field>
<field name="description" type="html"><p>You begin to understand things ! Keep going!</p></field>
<field name="description_reach_next" type="html"><p>Keep going !</p></field>
<field name="description_motivational" type="html">
<div class="media align-items-center">
<div class="media-body">Reach the next rank and gain a very nice mug !</div>
<img class="ml-3 img img-fluid" style="max-height: 72px;" src="/gamification/static/img/rank_misc_mug.jpg"/>
</div></field>
<field name="karma_min">100</field>
<field name="image" type="base64" file="gamification/static/img/rank_student_badge.svg"/>
</record>

<record id="rank_bachelor" model="gamification.karma.rank">
<field name="name">Bachelor</field>
<field name="description" type="html"><p>Hey ! You're not so dumb after all !</p></field>
<field name="description_reach_next" type="html"><p>Keep going !</p></field>
<field name="description_motivational" type="html">
<div class="media align-items-center">
<div class="media-body">Reach the next rank and gain a very magic wand !</div>
<img class="ml-3 img img-fluid" style="max-height: 72px;" src="/gamification/static/img/rank_misc_wand.jpg"/>
</div></field>
<field name="karma_min">500</field>
<field name="image" type="base64" file="gamification/static/img/rank_bachelor_badge.svg"/>
</record>

<record id="rank_master" model="gamification.karma.rank">
<field name="name">Master</field>
<field name="description" type="html"><p>You're good ! Really Good !</p></field>
<field name="description_reach_next" type="html"><p>Keep going !</p></field>
<field name="description_motivational" type="html">
<div class="media align-items-center">
<div class="media-body">Reach the next rank and gain a very nice hat !</div>
<img class="ml-3 img img-fluid" style="max-height: 72px;" src="/gamification/static/img/rank_misc_hat.jpg"/>
</div></field>
<field name="karma_min">2000</field>
<field name="image" type="base64" file="gamification/static/img/rank_master_badge.svg"/>
</record>

<record id="rank_doctor" model="gamification.karma.rank">
<field name="name">Doctor</field>
<field name="description" type="html"><p>Ok, RESPECT !</p></field>
<field name="description_reach_next" type="html"><p>Keep going !</p></field>
<field name="description_motivational" type="html">
<div class="media align-items-center">
<div class="media-body">Reach the next rank and gain a week-end with a unicorn !</div>
<img class="ml-3 img img-fluid" style="max-height: 72px;" src="/gamification/static/img/rank_misc_unicorn.jpg"/>
</div></field>
<field name="karma_min">10000</field>
<field name="image" type="base64" file="gamification/static/img/rank_doctor_badge.svg"/>
</record>
@@ -19,9 +19,9 @@ class KarmaRank(models.Model):

name = fields.Text(string='Rank Name', translate=True, required=True)
description = fields.Html(string='Description', translate=html_translate, sanitize_attributes=False,)
description_reach_next = fields.Html(
string='Motivation phrase', translate=html_translate, sanitize_attributes=False,
help="Motivation phrase to reach next rank")
description_motivational = fields.Html(
string='Motivational', translate=html_translate, sanitize_attributes=False,
help="Motivational phrase to reach this rank")
karma_min = fields.Integer(string='Required Karma', help='Minimum karma needed to reach this rank')
user_ids = fields.One2many('res.users', 'rank_id', string='Users', help="Users having this rank")
image = fields.Binary('Rank Icon')
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,3 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<!-- Ranks views -->
@@ -35,8 +36,6 @@
<tree string="Ranks List">
<field name="name"/>
<field name="karma_min"/>
<field name="description"/>
<field name="user_ids"/>
</tree>
</field>
</record>
@@ -58,6 +57,7 @@
<group>
<field name="karma_min"/>
<field name="description" placeholder="e.g. A Master Chief knows quite everything on the forum! You cannot beat him!"/>
<field name="description_motivational" placeholder="e.g. Reach this rank to gain a free mug !"/>
<field name="create_date" invisible="1"/>
</group>
<group string="Users to that rank" attrs="{'invisible': [('create_date','=',False)]}">
@@ -10,7 +10,6 @@
from odoo import http, modules, tools
from odoo.http import request
from odoo.osv import expression
from odoo.tools import limited_image_resize


class WebsiteProfile(http.Controller):
@@ -30,6 +29,20 @@ def _check_avatar_access(self, user_id, **post):
return user.website_published and user.karma > 0
return False

def _get_default_avatar(self, field, headers, width, height):
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)
dictheaders = dict(headers) if headers else {}
dictheaders['Content-Type'] = 'image/png'
headers = list(dictheaders.items())
if not (width or height):
suffix = field.split('_')[-1] if '_' in field else 'large'
if suffix in ('small', 'medium', 'large', 'big'):
content = getattr(tools, 'image_resize_image_%s' % suffix)(content)
return content

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 \
@@ -60,36 +73,28 @@ def _prepare_user_profile_values(self, user, **post):
@http.route([
'/profile/avatar/<int:user_id>',
], type='http', auth="public", website=True, sitemap=False)
def get_user_profile_avatar(self, user_id=0, width=0, height=0, crop=False, avoid_if_small=False, upper_limit=False, **post):
def get_user_profile_avatar(self, user_id, field='image_large', width=0, height=0, crop=False, avoid_if_small=False, upper_limit=False, **post):
if field not in ('image_small', 'image_medium', 'image_large'):
return werkzeug.exceptions.Forbidden()

can_sudo = self._check_avatar_access(user_id, **post)
fname = post.get('field', 'image_medium')
if can_sudo:
status, headers, content = request.env['ir.http'].sudo().binary_content(
model='res.users', id=user_id, field=fname,
model='res.users', id=user_id, field=field,
default_mimetype='image/png')
else:
status, headers, content = request.env['ir.http'].binary_content(
model='res.users', id=user_id, field=fname,
model='res.users', id=user_id, field=field,
default_mimetype='image/png')
if status == 301:
return request.env['ir.http']._response_by_status(status, headers, content)
if status == 304:
return werkzeug.wrappers.Response(status=304)

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)
dictheaders = dict(headers) if headers else {}
dictheaders['Content-Type'] = 'image/png'
headers = list(dictheaders.items())
if not (width or height):
suffix = fname.split('_')[-1]
if suffix in ('small', 'medium', 'big'):
content = getattr(tools, 'image_resize_image_%s' % suffix)(content)

content = limited_image_resize(
content = self._get_default_avatar(field, headers, width, height)

content = tools.limited_image_resize(
content, width=width, height=height, crop=crop, upper_limit=upper_limit, avoid_if_small=avoid_if_small)

image_base64 = base64.b64decode(content)
@@ -159,6 +164,7 @@ def ranks(self, **kwargs):
ranks = ranks.sorted(key=lambda b: b.karma_min)
values = {
'ranks': ranks,
'user': request.env.user,
}
return request.render("website_profile.rank_main", values)

@@ -127,7 +127,6 @@ $bronze: #eea91e;
height: 128px;
}
}
border-bottom: 1px solid lightgray;
.o_wprofile_next_rank_progress {
background-color: lightgray;
.o_wprofile_next_rank_progress_bar {
@@ -181,6 +181,7 @@
<th><span class="float-right" t-field="user.rank_id"/></th>
</tr></tbody></table>
<t t-call="website_profile.profile_next_rank_card"></t>
<hr/>
</div>
</div>
</t>
@@ -299,7 +300,7 @@
Ranks &amp; 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.
Do not stop learning with <t t-esc="user.company_id.name"/>. 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.
@@ -46,7 +46,7 @@
<field name="website_published" eval="True"/>
<field name="is_preview" eval="False"/>
<field name="public_views">10</field>
<field name="description" type="html"><div>Tools you will need to complete this course.</div></field>
<field name="description">Tools you will need to complete this course.</field>
</record>
<record id="slide_slide_demo_6_1" model="slide.slide">
<field name="name">How to find quality wood</field>
@@ -59,7 +59,7 @@
<field name="is_preview" eval="False"/>
<field name="public_views">5</field>
<field name="image" type="base64" file="website_sale_slides/static/img/5WMqwTnZ-qs.png"/>
<field name="description" type="html"><div>Learn of to identify quality wood in order to create solid furnitures.</div></field>
<field name="description">Learn of to identify quality wood in order to create solid furnitures.</field>
</record>
<record id="slide_slide_demo_6_2" model="slide.slide">
<field name="name">How to create your own piece of furniture</field>
@@ -72,7 +72,7 @@
<field name="is_preview" eval="False"/>
<field name="public_views">0</field>
<field name="image" type="base64" file="website_sale_slides/static/img/ptjeDDoURL8.png"/>
<field name="description" type="html"><div>From a piece of wood to a fully functional furniture, step by step.</div></field>
<field name="description">From a piece of wood to a fully functional furniture, step by step.</field>
</record>
</data>
</odoo>
@@ -6,15 +6,14 @@
import logging
import werkzeug

from odoo import http, _
from odoo import http, modules, tools, _
from odoo.exceptions import AccessError, UserError
from odoo.http import request
from odoo.osv import expression

from odoo.addons.http_routing.models.ir_http import slug
from odoo.addons.website_profile.controllers.main import WebsiteProfile
from odoo.addons.website.models.ir_http import sitemap_qs2dom
from odoo.tools import html2plaintext

_logger = logging.getLogger(__name__)

@@ -267,7 +266,7 @@ def channel(self, channel, category=None, tag=None, page=1, slide_type=None, sor
'message_post_hash': channel._sign_token(request.env.user.partner_id.id),
'message_post_pid': request.env.user.partner_id.id,
'last_message_id': last_message_data.get('id'),
'last_message': html2plaintext(last_message_data.get('body', '')),
'last_message': tools.html2plaintext(last_message_data.get('body', '')),
'last_rating_value': last_message_data.get('rating_value'),
})

@@ -362,6 +361,36 @@ def slide_download(self, slide, **kw):
return request.redirect('/web/login?redirect=/slides/slide/%s' % (slide.id))
return request.render("website.403")

@http.route('/slides/slide/<int:slide_id>/get_image', type='http', auth="public", website=True, sitemap=False)
def slide_get_image(self, slide_id, field='image_medium', width=0, height=0, crop=False, avoid_if_small=False, upper_limit=False):
# Protect infographics by limiting access to 256px (large) images
if field not in ('image_small', 'image_medium', 'image_large'):
return werkzeug.exceptions.Forbidden()

slide = request.env['slide.slide'].sudo().browse(slide_id).exists()
if not slide:
raise werkzeug.exceptions.NotFound()

status, headers, content = request.env['ir.http'].sudo().binary_content(
model='slide.slide', id=slide.id, field=field,
default_mimetype='image/png')
if status == 301:
return request.env['ir.http']._response_by_status(status, headers, content)
if status == 304:
return werkzeug.wrappers.Response(status=304)

if not content:
content = self._get_default_avatar(field, headers, width, height)

content = tools.limited_image_resize(
content, width=width, height=height, crop=crop, upper_limit=upper_limit, avoid_if_small=avoid_if_small)

image_base64 = base64.b64decode(content)
headers.append(('Content-Length', len(image_base64)))
response = request.make_response(image_base64, headers)
response.status_code = status
return response

# JSONRPC
@http.route('/slides/slide/like', type='json', auth="user", website=True)
def slide_like(self, slide_id, upvote):
@@ -12,7 +12,7 @@
<field name="is_preview" eval="True"/>
<field name="public_views">40</field>
<field name="tag_ids" eval="[(4, ref('website_slides.slide_tag_demo_tools')), (4, ref('website_slides.slide_tag_demo_howto'))]"/>
<field name="description" type="html"><div>A summary of know-how: how and what.</div></field>
<field name="description">A summary of know-how: how and what.</field>
</record>

<record id="slide_slide_demo_1_0" model="slide.slide">
@@ -25,7 +25,7 @@
<field name="is_preview" eval="True"/>
<field name="public_views">5</field>
<field name="tag_ids" eval="[(4, ref('website_slides.slide_tag_demo_colorfull'))]"/>
<field name="description" type="html"><div>Just some basics Tree Infographic.</div></field>
<field name="description">Just some basics Tree Infographic.</field>
</record>
<record id="slide_slide_demo_1_1" model="slide.slide">
<field name="name">Interesting Tree Facts</field>
@@ -37,7 +37,7 @@
<field name="is_preview" eval="True"/>
<field name="public_views">10</field>
<field name="tag_ids" eval="[(4, ref('website_slides.slide_tag_demo_colorfull'))]"/>
<field name="description" type="html"><div>Just some basics Interesting Tree Facts.</div></field>
<field name="description">Just some basics Interesting Tree Facts.</field>
</record>
<record id="slide_slide_demo_1_2" model="slide.slide">
<field name="name">Energy Efficiency Facts</field>
@@ -49,7 +49,7 @@
<field name="is_preview" eval="False"/>
<field name="public_views">15</field>
<field name="tag_ids" eval="[(5, 0)]"/>
<field name="description" type="html"><div>Just some basics Energy Efficiency Facts.</div></field>
<field name="description">Just some basics Energy Efficiency Facts.</field>
</record>
<record id="slide_slide_demo_1_3" model="slide.slide">
<field name="name">How to plant a potted tree</field>
@@ -63,7 +63,7 @@
<field name="is_preview" eval="False"/>
<field name="public_views">0</field>
<field name="tag_ids" eval="[(4, ref('website_slides.slide_tag_demo_howto'))]"/>
<field name="description" type="html"><div>Jim and Todd plant a potted tree for a customer of Knecht's Nurseries and Landscaping. Narrated by Leif Knecht, owner.</div></field>
<field name="description">Jim and Todd plant a potted tree for a customer of Knecht's Nurseries and Landscaping. Narrated by Leif Knecht, owner.</field>
</record>
<record id="slide_slide_demo_1_4" model="slide.slide">
<field name="name">A little chat with Harry Potted</field>
@@ -110,7 +110,7 @@
<field name="is_preview" eval="False"/>
<field name="public_views">5</field>
<field name="tag_ids" eval="[(4, ref('website_slides.slide_tag_demo_howto'))]"/>
<field name="description" type="html"><div>We had a little chat with Harry Potted, sure he had interesting things to say !</div></field>
<field name="description">We had a little chat with Harry Potted, sure he had interesting things to say !</field>
</record>
<record id="slide_slide_demo_1_5" model="slide.slide">
<field name="name">3 Main Methodologies</field>
@@ -123,7 +123,7 @@
<field name="is_preview" eval="False"/>
<field name="public_views">20</field>
<field name="tag_ids" eval="[(4, ref('website_slides.slide_tag_demo_howto'))]"/>
<field name="description" type="html"><div>A summary of know-how: how and what.</div></field>
<field name="description">A summary of know-how: how and what.</field>
</record>

<record id="slide_slide_demo_5_0" model="slide.slide">
Oops, something went wrong.

0 comments on commit f34a7ed

Please sign in to comment.
You can’t perform that action at this time.