[MERGE][IMP] website{_profile, _slide,_forum,*} : improve slides integration in user profile and add karma generation
Purpose of the merge is to continue the work done from PR #30988.
This completes the website profile page, gamifies the under development eLearning [1][2]
and continues integration between eLearning and the forum.
This merge contains notably:
- Complete profile page with course progression detail and eLearning certifications
- Apply Karma gains on various action on the eLearning
- Allow to set the communication type on eLearning course at creation (frontend)
- Redesign website users page
See sub commits for more details.
Merge linked to task ID 1922159 (elearning gamification / website_profile)
and task ID 1940516 (forum comments) PR #31097.
[1] see task ID 1902304 (main eLearning task) PR #29876.
[2] see task ID 1936153 (new homepage for slides) PR #30770.
closes odoo/odoo#31109
This commit is contained in:
@@ -540,31 +540,6 @@ class WebsiteForum(WebsiteProfile):
|
||||
|
||||
# User
|
||||
# --------------------------------------------------
|
||||
@http.route(['''/forum/<model("forum.forum", "[('website_id', 'in', (False, current_website_id))]"):forum>/users''',
|
||||
'''/forum/<model("forum.forum"):forum>/users/page/<int:page>'''],
|
||||
type='http', auth="public", website=True)
|
||||
def users(self, forum, page=1, **searches):
|
||||
User = request.env['res.users']
|
||||
step = 30
|
||||
dom = [('karma', '>', 1), ('website_published', '=', True)]
|
||||
tag_count = User.sudo().search_count(dom)
|
||||
pager = request.website.pager(url="/forum/%s/users" % slug(forum), total=tag_count, page=page, step=step, scope=30)
|
||||
|
||||
if searches.get('user'):
|
||||
dom += [('name', 'ilike', searches.get('user'))]
|
||||
|
||||
users = User.sudo().search(dom, limit=step, offset=pager['offset'], order='karma DESC')
|
||||
|
||||
searches['users'] = 'True'
|
||||
values = self._prepare_user_values(forum=forum, searches=searches)
|
||||
values .update({
|
||||
'users': users,
|
||||
'main_object': forum,
|
||||
'pager': pager,
|
||||
})
|
||||
|
||||
return request.render("website_forum.users", values)
|
||||
|
||||
@http.route(['/forum/<model("forum.forum"):forum>/partner/<int:partner_id>'], type='http', auth="public", website=True)
|
||||
def open_partner(self, forum, partner_id=0, **post):
|
||||
if partner_id:
|
||||
|
||||
@@ -163,7 +163,7 @@
|
||||
t-attf-class="nav-link #{sorting == 'create_date desc' and 'active' or ''}">Newest</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a t-attf-href="/forum/#{ slug(forum) }/users"
|
||||
<a t-attf-href="/profile/users"
|
||||
t-attf-class="nav-link #{searches.get('users') and 'active' or ''}">People</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
@@ -1160,51 +1160,6 @@
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="users">
|
||||
<t t-call="website_forum.header">
|
||||
<div class="row mb-3">
|
||||
<div class="col-sm-12 col-md-5">
|
||||
<form class="input-group" role="form" method="GET">
|
||||
<input type="text" t-att-value="request.params.get('user')" name="user" class="search-query form-control oe_search_box" placeholder="Search..."/>
|
||||
<div class="input-group-append">
|
||||
<button type="submit" class="btn btn-primary oe_search_button" aria-label="Search" title="Search"><i class="fa fa-search"></i></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div t-foreach="users" t-as="u" class="col-xl-4 col-md-6 col-sm-12 mb-4">
|
||||
<div class="card o_card_people h-100">
|
||||
<div class="card-body">
|
||||
<img class="o_wforum_people_photo_container float-left mr-3" t-attf-src="/web/image/res.users/#{u.id}/image_medium"/>
|
||||
<b><i class="text-secondary mr-1 fa fa-diamond"/><t t-esc="u.karma"/></b>
|
||||
<h4 class="font-weight-bold o_card_people_name"><a t-attf-href="/profile/user/#{u.id}?forum_id=#{forum.id}" t-field="u.name"/></h4>
|
||||
<t t-if="u.country_id">
|
||||
<span t-field="u.country_id.image" t-options='{"widget": "image", "class": "country_flag_people"}'/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="card-body text-muted">
|
||||
<b> Badges:
|
||||
<span class="float-right text-dark">
|
||||
<i class="fa fa-trophy badge-gold" role="img" aria-label="Gold badge" title="Gold badge"/>
|
||||
<span t-esc="u.gold_badge" class=""/>
|
||||
<i class="fa fa-trophy badge-silver ml16" role="img" aria-label="Silver badge" title="Silver badge"/>
|
||||
<span t-esc="u.silver_badge" class="" />
|
||||
<i class="fa fa-trophy badge-bronze ml16" role="img" aria-label="Bronze badge" title="Bronze badge"/>
|
||||
<span t-esc="u.bronze_badge" class="" />
|
||||
</span>
|
||||
</b>
|
||||
<t t-raw="0"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="float-left">
|
||||
<t t-call="website.pager"/>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- User Navbar -->
|
||||
<template id="user_navbar_inherit_website_forum" inherit_id="website.user_navbar">
|
||||
<xpath expr="//div[@id='o_new_content_menu_choices']//div[@name='module_website_forum']" position="attributes">
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
<xpath expr="//tr[@id='profile_abstract_info_company']" position="after">
|
||||
<t t-if="forum and (up_votes or down_votes)">
|
||||
<tr id="profile_abstract_info_company">
|
||||
<th style="width: 30%"><b class="text-muted">Votes</b></th>
|
||||
<th style="width: 30%"><span class="font-weight-bold text-muted">Votes</span></th>
|
||||
<th style="width: 70%">
|
||||
<span>
|
||||
<i class="fa fa-thumbs-up text-success" role="img" aria-label="Positive votes" title="Positive votes"/>
|
||||
<b t-esc="up_votes"/>
|
||||
<span class="font-weight-bold" t-esc="up_votes"/>
|
||||
<i class="fa fa-thumbs-down text-danger ml-3" role="img" aria-label="Negative votes" title="Negative votes"/>
|
||||
<b t-esc="down_votes"/>
|
||||
<span class="font-weight-bold" t-esc="down_votes"/>
|
||||
</span>
|
||||
</th>
|
||||
</tr>
|
||||
@@ -52,7 +52,7 @@
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<b>No question posted yet.</b>
|
||||
<span class="font-weight-bold">No question posted yet.</span>
|
||||
</t>
|
||||
</div>
|
||||
<t t-if="favourite">
|
||||
@@ -81,7 +81,7 @@
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<b>No answer posted yet.</b>
|
||||
<span class="font-weight-bold">No answer posted yet.</span>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
@@ -122,7 +122,7 @@
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<b>No activities yet!</b>
|
||||
<span class="font-weight-bold">No activities yet!</span>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
</t>
|
||||
</div>
|
||||
<div class="mb16" t-if="not vote_post">
|
||||
<b>No vote given by you yet!</b>
|
||||
<span class="font-weight-bold">No vote given by you yet!</span>
|
||||
</div>
|
||||
</template>
|
||||
</data></odoo>
|
||||
|
||||
@@ -189,3 +189,45 @@ class WebsiteProfile(http.Controller):
|
||||
'badges': badges,
|
||||
})
|
||||
return request.render("website_profile.badge_main", values)
|
||||
|
||||
# All Users Page
|
||||
# ---------------------------------------------------
|
||||
def _prepare_all_users_values(self, user, position):
|
||||
return {
|
||||
'position': position,
|
||||
'id': user.id,
|
||||
'name': user.name,
|
||||
'rank': user.rank_id.name,
|
||||
'karma': user.karma,
|
||||
'badge_count': len(user.badge_ids),
|
||||
}
|
||||
|
||||
@http.route(['/profile/users',
|
||||
'/profile/users/page/<int:page>'], type='http', auth="public", website=True)
|
||||
def view_all_users_page(self, page=1, **searches):
|
||||
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)]
|
||||
|
||||
# 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'))]
|
||||
|
||||
users = User.sudo().search(dom, limit=step, offset=pager['offset'], order='karma DESC')
|
||||
|
||||
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,
|
||||
'pager': pager
|
||||
}
|
||||
return request.render("website_profile.users_page_main", values)
|
||||
|
||||
@@ -31,37 +31,38 @@ $bronze: #eea91e;
|
||||
}
|
||||
}
|
||||
|
||||
.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_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;
|
||||
@@ -144,6 +145,88 @@ $bronze: #eea91e;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
// All Users Page
|
||||
.o_wprofile_all_users_header {
|
||||
margin: auto;
|
||||
background-color: #875A7B !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_all_user_content {
|
||||
position: relative;
|
||||
top: -140px;
|
||||
.o_wprofile_all_user_card_top3 {
|
||||
cursor: pointer;
|
||||
height: 325px;
|
||||
text-align: center;
|
||||
border-radius: 0px;
|
||||
border: 0px;
|
||||
.card-body {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
> div:first-child {
|
||||
margin-bottom: -40px;
|
||||
div {
|
||||
position: relative;
|
||||
margin: auto;
|
||||
right: -35px;
|
||||
bottom: 55px;
|
||||
width: 45px;
|
||||
padding: 12px 0px 12px 0px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.o_wprofile_top3_first {
|
||||
background-color: $gold;
|
||||
}
|
||||
.o_wprofile_top3_second {
|
||||
background-color: $silver;
|
||||
}
|
||||
.o_wprofile_top3_third {
|
||||
background-color: $bronze;
|
||||
}
|
||||
}
|
||||
:nth-child(4) {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
border-top: 1px solid lightgrey;
|
||||
div {
|
||||
padding: 4% 0% 4% 0%;
|
||||
border-radius: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.o_wprofile_all_users_card {
|
||||
cursor: pointer;
|
||||
border-radius: 0px;
|
||||
border: 0px;
|
||||
padding: 8px 15px 8px 15px;
|
||||
> div > div:first-child > div > div:nth-child(3) {
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
> div > div:first-child > div > div:nth-child(2) {
|
||||
padding: 0px;
|
||||
width: 80px;
|
||||
}
|
||||
> div > div:first-child > div > div:first-child {
|
||||
padding: 5% 0px 5% 16px;
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Badges
|
||||
.badge-gold {
|
||||
color: $gold;
|
||||
@@ -168,7 +251,7 @@ $bronze: #eea91e;
|
||||
|
||||
// Other stuffs
|
||||
.o_wprofile_bb_grey {
|
||||
border-bottom: 2px solid lightgrey;
|
||||
border-bottom: 2px solid lightgrey !important;
|
||||
}
|
||||
|
||||
.country_flag {
|
||||
|
||||
@@ -6,6 +6,11 @@
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
|
||||
<!--
|
||||
Single User Profile Page
|
||||
-->
|
||||
|
||||
<!-- Edit Profile Page -->
|
||||
<template id="user_profile_edit_main" name="Edit Profile">
|
||||
<t t-call="website.layout">
|
||||
@@ -36,7 +41,7 @@
|
||||
</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>
|
||||
<label class="text-primary" for="user_website_published" t-if="user.id == uid"><span class="font-weight-bold">Public profile</span></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"/>
|
||||
@@ -51,14 +56,14 @@
|
||||
<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>
|
||||
<label class="text-primary mb-1 d-block" for="user_name"><span class="font-weight-bold">Real name</span></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>
|
||||
<label class="mb-1 text-primary" for="user_website"><span class="font-weight-bold">Website</span></label>
|
||||
<div>
|
||||
<input type="text" class="form-control" name="website" id="user_website" t-attf-value="#{user.partner_id.website or ''}"/>
|
||||
</div>
|
||||
@@ -68,19 +73,19 @@
|
||||
<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>
|
||||
<label class="mb-1 text-primary" for="user_email"><span class="font-weight-bold">Email</span></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>
|
||||
<label class="mb-1 text-primary" for="user_city"><span class="font-weight-bold">City</span></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>
|
||||
<label class="mb-1 text-primary"><span class="font-weight-bold">Country</span></label>
|
||||
<div>
|
||||
<select class="form-control" name="country">
|
||||
<option value="">Country...</option>
|
||||
@@ -91,7 +96,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-12">
|
||||
<label class="mb-1 text-primary" for="description"><b>Biography</b></label>
|
||||
<label class="mb-1 text-primary" for="description"><span class="font-weight-bold">Biography</span></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>
|
||||
@@ -124,9 +129,9 @@
|
||||
<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>
|
||||
t-attf-href="/profile/edit?url_param=#{edit_button_url_param}"><span class="font-weight-bold">EDIT PROFILE</span></a>
|
||||
<div class="row mb32">
|
||||
<h3 class="o_card_people_name text-white"><b t-field="user.name"/></h3>
|
||||
<h3 class="o_card_people_name text-white"><span class="font-weight-bold" t-field="user.name"/></h3>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="o_wprofile_id_info">
|
||||
@@ -177,7 +182,7 @@
|
||||
<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="font-weight-bold text-muted">Current <a href="/profile/ranks">Rank</a> : </span></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>
|
||||
@@ -190,11 +195,11 @@
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th style="width: 30%"><b class="text-muted">Joined</b></th>
|
||||
<th style="width: 30%"><span class="font-weight-bold text-muted">Joined</span></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: 30%"><span class="font-weight-bold text-muted">Badges</span></th>
|
||||
<th style="width: 70%">
|
||||
<t t-if="user.badge_ids">
|
||||
<span t-esc="len(user.badge_ids)"/>
|
||||
@@ -206,20 +211,20 @@
|
||||
</tr>
|
||||
<t t-if="user.website">
|
||||
<tr>
|
||||
<th style="width: 30%"><b class="text-muted">Website</b></th>
|
||||
<th style="width: 30%"><span class="font-weight-bold text-muted">Website</span></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: 30%"><span class="font-weight-bold text-muted">Location</span></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: 30%"><span class="font-weight-bold text-muted">Company</span></th>
|
||||
<th style="width: 70%"><span t-field="user.company_id"/></th>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -249,8 +254,8 @@
|
||||
<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><span class="text-center font-weight-bold">"<span t-field="user.next_rank_id.name"/>"</span></div>
|
||||
<div><span class="text-center"><span class="font-weight-bold text-primary" t-field="user.karma"/>/<span class="font-weight-bold" 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"
|
||||
@@ -274,7 +279,7 @@
|
||||
<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"/>
|
||||
<span class="font-weight-bold" t-field="badge.badge_id.name"/>
|
||||
</th>
|
||||
</tr></tbody></table>
|
||||
</div>
|
||||
@@ -282,7 +287,7 @@
|
||||
</div>
|
||||
</t>
|
||||
<div class="mb16" t-if="not user.badge_ids">
|
||||
<b>No badge yet!</b><br/>
|
||||
<span class="font-weight-bold">No badge yet!</span><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>
|
||||
@@ -303,10 +308,10 @@
|
||||
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.
|
||||
<span class="font-weight-bold">How do I earn badges?</span> 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.
|
||||
<span class="font-weight-bold">How do I score more points?</span> 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">
|
||||
@@ -321,7 +326,7 @@
|
||||
<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 class="font-weight-bold" 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>
|
||||
@@ -352,12 +357,12 @@
|
||||
<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 t-if="badge.level == 'gold'"><i class="fa fa-trophy badge-gold"/> <span t-field="badge.name" class="font-weight-bold text-white bg-gold badge-pill"/></span>
|
||||
<span t-if="badge.level == 'silver'"><i class="fa fa-trophy badge-silver"/> <span t-field="badge.name" class="font-weight-bold text-white bg-silver badge-pill"/></span>
|
||||
<span t-if="badge.level == 'bronze'"><i class="fa fa-trophy badge-bronze"/> <span t-field="badge.name" class="font-weight-bold text-white bg-bronze badge-pill"/></span>
|
||||
</span>
|
||||
</td><td>
|
||||
<b t-esc="badge.stat_count_distinct"/>
|
||||
<span class="font-weight-bold" t-esc="badge.stat_count_distinct"/>
|
||||
<i class="text-muted"> awarded users</i>
|
||||
</td><td>
|
||||
<span t-field="badge.description"/>
|
||||
@@ -377,4 +382,85 @@
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!--
|
||||
All Users Page
|
||||
-->
|
||||
<template id="users_page_main" name="Users Page">
|
||||
<t t-call="website.layout">
|
||||
<t t-call="website_profile.users_page_header"></t>
|
||||
<t t-call="website_profile.users_page_content"></t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="users_page_header" name="Users Page Header">
|
||||
<div class="row o_wprofile_all_users_header">
|
||||
<div class="col-lg-12">
|
||||
<h2 class="row">Users</h2>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="users_page_content">
|
||||
<div class="row o_wprofile_all_user_content">
|
||||
<div class="container mb32 mt48">
|
||||
<div class="row">
|
||||
<div class="col-lg-4 mb32" t-foreach="top3_users" t-as="user" t-attf-onclick="location.href='/profile/user/#{user['id']}';">
|
||||
<t t-call="website_profile.top3_user_card"></t>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12" t-foreach="users" t-as="user" t-attf-onclick="location.href='/profile/user/#{user['id']}';">
|
||||
<t t-call="website_profile.all_user_card"></t>
|
||||
</div>
|
||||
</div>
|
||||
<div class="float-left">
|
||||
<t t-call="website.pager"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="top3_user_card" name="Top 3 User Card">
|
||||
<div class="card o_wprofile_all_user_card_top3 o_wprofile_bb_grey">
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<img class="mb-3 rounded-circle o_wprofile_img_medium" t-att-src="'/profile/avatar/%s?field=image_medium%s' % (user['id'], '&res_model=%s&res_id=%s' % (record._name, record.id) if record else '')"/>
|
||||
<div t-if="user['position'] == 1" class="o_wprofile_top3_first"><span>1st</span></div>
|
||||
<div t-if="user['position'] == 2" class="o_wprofile_top3_second"><span>2nd</span></div>
|
||||
<div t-if="user['position'] == 3" class="o_wprofile_top3_third"><span>3rd</span></div>
|
||||
</div>
|
||||
<div><h3 t-esc="user['name']"></h3></div>
|
||||
<div><span t-esc="user['rank']"></span></div>
|
||||
<div class="row">
|
||||
<div class="col" style="border-right: 1px solid lightgrey"><span><span t-esc="user['karma']"/> XP</span></div>
|
||||
<div id="top3_user_badge_count" class="col"><span><span t-esc="user['badge_count']"/> Badges</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="all_user_card" name="All User Card">
|
||||
<div class="card o_wprofile_all_users_card o_wprofile_bb_grey">
|
||||
<div class="row">
|
||||
<div class="col-lg-4">
|
||||
<div class="row">
|
||||
<div><span><span t-esc="user['position']"/>th</span></div>
|
||||
<div><img class="rounded-circle o_wprofile_img_small" t-att-src="'/profile/avatar/%s?field=image_medium%s' % (user['id'], '&res_model=%s&res_id=%s' % (record._name, record.id) if record else '')"/></div>
|
||||
<div>
|
||||
<span class="font-weight-bold" t-esc="user['name']"/><br/>
|
||||
<span t-esc="user['rank']"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body col-lg-8">
|
||||
<div class="row">
|
||||
<div class="col"><span><span t-esc="user['karma']"/> XP</span></div>
|
||||
<div id="all_user_badge_count" class="col"><span><span t-esc="user['badge_count']"/> Badges</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</data></odoo>
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
|
||||
from . import mail
|
||||
from . import main
|
||||
from . import portal
|
||||
|
||||
@@ -96,6 +96,9 @@ class WebsiteSlides(WebsiteProfile):
|
||||
domain = expression.AND([domain, [('nbr_%ss' % slide_type, '>', 0)]])
|
||||
return domain
|
||||
|
||||
def _prepare_channel_values(self, **kwargs):
|
||||
return dict(**kwargs)
|
||||
|
||||
# --------------------------------------------------
|
||||
# MAIN / SEARCH
|
||||
# --------------------------------------------------
|
||||
@@ -571,13 +574,15 @@ class WebsiteSlides(WebsiteProfile):
|
||||
return channels
|
||||
|
||||
def _prepare_user_slides_profile(self, user):
|
||||
courses = request.env['slide.channel.partner'].sudo().search([('partner_id', '=', user.partner_id.id)]).mapped('channel_id')
|
||||
courses = request.env['slide.channel.partner'].sudo().search([('partner_id', '=', user.partner_id.id)])
|
||||
courses_completed = courses.filtered(lambda c: c.completed)
|
||||
courses_ongoing = courses - courses_completed
|
||||
values = {
|
||||
'uid': request.env.user.id,
|
||||
'user': user,
|
||||
'main_object': user,
|
||||
'courses': courses,
|
||||
'count_courses': len(courses),
|
||||
'courses_completed': courses_completed,
|
||||
'courses_ongoing': courses_ongoing,
|
||||
'is_profile_page': True,
|
||||
'badge_category': 'slides',
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
from odoo.addons.portal.controllers.mail import PortalChatter
|
||||
|
||||
|
||||
class SlidesPortalChatter(PortalChatter):
|
||||
|
||||
@http.route(['/mail/chatter_post'], type='http', methods=['POST'], auth='public', website=True)
|
||||
def portal_chatter_post(self, res_model, res_id, message, **kw):
|
||||
result = super(SlidesPortalChatter, self).portal_chatter_post(res_model, res_id, message, **kw)
|
||||
if res_model == 'slide.channel':
|
||||
rating_value = kw.get('rating_value', False)
|
||||
slide_channel = request.env[res_model].sudo().browse(int(res_id))
|
||||
if rating_value and slide_channel and request.env.user.partner_id.id == int(kw.get('pid')):
|
||||
# apply karma gain rule only once
|
||||
request.env.user.add_karma(slide_channel.karma_gen_channel_rank)
|
||||
return result
|
||||
@@ -45,8 +45,18 @@ class ChannelUsersRelation(models.Model):
|
||||
slide_done = mapped_data.get(record.channel_id.id, dict()).get(record.partner_id.id, 0)
|
||||
slide_total = channel_data.get(record.channel_id.id) or 1
|
||||
record.completion = math.ceil(100.0 * slide_done / slide_total)
|
||||
if record.completion >= 100:
|
||||
record.update({'completed': True})
|
||||
|
||||
def _write(self, values):
|
||||
if 'completion' in values and values['completion'] >= 100:
|
||||
values['completed'] = True
|
||||
result = super(ChannelUsersRelation, self)._write(values)
|
||||
partner_has_completed = {channel_partner.partner_id.id: channel_partner.channel_id for channel_partner in self}
|
||||
users = self.env['res.users'].sudo().search([('partner_id', 'in', list(partner_has_completed.keys()))])
|
||||
for user in users:
|
||||
users.add_karma(partner_has_completed[user.partner_id.id].karma_gen_channel_finish)
|
||||
else:
|
||||
result = super(ChannelUsersRelation, self)._write(values)
|
||||
return result
|
||||
|
||||
|
||||
class Channel(models.Model):
|
||||
@@ -128,6 +138,12 @@ class Channel(models.Model):
|
||||
completion = fields.Integer('Completion', compute='_compute_user_statistics')
|
||||
can_upload = fields.Boolean('Can Upload', compute='_compute_access')
|
||||
can_publish = fields.Boolean('Can Publish', compute='_compute_access')
|
||||
# karma generation
|
||||
karma_gen_slide_vote = fields.Integer(string='Lesson voted', default=1)
|
||||
karma_gen_channel_share = fields.Integer(string='Course shared', default=2)
|
||||
karma_gen_channel_rank = fields.Integer(string='Course ranked', default=5)
|
||||
karma_gen_channel_finish = fields.Integer(string='Course finished', default=10)
|
||||
# TODO DBE : Add karma based action rules (like in forum)
|
||||
|
||||
@api.depends('slide_ids.is_published')
|
||||
def _compute_slide_last_update(self):
|
||||
|
||||
@@ -412,6 +412,7 @@ class Slide(models.Model):
|
||||
new_slide.write({
|
||||
'slide_partner_ids': [(0, 0, {'vote': new_vote, 'partner_id': self.env.user.partner_id.id})]
|
||||
})
|
||||
self.env.user.add_karma(new_slide.channel_id.karma_gen_slide_vote)
|
||||
|
||||
def action_set_viewed(self):
|
||||
if not all(slide.channel_id.is_member for slide in self):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.o_wprofile_slides_card {
|
||||
padding: 8px;
|
||||
max-height: 68px;
|
||||
min-width: 350px;
|
||||
}
|
||||
.o_wprofile_slides_title {
|
||||
top: -15px;
|
||||
@@ -10,7 +11,7 @@
|
||||
// Quest - Course Card
|
||||
.o_wprofile_slides_course_card_body {
|
||||
cursor: pointer;
|
||||
max-height: 64px;
|
||||
height: 66px;
|
||||
.o_wprofile_slides_course_img {
|
||||
position: absolute;
|
||||
padding: 0px;
|
||||
@@ -24,4 +25,19 @@
|
||||
top: 13px;
|
||||
right: 16px;
|
||||
}
|
||||
.o_wprofile_slide_course_progress {
|
||||
position: absolute;
|
||||
bottom: 4px;
|
||||
width: 100%;
|
||||
padding-left: 88px;
|
||||
.o_wprofile_slide_course_progress_bar {
|
||||
background-color: lightgray;
|
||||
top: 2px;
|
||||
padding: 0px;
|
||||
}
|
||||
.o_wprofile_slide_course_progress_value {
|
||||
min-width: 70px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<textarea name="description" id="description" style="height:8em" class="form-control" placeholder="Write here a short description of your first course"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="form-group row o_wslide_channel_communication_type">
|
||||
<div class="col-sm-2">Review</div>
|
||||
<div class="col-sm-10">
|
||||
<div class="form-check">
|
||||
|
||||
@@ -90,7 +90,6 @@
|
||||
<group>
|
||||
<group>
|
||||
<field name="channel_type" widget="radio"/>
|
||||
<field name="allow_comment"/>
|
||||
<field name="website_id" options="{'no_create': True}" groups="website.group_multi_website"/>
|
||||
<field name="promote_strategy" widget="radio"/>
|
||||
</group>
|
||||
@@ -105,6 +104,11 @@
|
||||
<field name="enroll_msg" colspan="4" nolabel="1"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Communication">
|
||||
<group>
|
||||
<field name="allow_comment"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Statistics" groups="base.group_no_one">
|
||||
<group>
|
||||
<group>
|
||||
|
||||
@@ -13,38 +13,58 @@
|
||||
<xpath expr="//div[@id='profile_about_badge']" position="before">
|
||||
<t t-if="channel">
|
||||
<div class="mb32">
|
||||
<h4><i class="fa fa-graduation-cap"/> Courses</h4>
|
||||
<t t-call="website_slides.display_course"/>
|
||||
<h4><i class="fa fa-graduation-cap"/> Completed Courses</h4>
|
||||
<t t-if="courses_completed">
|
||||
<t t-call="website_slides.display_course">
|
||||
<t t-set="courses" t-value="courses_completed"></t>
|
||||
</t>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span class="font-weight-bold">No completed course yet!</span>
|
||||
</t>
|
||||
</div>
|
||||
<div class="mb32">
|
||||
<h4><i class="fa fa-book"/> Followed Courses</h4>
|
||||
<t t-if="courses_ongoing">
|
||||
<t t-call="website_slides.display_course">
|
||||
<t t-set="courses" t-value="courses_ongoing"></t>
|
||||
</t>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span class="font-weight-bold">No followed course!</span>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="display_course" name="Profile Courses">
|
||||
<t t-if="courses">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 o_wprofile_slides_card" t-foreach="courses" t-as="course">
|
||||
<div class="card-body o_wprofile_slides_course_card_body o_wprofile_bb_grey" t-attf-onclick="location.href='/slides/#{slug(course)}';">
|
||||
<div class="row">
|
||||
<div class="o_wprofile_slides_course_img">
|
||||
<img class="o_wprofile_img_small mb-3" t-attf-src="/web/image/slide.channel/#{course.id}/image_small"/>
|
||||
</div>
|
||||
<div class="col-10 o_wprofile_slides_title">
|
||||
<b t-field="course.name"/>
|
||||
</div>
|
||||
<div class="o_wprofile_slide_course_rating">
|
||||
<t t-call="website_rating.rating_widget_stars_static">
|
||||
<t t-set="rating_avg" t-value="course.rating_avg"/>
|
||||
<t t-set="rating_count" t-value="course.rating_count"/>
|
||||
</t>
|
||||
<template id="display_course">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 o_wprofile_slides_card" t-foreach="courses" t-as="course">
|
||||
<div class="card-body o_wprofile_slides_course_card_body o_wprofile_bb_grey" t-attf-onclick="location.href='/slides/#{slug(course.channel_id)}';">
|
||||
<div class="row">
|
||||
<div class="o_wprofile_slides_course_img">
|
||||
<img class="o_wprofile_img_small" t-attf-src="/web/image/slide.channel/#{course.channel_id.id}/image_small"/>
|
||||
</div>
|
||||
<div class="col-10 o_wprofile_slides_title">
|
||||
<span class="font-weight-bold" t-field="course.channel_id.name"/>
|
||||
</div>
|
||||
<div class="o_wprofile_slide_course_rating">
|
||||
<t t-call="website_rating.rating_widget_stars_static">
|
||||
<t t-set="rating_avg" t-value="course.channel_id.rating_avg"/>
|
||||
<t t-set="rating_count" t-value="course.channel_id.rating_count"/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="row o_wprofile_slide_course_progress">
|
||||
<div class="col progress o_wprofile_slide_course_progress_bar">
|
||||
<div class="progress-bar" style="background-color: #00A09D !important"
|
||||
t-att-style="'width: '+ str(course.completion)+'%'"></div>
|
||||
</div>
|
||||
<span class="col-3 text-primary o_wprofile_slide_course_progress_value"><span t-field="course.completion"/> %</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<b>No followed course yet!</b>
|
||||
</t>
|
||||
</div>
|
||||
</template>
|
||||
</data></odoo>
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
'views/slide_channel_views.xml',
|
||||
'views/website_slides_templates.xml',
|
||||
'data/slide_channel_demo.xml',
|
||||
'views/assets.xml',
|
||||
'views/website_slides_forum_templates.xml'
|
||||
],
|
||||
'auto_install': True,
|
||||
}
|
||||
|
||||
@@ -6,8 +6,21 @@ from odoo.addons.website_slides.controllers.main import WebsiteSlides
|
||||
|
||||
|
||||
class WebsiteSlidesForum(WebsiteSlides):
|
||||
|
||||
def _slide_channel_prepare_values(self, **kwargs):
|
||||
communication_type = kwargs.get('communication_type')
|
||||
if communication_type:
|
||||
if communication_type == 'forum':
|
||||
forum = request.env['forum.forum'].create({
|
||||
'name': kwargs.get('name')
|
||||
})
|
||||
kwargs['forum_id'] = forum.id
|
||||
kwargs['allow_comment'] = communication_type == 'comment'
|
||||
return super(WebsiteSlidesForum, self)._slide_channel_prepare_values(**kwargs)
|
||||
|
||||
# Profile
|
||||
# ---------------------------------------------------
|
||||
|
||||
def _prepare_user_profile_parameters(self, **post):
|
||||
post = super(WebsiteSlidesForum, self)._prepare_user_profile_parameters(**post)
|
||||
if post.get('channel_id'):
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class Forum(models.Model):
|
||||
_inherit = 'forum.forum'
|
||||
|
||||
slide_channel_id = fields.Many2one('slide.channel', 'Course')
|
||||
slide_channel_ids = fields.One2many('slide.channel', 'forum_id', 'Courses')
|
||||
slide_channel_id = fields.Many2one('slide.channel', 'Course', compute='_compute_slide_channel_id')
|
||||
|
||||
@api.depends('slide_channel_ids')
|
||||
def _compute_slide_channel_id(self):
|
||||
for forum in self:
|
||||
if forum.slide_channel_ids:
|
||||
forum.slide_channel_id = forum.slide_channel_ids[0]
|
||||
else:
|
||||
forum.slide_channel_id = None
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_forum_forum_website_publisher,access.forum.forum.website.publisher,model_forum_forum,base.group_website_publisher,1,1,1,0
|
||||
|
@@ -0,0 +1,13 @@
|
||||
odoo.define('website_slides_forum.editor', function (require) {
|
||||
"use strict";
|
||||
|
||||
var core = require('web.core');
|
||||
var QWeb = core.qweb;
|
||||
var WebsiteNewMenu = require('website.newMenu');
|
||||
|
||||
WebsiteNewMenu.include({
|
||||
xmlDependencies: WebsiteNewMenu.prototype.xmlDependencies.concat(
|
||||
['/website_slides_forum/static/src/xml/website_slides_forum_channel.xml']
|
||||
),
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
<t t-extend="website.slide.channel.create">
|
||||
<t t-jquery=".o_wslide_channel_communication_type" t-operation="replace">
|
||||
<fieldset id="channel_create_forum" class="form-group">
|
||||
<div class="row">
|
||||
<legend class="col-form-label col-sm-2">Communication</legend>
|
||||
<div class="col-sm-10">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="communication_type" id="communication_type1" value="nothing" checked="checked"/>
|
||||
<label class="form-check-label" for="communication_type1">
|
||||
No communication
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="communication_type" id="communication_type2" value="forum"/>
|
||||
<label class="form-check-label" for="communication_type2">
|
||||
Create a Forum
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="communication_type" id="communication_type3" value="comment"/>
|
||||
<label class="form-check-label" for="communication_type3">
|
||||
Allow comments
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</t>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="assets_frontend" inherit_id="website.assets_frontend" name="Website slide forum frontend assets">
|
||||
<xpath expr="//script[last()]" position="after">
|
||||
<script type="text/javascript" src="/website_slides_forum/static/src/js/website_slides_forum.editor.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
@@ -5,12 +5,8 @@
|
||||
<field name="model">slide.channel</field>
|
||||
<field name="inherit_id" ref="website_slides.view_slide_channel_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//notebook" position="inside">
|
||||
<page string="Forum">
|
||||
<group>
|
||||
<field name="forum_id"/>
|
||||
</group>
|
||||
</page>
|
||||
<xpath expr="//field[@name='allow_comment']" position="after">
|
||||
<field name="forum_id"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data>
|
||||
<template id="website_slides_forum_header" inherit_id="website_forum.header">
|
||||
<xpath expr="//div[hasclass('container')]" position="before">
|
||||
<t t-if="forum.slide_channel_id">
|
||||
<div class="row" style="background-color: #66445e; height: 48px;">
|
||||
<div class="container o_wprofile_navtabs_container">
|
||||
<ul class="nav nav-tabs o_wprofile_nav_tabs" role="tablist" id="profile_extra_info_tablist">
|
||||
<li class="nav-item">
|
||||
<a t-att-href="'/slides/%s%s?%s' % (slug(forum.slide_channel_id), '/%s' % slug(category) if category else '', keep_query('debug'))"
|
||||
t-att-class="'nav-link o_wprofile_navlink'" style="border-left: 0px">
|
||||
<i class="fa fa-home"/> Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#" t-att-class="'nav-link active o_wprofile_navlink'" style="border-left: 0px">Forum</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a t-att-href="'/slides/%s' % (slug(forum.slide_channel_id))"
|
||||
t-att-class="'nav-link o_wprofile_navlink'" style="border-left: 0px">Review</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
</data></odoo>
|
||||
@@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import models
|
||||
from . import controllers
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
'description': """This module lets you use the full power of certifications within your courses.""",
|
||||
'category': 'Hidden',
|
||||
'version': '0.1',
|
||||
|
||||
'depends': ['website_slides', 'website_survey'],
|
||||
'installable': True,
|
||||
'auto_install': True,
|
||||
@@ -17,6 +16,7 @@
|
||||
'views/website_slides_templates.xml',
|
||||
'views/website_slides_templates_homepage.xml',
|
||||
'views/survey_templates.xml',
|
||||
'views/website_profile.xml',
|
||||
'data/gamification_data.xml',
|
||||
],
|
||||
'demo': [
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import main
|
||||
from . import slides
|
||||
|
||||
@@ -47,3 +47,30 @@ class WebsiteSlides(WebsiteSlides):
|
||||
result['certification_url'] = user_input._get_survey_url()
|
||||
|
||||
return result
|
||||
|
||||
# Profile
|
||||
# ---------------------------------------------------
|
||||
def _prepare_user_slides_profile(self, user):
|
||||
values = super(WebsiteSlides, self)._prepare_user_slides_profile(user)
|
||||
values.update({
|
||||
'certificates': self._get_user_certificates(user),
|
||||
})
|
||||
return values
|
||||
|
||||
# All Users Page
|
||||
# ---------------------------------------------------
|
||||
def _prepare_all_users_values(self, user, position):
|
||||
result = super(WebsiteSlides, self)._prepare_all_users_values(user, position)
|
||||
result.update({
|
||||
'certification_count': len(self._get_user_certificates(user))
|
||||
})
|
||||
return result
|
||||
|
||||
def _get_user_certificates(self, user):
|
||||
domain = [
|
||||
('partner_id', '=', user.partner_id.id),
|
||||
('quizz_passed', '=', True),
|
||||
('slide_partner_id.survey_quizz_passed', '=', True)
|
||||
]
|
||||
certifications = request.env['survey.user_input'].sudo().search(domain)
|
||||
return certifications
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// Certification Card
|
||||
.o_wprofile_slides_certif_card_body {
|
||||
height: 66px;
|
||||
> div > div:first-child {
|
||||
position: absolute;
|
||||
padding: 0px;
|
||||
top: 8px;
|
||||
left: 8px;
|
||||
width: 66px;
|
||||
height: 66px;
|
||||
}
|
||||
> div > div:nth-child(3) > span {
|
||||
position: absolute;
|
||||
right: 32px;
|
||||
top: 13px;
|
||||
}
|
||||
> div > div > div:nth-child(2) {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
right: 32px;
|
||||
a {
|
||||
margin-left: 8px;
|
||||
color: grey;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@
|
||||
<xpath expr="//script[last()]" position="after">
|
||||
<script type="text/javascript" src="/website_slides_survey/static/src/js/slides_upload.js"/>
|
||||
<script type="text/javascript" src="/website_slides_survey/static/src/js/slides_certification_download.js"/>
|
||||
<link rel="stylesheet" type="text/scss" href="/website_slides_survey/static/src/scss/website_profile.scss"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data>
|
||||
<template id="user_profile_content" inherit_id="website_profile.user_profile_content">
|
||||
<xpath expr="//div[@id='profile_about_badge']" position="before">
|
||||
<t t-if="channel">
|
||||
<div class="mb32">
|
||||
<h4><i class="fa fa-trophy"/> Certifications</h4>
|
||||
<t t-call="website_slides_survey.display_certificate"/>
|
||||
</div>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="display_certificate">
|
||||
<t t-if="certificates">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 o_wprofile_slides_card" t-foreach="certificates" t-as="certificate">
|
||||
<div class="card-body o_wprofile_slides_certif_card_body o_wprofile_bb_grey">
|
||||
<div class="row">
|
||||
<div><img class="o_wprofile_img_small" t-attf-src="/web/image/slide.slide/#{certificate.slide_id.id}/image_small"/></div>
|
||||
<div class="col-10 o_wprofile_slides_title">
|
||||
<span class="font-weight-bold" t-field="certificate.survey_id.title"/>
|
||||
</div>
|
||||
<div class="float-right" t-if="user.id == uid">
|
||||
<span class="row">Score : <b t-field="certificate.quizz_score"/>%</span>
|
||||
<div class="row">
|
||||
<a role="button"
|
||||
t-att-data-survey_id="certificate.survey_id.id"
|
||||
class="o_wslides_survey_download_certification"
|
||||
href="#">
|
||||
<i class="fa fa-download"/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<b>No certifications yet!</b>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="top3_user_card" inherit_id="website_profile.top3_user_card">
|
||||
<xpath expr="//div[@id='top3_user_badge_count']" position="after">
|
||||
<div class="col" style="border-left: 1px solid lightgrey"><span><span t-esc="user['certification_count']"/> Certifications</span></div>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="all_user_card" inherit_id="website_profile.all_user_card">
|
||||
<xpath expr="//div[@id='all_user_badge_count']" position="after">
|
||||
<div class="col"><span><span t-esc="user['certification_count']"/> Certifications</span></div>
|
||||
</xpath>
|
||||
</template>
|
||||
</data></odoo>
|
||||
Reference in New Issue
Block a user