[MERGE] website_slides: improve layout of courses main pages and apply various fixes

Purpose of this merge is to provide several fixes related to new elearning
application [1]. This is second batch of fixes [2]. Those are mainly fixes
and improvements for front-end templates: layouting, clean design. Some
other fixes are also included, notably links and search capabilities.

See sub commits for more details.

Thanks to @awa-odoo @jem-odoo @stefanorigano @qmo-odoo @dbeguin
for their work as most of code come from cherry-picks.

Merge linked to task ID 1941250.

[1] eLearning feature: see task ID 1902304
[2] First batch of fixes landed at 5cec06b179

closes odoo/odoo#31317
This commit is contained in:
Robot Odoo
2019-02-21 18:29:32 +01:00
committed by GitHub
29 changed files with 508 additions and 298 deletions
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data noupdate="1">
<odoo><data noupdate="0">
<!-- Set demo karma for test tour as demo user-->
<record id="base.user_demo" model="res.users">
<field name="karma">20</field>
@@ -15,7 +15,7 @@
<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"/>
<img class="ml-3 img img-fluid" style="max-height: 72px;" src="/gamification/static/img/rank_misc_mug.png"/>
</div></field>
<field name="karma_min">100</field>
<field name="image" type="base64" file="gamification/static/img/rank_student_badge.svg"/>
@@ -27,7 +27,7 @@
<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"/>
<img class="ml-3 img img-fluid" style="max-height: 72px;" src="/gamification/static/img/rank_misc_wand.png"/>
</div></field>
<field name="karma_min">500</field>
<field name="image" type="base64" file="gamification/static/img/rank_bachelor_badge.svg"/>
@@ -39,7 +39,7 @@
<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"/>
<img class="ml-3 img img-fluid" style="max-height: 72px;" src="/gamification/static/img/rank_misc_hat.png"/>
</div></field>
<field name="karma_min">2000</field>
<field name="image" type="base64" file="gamification/static/img/rank_master_badge.svg"/>
@@ -51,7 +51,7 @@
<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"/>
<img class="ml-3 img img-fluid" style="max-height: 72px;" src="/gamification/static/img/rank_misc_unicorn.png"/>
</div></field>
<field name="karma_min">10000</field>
<field name="image" type="base64" file="gamification/static/img/rank_doctor_badge.svg"/>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

@@ -120,6 +120,73 @@ $bronze: #eea91e;
}
}
/// Progress Circle
.o_wprofile_progress_circle {
position: relative;
$circle-size: 220px;
$circle-background: rgba(black, 0.1);
$circle-color: theme-color('primary');
$inset-size: $circle-size - 20;
$transition-length: 1s;
.o_progress_circle_container, .o_pc, .o_pc div {
@include size($circle-size);
}
.o_pc_overlay, .o_pc, .o_pc div {
border-radius: 50%;
}
.o_progress_circle_container {
position: relative;
margin: 0 auto;
background-color: inherit;
}
.o_pc_overlay {
$tmp-gap: ($circle-size - $inset-size)/2;
@include o-position-absolute($tmp-gap, $left: $tmp-gap);
@include size($inset-size);
background-color: inherit;
}
.o_pc {
background-color: $circle-background;
div {
position: absolute;
-webkit-backface-visibility: hidden;
transition: transform $transition-length;
&.o_pc_slice {
clip: rect(0px, $circle-size, $circle-size, $circle-size/2);
.o_pc_fill {
clip: rect(0px, $circle-size/2, $circle-size, 0px);
background-color: $circle-color;
}
}
}
$i: 0;
$increment: 180deg / 100;
@while $i <= 100 {
&[data-progress='#{$i}'] {
.o_pc_slice.full, .o_pc_fill {
transform: rotate($increment * $i);
}
.o_pc_fill.o_pc_bar {
transform: rotate($increment * $i * 2);
}
$i: $i + 1;
}
}
}
}
.o_wprofile_next_rank_card {
div {
text-align: center;
@@ -252,19 +252,29 @@
</template>
<template id="profile_next_rank_card" name="Profile Next Rank Card">
<div class="card-body o_wprofile_next_rank_card mt-2 pt-2">
<div><img class="o_wprofile_img_medium" t-attf-src="/web/image/gamification.karma.rank/#{user.next_rank_id.id}/image_medium"/></div>
<div><span class="text-center 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"
t-att-style="'width: '+ str(100*(user.karma - user.rank_id.karma_min)/(user.next_rank_id.karma_min - user.rank_id.karma_min))+'%'"></div>
</t>
<t t-else="">
<div class="progress-bar o_wprofile_next_rank_progress_bar" style="background-color: #00A09D !important"
t-att-style="'width: 0%'"></div>
</t>
<div t-attf-class="o_wprofile_progress_circle #{bg_class if bg_class else ''}">
<div class="o_progress_circle_container">
<div class="o_pc" t-att-data-progress="int(100*(user.karma - user.rank_id.karma_min)/(user.next_rank_id.karma_min - user.rank_id.karma_min))" >
<div class="o_pc_circle">
<div class="o_pc_slice full">
<div class="o_pc_fill"/>
</div>
<div class="o_pc_slice">
<div class="o_pc_fill full"/>
<div class="o_pc_fill o_pc_bar"/>
</div>
</div>
</div>
<div class="o_pc_overlay d-flex flex-column align-items-center justify-content-center">
<img class="img-fluid"
t-attf-src="/web/image/gamification.karma.rank/#{user.next_rank_id.id}/image_medium"
t-att-alt="user.next_rank_id.name + ' badge'"
t-att-style="'max-width: ' + (img_max_width if img_max_width else '60%;')"/>
<h4 class="mt-2 mb-0">"<span t-field="user.next_rank_id.name"/>"</h4>
<small>
<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
</small>
</div>
</div>
</div>
</template>
@@ -305,10 +315,10 @@
Ranks &amp; Badges
</h1>
<p>
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.
Keep learning with <t t-esc="user.company_id.name"/>. Collect points on the forum or on the eLearning platform. Those points will make you reach new ranks.
</p>
<p>
<span class="font-weight-bold">How do I earn badges?</span> 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 or reach milestones, you're awarded badges.
</p>
<p>
<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.
@@ -33,6 +33,11 @@ odoo.define('website_rating.thread', function(require) {
init: function(parent, options){
this._super.apply(this, arguments);
// apply ratio to default rating value
if (options.default_rating_value) {
options.default_rating_value = parseFloat(options.default_rating_value) / STAR_RATING_RATIO;
}
// default options
this.options = _.defaults(this.options, {
'default_message': false,
@@ -50,7 +55,7 @@ odoo.define('website_rating.thread', function(require) {
'5': _t("I love it"),
};
this.user_click = false; // user has click or not
this.set("star_value", this.options.rating_default_value);
this.set("star_value", this.options.default_rating_value);
this.on("change:star_value", this, this._onChangeStarValue);
},
start: function(){
@@ -59,7 +64,7 @@ odoo.define('website_rating.thread', function(require) {
// rating stars
self.$input = self.$('input[name="rating_value"]');
self.$star_list = self.$('.stars').find('i');
self.set("star_value", self.options.rating_default_value); // set the default value to trigger the display of star widget
self.set("star_value", self.options.default_rating_value); // set the default value to trigger the display of star widget
});
},
@@ -265,7 +270,7 @@ odoo.define('website_rating.thread', function(require) {
_onChangeRatingDomain: function(){
var domain = [];
if(this.get('rating_value')){
domain = [['rating_value', '=', this.get('rating_value')]];
domain = [['rating_value', '=', this.get('rating_value') * STAR_RATING_RATIO]];
}
this._changeCurrentPage(1, domain);
},
@@ -79,7 +79,7 @@
<div class="rate_text">
<span class="badge badge-info"></span>
</div>
<input type="hidden" readonly="readonly" name="rating_value" t-att-value="default_rating"/>
<input type="hidden" readonly="readonly" name="rating_value" t-att-value="default_rating || ''"/>
</div>
</t>
@@ -18,7 +18,7 @@
Rating widget static: show 5 stars (full or empty) regarding the given rating_avg and rating_count
-->
<template id="rating_widget_stars_static" name="Rating: static star widget">
<t t-set="rating_avg" t-value="round(rating_avg * 100) / 100"/>
<t t-set="rating_avg" t-value="round(rating_avg * 100) / 100 / 2"/>
<t t-set="val_decimal" t-value="round(rating_avg % 1, 1)"/>
<t t-set="val_integer" t-value="int(rating_avg)"/>
<t t-set="empty_star" t-value="5 - (val_integer+1) if val_decimal else 5 - val_integer"/>
@@ -33,6 +33,7 @@ class SlidesMail(http.Controller):
('res_id', '=', res_id),
('website_published', '=', True),
('author_id', '=', request.env.user.partner_id.id),
('message_type', '=', 'comment'),
('id', '=', message_id)
] # restrict to the given message_id
message = request.env['mail.message'].search(domain, limit=1)
+59 -21
View File
@@ -100,9 +100,8 @@ class WebsiteSlides(WebsiteProfile):
tags |= search_tag
return tags
def _build_channel_domain(self, base_domain, slide_type=None, **post):
def _build_channel_domain(self, base_domain, slide_type=None, my=False, **post):
search_term = post.get('search')
category_id = post.get('category_id')
channel_tag_id = post.get('channel_tag_id')
tags = self._extract_channel_tag_search(**post)
@@ -111,15 +110,16 @@ class WebsiteSlides(WebsiteProfile):
domain = expression.AND([
domain,
['|', ('name', 'ilike', search_term), ('description', 'ilike', search_term)]])
if category_id:
domain = expression.AND([domain, [('category_ids', 'in', [category_id])]])
elif channel_tag_id:
if channel_tag_id:
domain = expression.AND([domain, [('tag_ids', 'in', [channel_tag_id])]])
elif tags:
domain = expression.AND([domain, [('tag_ids', 'in', tags.ids)]])
if slide_type and 'nbr_%s' % slide_type in request.env['slide.channel']:
domain = expression.AND([domain, [('nbr_%s' % slide_type, '>', 0)]])
if my:
domain = expression.AND([domain, [('partner_ids', '=', request.env.user.partner_id.id)]])
return domain
def _prepare_channel_values(self, **kwargs):
@@ -179,24 +179,29 @@ class WebsiteSlides(WebsiteProfile):
})
@http.route('/slides/all', type='http', auth="public", website=True)
def slides_channel_all(self, slide_type=None, **post):
def slides_channel_all(self, slide_type=None, my=False, **post):
""" Home page displaying a list of courses displayed according to some
criterion and search terms.
: param string slide_type: if provided, filter the slide.channels
to contain at least one slide of type 'slide_type'
: param dict post: post parameters, including
* search_term: keywords entered in the search box, used to filter on slide content;
* category_id: id of a slide.category;
* channel_tag_id: id of a channel.tag;
:param string slide_type: if provided, filter the course to contain at
least one slide of type 'slide_type'. Used notably to display courses
with certifications;
:param bool my: if provided, filter the slide.channels for which the
current user is a member of
:param dict post: post parameters, including
* ``search``: filter on course description / name;
* ``channel_tag_id``: filter on courses containing this tag;
* ``channel_tag_group_id_<id>``: filter on courses containing this tag
in the tag group given by <id> (used in navigation based on tag group);
"""
domain = request.website.website_domain()
domain = self._build_channel_domain(domain, slide_type=slide_type, **post)
domain = self._build_channel_domain(domain, slide_type=slide_type, my=my, **post)
order = self._channel_order_by_criterion.get(post.get('sorting', 'date'), 'create_date desc')
channels = request.env['slide.channel'].search(domain, order=order)
channels_layouted = list(itertools.zip_longest(*[iter(channels)] * 4, fillvalue=None))
# channels_layouted = list(itertools.zip_longest(*[iter(channels)] * 4, fillvalue=None))
tag_groups = request.env['slide.channel.tag.group'].search(['&', ('tag_ids', '!=', False), ('website_published', '=', True)])
search_tags = self._extract_channel_tag_search(**post)
@@ -204,10 +209,13 @@ class WebsiteSlides(WebsiteProfile):
return request.render('website_slides.courses_all', {
'user': request.env.user,
'is_public_user': request.website.is_public_user(),
'channels_layouted': channels_layouted,
'channels': channels,
'tag_groups': tag_groups,
'search_term': post.get('search'),
'search_slide_type': slide_type,
'search_my': my,
'search_tags': search_tags,
'search_channel_tag_id': post.get('channel_tag_id'),
})
def _prepare_additional_channel_values(self, values, **kwargs):
@@ -221,11 +229,12 @@ class WebsiteSlides(WebsiteProfile):
'/slides/<model("slide.channel"):channel>/category/<model("slide.category"):category>',
'/slides/<model("slide.channel"):channel>/category/<model("slide.category"):category>/page/<int:page>'
], type='http', auth="public", website=True, sitemap=sitemap_slide)
def channel(self, channel, category=None, tag=None, page=1, slide_type=None, sorting=None, search=None, **kw):
def channel(self, channel, category=None, tag=None, page=1, slide_type=None, uncategorized=False, sorting=None, search=None, **kw):
if not channel.can_access_from_current_website():
raise werkzeug.exceptions.NotFound()
domain = [('channel_id', '=', channel.id)]
if not request.env.user.has_group('website.group_website_publisher'):
if not channel.can_publish:
domain = expression.AND([
domain,
['&', ('website_published', '=', True), ('channel_id.website_published', '=', True)]
@@ -250,7 +259,10 @@ class WebsiteSlides(WebsiteProfile):
elif tag:
domain += [('tag_ids.id', '=', tag.id)]
pager_url += "/tag/%s" % tag.id
if slide_type:
if uncategorized:
domain += [('category_id', '=', False)]
pager_url += "?uncategorized=1"
elif slide_type:
domain += [('slide_type', '=', slide_type)]
pager_url += "?slide_type=%s" % slide_type
@@ -270,6 +282,7 @@ class WebsiteSlides(WebsiteProfile):
'search_category': category,
'search_tag': tag,
'search_slide_type': slide_type,
'search_uncategorized': uncategorized,
'slide_types': slide_types,
'sorting': actual_sorting,
'search': search,
@@ -287,6 +300,7 @@ class WebsiteSlides(WebsiteProfile):
('model', '=', channel._name),
('res_id', '=', channel.id),
('author_id', '=', request.env.user.partner_id.id),
('message_type', '=', 'comment'),
('website_published', '=', True)
], order='write_date DESC', limit=1).read(['body', 'rating_value'])
last_message_data = last_message_values[0] if last_message_values else {}
@@ -301,24 +315,48 @@ class WebsiteSlides(WebsiteProfile):
# Display uncategorized slides
# fetch slides; done as sudo because we want to display all of them but
# unreachable ones won't be clickable (+ slide controller will crash anyway)
if not category:
if not category and not uncategorized:
category_data = []
for category in request.env['slide.slide'].sudo().read_group(domain, ['category_id'], ['category_id']):
category_id, name = category.get('category_id') or (False, _('Uncategorized'))
slides_sudo = request.env['slide.slide'].sudo().search(category['__domain'], limit=4, offset=0, order=order)
category_data.append({
'id': category_id,
'name': name,
'slug_name': slug((category_id, name)) if category_id else name,
'total_slides': category['category_id_count'],
'slides': request.env['slide.slide'].sudo().search(category['__domain'], limit=4, offset=0, order=order)
'slides': slides_sudo,
})
elif uncategorized:
slides_sudo = request.env['slide.slide'].sudo().search(domain, limit=self._slides_per_page, offset=pager['offset'], order=order)
category_data = [{
'id': False,
'name': _('Uncategorized'),
'slug_name': _('Uncategorized'),
'total_slides': len(slides_sudo),
'slides': slides_sudo,
}]
else:
slides_sudo = request.env['slide.slide'].sudo().search(domain, limit=self._slides_per_page, offset=pager['offset'], order=order)
category_data = [{
'id': category.id,
'name': category.name,
'slug_name': slug(category),
'total_slides': len(slides_sudo),
'slides': slides_sudo,
}]
# post slide-fetch computation: promoted, user completion (separated because sudo-ed)
if not request.website.is_public_user() and channel.is_member:
displayed_slide_ids = list(set(sid for item in category_data for sid in item['slides'].ids))
done_slide_ids = request.env['slide.slide.partner'].sudo().search([
('slide_id', 'in', displayed_slide_ids),
('partner_id', '=', request.env.user.partner_id.id),
('completed', '=', True)
]).mapped('slide_id').ids
else:
done_slide_ids = []
values['done_slide_ids'] = done_slide_ids
values['slide_promoted'] = request.env['slide.slide'].sudo().search(domain, limit=1, order=order)
values['category_data'] = category_data
@@ -500,7 +538,7 @@ class WebsiteSlides(WebsiteProfile):
}
# JSONRPC
@http.route('/slides/slide/like', type='json', auth="user", website=True)
@http.route('/slides/slide/like', type='json', auth="public", website=True)
def slide_like(self, slide_id, upvote):
if request.website.is_public_user():
return {'error': 'public_user'}
@@ -83,7 +83,7 @@
<field name="promote_strategy">latest</field>
<field name="website_published" eval="True"/>
<field name="enroll">public</field>
<field name="image" type="base64" file="website_slides/static/src/img/channel_demo_flowers.jpg"/>
<field name="image" type="base64" file="website_slides/static/src/img/channel_demo_tree_1.jpg"/>
<field name="description">Knowing which kind of wood to use depending on your application is important. In this course you
will learn the basics of wood characteristics.</field>
</record>
@@ -131,21 +131,20 @@ will learn the basics of wood characteristics.</field>
<field name="sequence">10</field>
</record>
<record id="slide_tag_demo_0" model="slide.tag">
<field name="name">Tag0</field>
<record id="slide_tag_demo_cheatsheet" model="slide.tag">
<field name="name">CheatSheet</field>
</record>
<record id="slide_tag_demo_1" model="slide.tag">
<field name="name">Tag1</field>
<record id="slide_tag_demo_theory" model="slide.tag">
<field name="name">Theory</field>
</record>
<record id="slide_tag_demo_2" model="slide.tag">
<field name="name">Tag2</field>
<record id="slide_tag_demo_exercises" model="slide.tag">
<field name="name">Exercises</field>
</record>
<record id="slide_tag_demo_tools" model="slide.tag">
<field name="name">Tools</field>
</record>
<record id="slide_tag_demo_colorfull" model="slide.tag">
<field name="name">Colorfull</field>
<record id="slide_tag_demo_colorful" model="slide.tag">
<field name="name">Colorful</field>
</record>
<record id="slide_tag_demo_howto" model="slide.tag">
<field name="name">HowTo</field>
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data noupdate="0">
<!-- CHANNEL 0: Basics of Gardening -->
<!-- ================================================== -->
<record id="slide_slide_demo_0_0" model="slide.slide">
<field name="name">Know-How Summary</field>
<field name="datas" type="base64" file="website_slides/static/src/img/presentation.pdf"/>
@@ -10,11 +12,14 @@
<field name="category_id" ref="website_slides.slide_category_demo_0_0"/>
<field name="website_published" eval="True"/>
<field name="is_preview" eval="True"/>
<field name="public_views">40</field>
<field name="public_views">10</field>
<field name="completion_time">2.5</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">A summary of know-how: how and what.</field>
</record>
<!-- CHANNEL 1: Taking care of Trees -->
<!-- ================================================== -->
<record id="slide_slide_demo_1_0" model="slide.slide">
<field name="name">Tree Infographic</field>
<field name="image" type="base64" file="website_slides/static/src/img/slide_demo_tree_infographic_1.jpg"/>
@@ -24,7 +29,8 @@
<field name="website_published" eval="True"/>
<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="completion_time">0.5</field>
<field name="tag_ids" eval="[(4, ref('website_slides.slide_tag_demo_colorful'))]"/>
<field name="description">Just some basics Tree Infographic.</field>
</record>
<record id="slide_slide_demo_1_1" model="slide.slide">
@@ -34,9 +40,9 @@
<field name="channel_id" ref="website_slides.slide_channel_demo_1_gard1"/>
<field name="category_id" ref="website_slides.slide_category_demo_1_0"/>
<field name="website_published" eval="True"/>
<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="is_preview" eval="False"/>
<field name="public_views">5</field>
<field name="tag_ids" eval="[(4, ref('website_slides.slide_tag_demo_colorful'))]"/>
<field name="description">Just some basics Interesting Tree Facts.</field>
</record>
<record id="slide_slide_demo_1_2" model="slide.slide">
@@ -47,8 +53,8 @@
<field name="category_id" ref="website_slides.slide_category_demo_1_0"/>
<field name="website_published" eval="True"/>
<field name="is_preview" eval="False"/>
<field name="public_views">15</field>
<field name="tag_ids" eval="[(5, 0)]"/>
<field name="public_views">10</field>
<field name="tag_ids" eval="[(4, ref('website_slides.slide_tag_demo_colorful')), (4, ref('website_slides.slide_tag_demo_theory'))]"/>
<field name="description">Just some basics Energy Efficiency Facts.</field>
</record>
<record id="slide_slide_demo_1_3" model="slide.slide">
@@ -126,6 +132,71 @@
<field name="description">A summary of know-how: how and what.</field>
</record>
<!-- CHANNEL 2: Trees, Wood and Garden -->
<!-- ================================================== -->
<record id="slide_slide_demo_2_0" model="slide.slide">
<field name="name">Main Trees Categories</field>
<field name="datas" type="base64" file="website_slides/static/src/img/presentation.pdf"/>
<field name="image" type="base64" file="website_slides/static/src/img/slide_demo_tree_img_1.jpg"/>
<field name="slide_type">presentation</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_2_gard2"/>
<field name="category_id" ref="website_slides.slide_category_demo_2_0"/>
<field name="website_published" eval="True"/>
<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">A summary of know-how: how and what.</field>
</record>
<record id="slide_slide_demo_2_1" model="slide.slide">
<field name="name">A Mighty Forest from Ages</field>
<field name="image" type="base64" file="website_slides/static/src/img/slide_demo_tree_img_1.jpg"/>
<field name="slide_type">webpage</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_2_gard2"/>
<field name="category_id" ref="website_slides.slide_category_demo_2_0"/>
<field name="html_content" type="html">
<section class="s_cover parallax bg-black-50 pt16 pb16" data-scroll-background-ratio="0" style="background-image: none;">
<span class="s_parallax_bg oe_img_bg oe_custom_bg" style="background-image: url('/website_slides/static/src/img/slide_demo_tree_img_1.jpg'); background-position: 50% 0;"></span>
<div class="container">
<div class="row s_nb_column_fixed">
<div class="col-lg-12 s_title">
<h1 class="s_title_thin o_default_snippet_text" style="font-size: 62px; text-align: center;">Catchy Headline</h1>
</div>
<div class="col-lg-12 s_text pt16 pb16">
<p class="lead o_default_snippet_text" style="text-align: center;">Write one or two paragraphs describing your product, services or a specific feature.<br/> To be successful your content needs to be useful to your readers.</p>
</div>
<div class="col-lg-12 s_btn text-center pt16 pb16">
<a href="/aboutus" class="btn btn-delta rounded-circle o_default_snippet_text">About us</a>
<a href="/contactus" class="btn btn-primary rounded-circle o_default_snippet_text">Contact us</a>
</div>
</div>
</div>
</section>
<section class="s_text_image pt32 pb32">
<div class="container">
<div class="row align-items-center">
<div class="col-lg-6 pt16 pb16">
<img src="/website_slides/static/src/img/slide_demo_tree_img_2.jpg" class="img img-fluid mx-auto" alt="Odoo • Image and Text"/>
</div>
<div class="col-lg-6 pt16 pb16">
<h2 class="o_default_snippet_text">Section Subtitle</h2>
<p class="o_default_snippet_text">Write one or two paragraphs describing your product or services. <br/>To be successful your content needs to be useful to your readers.</p>
<p class="o_default_snippet_text">Start with the customer – find out what they want and give it to them.</p>
<div class="s_btn text-left pt16 pb16">
<a href="#" class="btn btn-outline-primary o_default_snippet_text">Discover more</a>
</div>
</div>
</div>
</div>
</section></field>
<field name="website_published" eval="True"/>
<field name="is_preview" eval="False"/>
<field name="public_views">0</field>
<field name="tag_ids" eval="[(4, ref('website_slides.slide_tag_demo_theory'))]"/>
<field name="description">Mighty forest just don't appear in a few weeks. Learn how time made our forests mighty and mysterious.</field>
</record>
<!-- CHANNEL 2: Trees, Wood and Garden -->
<!-- ================================================== -->
<record id="slide_slide_demo_5_0" model="slide.slide">
<field name="name">How to find quality wood</field>
<field name="url">https://www.youtube.com/watch?v=5WMqwTnZ-qs</field>
@@ -1,6 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data noupdate="0">
<!-- CHANNEL 0: Basics of Gardening -->
<!-- ================================================== -->
<record id="slide_channel_0_partner_admin" model="slide.channel.partner">
<field name="channel_id" ref="website_slides.channel_public"/>
<field name="partner_id" ref="base.partner_admin"/>
</record>
<record id="slide_slide_0_0_partner_admin" model="slide.slide.partner">
<field name="slide_id" ref="website_slides.slide_slide_demo_0_0"/>
<field name="partner_id" ref="base.partner_admin"/>
<field name="completed" eval="True"/>
</record>
<record id="slide_channel_0_partner_demo" model="slide.channel.partner">
<field name="channel_id" ref="website_slides.channel_public"/>
<field name="partner_id" ref="base.partner_demo"/>
</record>
<record id="slide_slide_0_0_partner_demo" model="slide.slide.partner">
<field name="slide_id" ref="website_slides.slide_slide_demo_0_0"/>
<field name="partner_id" ref="base.partner_demo"/>
</record>
<!-- CHANNEL 1: Taking care of Trees -->
<!-- ================================================== -->
<record id="slide_channel_1_partner_admin" model="slide.channel.partner">
<field name="channel_id" ref="website_slides.slide_channel_demo_1_gard1"/>
<field name="partner_id" ref="base.partner_admin"/>
@@ -59,4 +82,17 @@
<field name="message_id" ref="website_slides.message_channel_1_demo"/>
</record>
<!-- CHANNEL 2: Trees, Wood and Garden -->
<!-- ================================================== -->
<record id="slide_channel_2_partner_admin" model="slide.channel.partner">
<field name="channel_id" ref="website_slides.slide_channel_demo_2_gard2"/>
<field name="partner_id" ref="base.partner_admin"/>
</record>
<record id="slide_slide_2_0_partner_admin" model="slide.slide.partner">
<field name="slide_id" ref="website_slides.slide_slide_demo_2_0"/>
<field name="partner_id" ref="base.partner_admin"/>
<field name="vote">1</field>
<field name="completed" eval="True"/>
</record>
</data></odoo>
+2 -2
View File
@@ -109,8 +109,8 @@ class Slide(models.Model):
completion_time = fields.Float('# Hours', default=1, digits=(10, 4))
# subscribers
partner_ids = fields.Many2many('res.partner', 'slide_slide_partner', 'slide_id', 'partner_id',
string='Subscribers', groups='base.group_website_publisher')
slide_partner_ids = fields.One2many('slide.slide.partner', 'slide_id', string='Subscribers information', groups='base.group_website_publisher')
string='Subscribers', groups='website.group_website_publisher')
slide_partner_ids = fields.One2many('slide.slide.partner', 'slide_id', string='Subscribers information', groups='website.group_website_publisher')
user_membership_id = fields.Many2one(
'slide.slide.partner', string="Subscriber information", compute='_compute_user_membership_id',
help="Subscriber information for the current logged in user")
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

@@ -51,10 +51,10 @@ var JoinChannelButton = Widget.extend({
},
}).then(function (data) {
if (! data.error) {
self.$('a.o_wslides_join_channel_link').text(_t('Joined'));
location.reload();
} else {
if (data.error === 'public_user') {
self._popoverAlert(self.$el, _.str.sprintf(_t('Please <a href="/web/login?redirect=%s">login</a> to join this channel.'), (document.URL)));
self._popoverAlert(self.$el, _.str.sprintf(_t('Please <a href="/web/login?redirect=%s">login</a> to join this course.'), (document.URL)));
} else if (data.error === 'join_done') {
self._popoverAlert(self.$el, _t('You have already joined this channel'));
} else {
@@ -86,8 +86,11 @@ sAnimations.registry.websiteSlidesLike = sAnimations.Class.extend({
* @param {Object} parent
*/
start: function () {
var self = this;
var defs = [this._super.apply(this, arguments)];
defs.push(new LikeButton(this).attachTo($('.o_wslides_like')));
$('.o_wslides_like').each(function () {
defs.push(new LikeButton(self).attachTo($(this)));
});
return $.when.apply($, defs);
},
});
@@ -1,13 +1,7 @@
// FIXME may be useless (check initial code here: https://github.com/odoo/odoo/blob/51be5bbfb38db784cb3406877cc0e8806b1bf55a/addons/website_slides/static/src/less/website_slides.less#L1)
$nav-tabs-border-color: #dddddd !default;
$info: #dddddd !default;
$body-bg: #FFFFFF !default;
$primary: #428bca !default;
$link-color: theme-color('primary') !default;
$panel-footer-bg: #f5f5f5 !default;
$nav-link-hover-bg: #FFFFFF !default;
$navbar-default-link-hover-color: #333 !default;
$light: #f8f8f8 !default;
$MAX-Z-INDEX : 2147483647 !default;
@mixin top-covered-image {
@@ -26,38 +20,39 @@ $MAX-Z-INDEX : 2147483647 !default;
padding: 0px 32px 0px 32px;
}
div .o_wslides_home_nav {
top: -40px;
@include media-breakpoint-up(md) {
font-size: 1rem;
.o_wslides_nav_navbar_right {
margin-left: auto;
padding-left: $spacer;
border-left: 1px solid $border-color;
}
}
}
.o_wslides_wrap {
background-color: desaturate(map-get($grays, "200"), 80%);
.cta-title {
font-size: 1.2em;
}
.o_wslides_course_card {
.card-body {
padding: 0.75em 1em;
}
.card-footer {
padding: 0.75em 1em;
}
border-width: 0 0 3px 0;
}
.o_wslides_lesson_card {
.card-body {
padding: 0.75em 1em;
}
.card-footer {
padding: 0.75em 1em;
}
border-width: 0 0 3px 0;
}
// responsive card-deck like
.row.o_wslides_row > div[class*='col-'] {
display: flex;
flex:1 0 auto;
.card {
width:100%;
}
}
// .row.o_wslides_row > div[class*='col-'] {
// display: flex;
// flex:1 0 auto;
// .card {
// width:100%;
// }
// }
// tools
// ****************************************
@@ -65,7 +60,8 @@ $MAX-Z-INDEX : 2147483647 !default;
.text_small_caps {
font-variant: small-caps;
}
img.o_wslides_img_muted {
.o_wslides_entry_muted {
opacity: 0.5;
}
img.o_wslides_img_center_crop {
@@ -144,13 +140,8 @@ div .o_wslides_home_main {
font-size: 1.2em;
}
}
.o_wslides_home_content_section_title, .o_wslides_home_aside_title {
h3 {
font-size: 1.4em;
}
h4 {
font-size: 1.3em;
}
.o_wprofile_next_rank_card {
background-color: transparent !important;
}
}
@@ -21,6 +21,9 @@
<li class="breadcrumb-item active" t-att-aria-current="'page' and search_tag" t-if="search_tag">
<a t-att-href="'/slides/%s/tag/%s' % (slug(channel), slug(search_tag))"><span t-esc="search_tag.name"/></a>
</li>
<li class="breadcrumb-item active" t-att-aria-current="'page' and search_uncategorized" t-if="search_uncategorized">
<a t-att-href="'/slides/%s?search_uncategorized=1' % (slug(channel))">Uncategorized</a>
</li>
<li class="breadcrumb-item active" t-att-aria-current="'page' and search_slide_type" t-if="search_slide_type">
<a t-att-href="'/slides/%s?slide_type=%s' % (slug(channel), search_slide_type)"><span t-esc="slide_types[search_slide_type]"/></a>
</li>
@@ -123,7 +126,7 @@
</div>
</div>
<div class="row o_wslides_course_main">
<div class="col-lg-12 o_wslides_container o_wslides_content_containerUNFUCKME">
<div class="col-lg-12 o_wslides_container">
<div class="row o_wslides_course_content_main">
<!-- Channel presentation, aka general information + CTAs -->
<div class="col-md-4 col-xs-12 o_wslides_course_content_aside">
@@ -159,8 +162,8 @@
<span class="col-5">Members</span>
<span class="col" t-esc="channel.members_count"/>
</div>
<div class="row mt8"><div class="col"><hr/></div></div>
<t t-if="channel.channel_type == 'documentation'">
<div class="row mt8"><div class="col"><hr/></div></div>
<t t-set="slide_type_keys" t-value="slide_types.keys()"/>
<t t-foreach="slide_type_keys" t-as="slide_type_key">
<div class="row mt8 align-items-center">
@@ -220,7 +223,7 @@
</div>
</div>
</div>
<div t-if="channel.can_upload" class="row justify-content-center mt8 mb8">
<div t-if="channel.can_upload" class="row justify-content-center mt8">
<a role="button" class="btn btn-primary oe_slide_js_upload col-10" title="Upload Presentation"
aria-label="Upload Presentation" href="#"
t-att-data-channel-id="channel.id"
@@ -313,8 +316,8 @@
<div t-if="not search" class="col-md-8 col-xs-12 mt16 o_wslides_channel_content_promoted">
<div class="row align-items-center">
<div class="col">
<h3> Featured lesson</h3>
<hr/>
<h5 class="m-0"> Featured lesson</h5>
<hr class="mt-2"/>
</div>
</div>
<div class="row" t-if="slide_promoted">
@@ -351,11 +354,14 @@
<t t-foreach="category_data" t-as="category">
<div class="row mt16 align-items-center">
<div class="col">
<a t-att-href="'/slides/%s/category/%s' % (slug(channel), category['id'])" class="float-right">
View all
</a>
<h3><t t-esc="category['name']"/></h3>
<hr/>
<t t-if="category['id']">
<a t-att-href="'/slides/%s/category/%s' % (slug(channel), category['slug_name'])" class="float-right">View all</a>
</t>
<t t-else="">
<a t-att-href="'/slides/%s?uncategorized=1' % (slug(channel))" class="float-right">View all</a>
</t>
<h5 class="m-0"><t t-esc="category['name']"/></h5>
<hr class="mt-2"/>
</div>
</div>
<t t-call="website_slides.course_lessons_grid">
@@ -370,56 +376,59 @@
<!-- Embedded template: display a list of slides in a grid view -->
<template id='course_lessons_grid' name="Course: Lessons Grid View">
<div class="row o_wslides_row mt8">
<div class="col-lg-3 col-md-6 col-12" t-foreach="slides" t-as="slide">
<div class="card o_wslides_lesson_card">
<img class="card-img-top img-fuild" style="max-height: 160px;"
t-att-src="'/slides/slide/%s/get_image?field=image_large' % slide.id"
t-att-alt="slide.name"/>
<div class="card-body">
<h5 class="card-title" t-att-title="slide.name">
<t t-if="slide.is_preview or channel.is_member or is_slides_publisher">
<a t-attf-href="/slides/slide/#{slug(slide)}" t-esc="slide.name"/>
</t>
<t t-else="">
<span class="text-muted" t-esc="slide.name"/>
</t>
</h5>
<h6 class="card-subtitle mb-2 text-muted">
<t t-if="slide.is_preview">
<span class="badge badge-info">Free preview</span>
</t>
<t t-if="not slide.is_published and user.has_group('website.group_website_publisher')">
<span class="badge badge-danger">Unpublished</span>
</t>
</h6>
<div class="card-text">
<div class="text-muted oe_no_empty" t-field="slide.description"/>
<div t-if="slide.tag_ids">
<t t-foreach="slide.tag_ids" t-as="tag">
<a t-att-href="'/slides/%s/tag/%s' % (slug(slide.channel_id), slug(tag))" class="badge badge-secondary">
<t t-esc="tag.name"/>
</a>
</t>
</div>
</div>
<div class="row o_wslides_row mx-n2 mt8">
<t t-foreach="slides" t-as="slide">
<div class="col-md-3 col-sm-6 px-2 col-xs-12 d-flex flex-grow-1" t-call="website_slides.lesson_card"/>
</t>
</div>
</template>
<template id='lesson_card' name="Lesson Card">
<div class="card w-100 o_wslides_lesson_card mb-4">
<t t-set="lesson_image" t-value="'/slides/slide/%s/get_image?field=image_large' % slide.id"/>
<t t-set="can_access" t-value="slide.is_preview or channel.is_member or is_slides_publisher"/>
<a t-if="can_access" t-attf-href="/slides/slide/#{slug(slide)}" t-title="slide.name">
<div class="card-img-top" t-attf-style="padding-top: 50%; background-image: url(#{lesson_image}); background-size: cover; background-position:center"/>
</a>
<t t-else="">
<div class="card-img-top" t-attf-style="padding-top: 50%; background-image: url(#{lesson_image}); background-size: cover; background-position:center"/>
</t>
<div class="card-body">
<a t-if="can_access" class="card-title h5" t-attf-href="/slides/slide/#{slug(slide)}" t-esc="slide.name"/>
<span t-else="" class="card-title h5 text-muted" t-esc="slide.name"/>
<div class="card-subtitle mb-2 text-muted">
<t t-if="slide.is_preview">
<span class="badge badge-info">Free preview</span>
</t>
<t t-if="not slide.is_published and user.has_group('website.group_website_publisher')">
<span class="badge badge-danger">Unpublished</span>
</t>
</div>
<div class="card-text mt-1">
<div class="font-weight-light oe_no_empty" t-field="slide.description"/>
<div t-if="slide.tag_ids" class="border-top mt-2 pt-1">
<t t-foreach="slide.tag_ids" t-as="tag">
<a t-att-href="'/slides/%s/tag/%s' % (slug(slide.channel_id), slug(tag))" class="badge badge-light" t-esc="tag.name"/>
</t>
</div>
<div class="card-footer">
<!-- CHECKME: like only if logged -->
<div class="float-right text-right o_wslides_like">
<span class="o_wslides_like_up" tabindex="0" data-toggle="popover" t-att-data-slide-id="slide.id">
<i class="fa fa-thumbs-up fa-1x" role="img" aria-label="Likes" title="Likes"></i>
<span t-esc="slide.likes"/>
</span>
<span class="o_wslides_like_down" tabindex="0" data-toggle="popover" t-att-data-slide-id="slide.id">
<i class="fa fa-thumbs-down fa-1x" role="img" aria-label="Dislikes" title="Dislikes"></i>
<span t-esc="slide.dislikes"/>
</span>
</div>
<small class="text-muted" t-field="slide.completion_time" t-options='{"widget": "float_time"}'/>
<!-- <t t-esc="slide.total_views"/> Views
<timeago class="timeago" t-att-datetime="slide.create_date"></timeago> -->
</div>
</div>
<div class="card-footer border-top-0 text-600">
<div class="d-flex align-items-center small">
<span class="font-weight-bold mr-auto" t-field="slide.completion_time" t-options='{"widget": "float_time"}'/>
<div class="o_wslides_like mr-2">
<span class="o_wslides_like_up" tabindex="0" data-toggle="popover" t-att-data-slide-id="slide.id">
<i class="fa fa-thumbs-up fa-1x" role="img" aria-label="Likes" title="Likes"></i>
<span t-esc="slide.likes"/>
</span>
<span class="o_wslides_like_down" tabindex="0" data-toggle="popover" t-att-data-slide-id="slide.id">
<i class="fa fa-thumbs-down fa-1x" role="img" aria-label="Dislikes" title="Dislikes"></i>
<span t-esc="slide.dislikes"/>
</span>
</div>
<t t-if="channel.is_member and slide.id in done_slide_ids">
<span class="badge badge-pill badge-success"><i class="fa fa-check"/> Completed</span>
</t>
</div>
</div>
</div>
@@ -5,16 +5,18 @@
<template id='courses_home' name="Odoo Courses Homepage">
<t t-call="website.layout">
<div class="wrap o_wslides_wrap">
<section class="s_banner bg-900" style="background-image: url(&quot;/website_slides/static/src/img/channel_banner_mountain.jpg&quot;); background-size: cover; background-position: 65% 55%">
<div class="container py-5">
<h1 class="display-3 mb-0">Reach new heights</h1>
<h2 class="mb-4">Start your online course today !</h2>
<div class="row mt-1 mb-3">
<div class="col-lg-4">
<p>Skill up and have an impact! Your business career starts here. Time to start a course.</p>
</div>
<div class="col-lg-3">
<a href="/slides/all" class="btn btn-primary btn-lg">Pick a course</a>
<section class="s_banner overflow-hidden bg-900" style="background-image: url(&quot;/website_slides/static/src/img/banner_default.svg&quot;); background-size: cover; background-position: 55% 65%">
<div class="container align-items-center d-flex mb-5 mt-lg-5 pt-lg-4 pb-lg-1">
<div>
<h1 class="display-3 mb-0">Reach new heights</h1>
<h2 class="mb-4">Start your online course today !</h2>
<div class="row mt-1 mb-3">
<div class="col-lg-4">
<p>Skill up and have an impact! Your business career starts here. Time to start a course.</p>
</div>
<div class="col-lg-3">
<a href="slides/all" class="btn btn-primary btn-lg">Pick a course</a>
</div>
</div>
</div>
</div>
@@ -31,61 +33,65 @@
<a class="nav-link nav-link mr-md-2 o_wslides_home_all_slides" href="/slides/all"><i class="fa fa-graduation-cap mr-1"/>All courses</a>
</div>
<div class="navbar-nav o_wslides_nav_navbar_right">
<a class="nav-link nav-link" href="/slides/all"><i class="fa fa-file mr-1"/>My courses</a>
<t t-if="is_public_user">
<a class="nav-link nav-link" href="/web/login?redirect=/slides/all?my=1"><i class="fa fa-file mr-1"/>My courses</a>
</t><t t-else="">
<a class="nav-link nav-link" href="/slides/all?my=1"><i class="fa fa-file mr-1"/>My courses</a>
</t>
</div>
</div>
</nav>
</div>
<div class="container o_wslides_home_main">
<div class="row">
<div class="col-md-9">
<div class="col-lg-9 pr-lg-5">
<t t-if="channels_my">
<t t-set="void_count" t-value="3 - len(channels_my[:3])"/>
<div class="o_wslides_home_content_section">
<div class="row mt16 o_wslides_home_content_section_title align-items-center">
<div class="o_wslides_home_content_section mb-5">
<div class="row o_wslides_home_content_section_title align-items-center">
<div class="col">
<a href="slides/all" class="float-right">View all</a>
<h3><i class="fa fa-trophy"/> Complete latest courses</h3>
<hr/>
<h5 class="m-0">Complete latest courses</h5>
<hr class="mt-2"/>
</div>
</div>
<div class="row o_wslides_row mt8">
<div class="row o_wslides_row mx-n2 mt8">
<t t-foreach="channels_my[:3]" t-as="channel">
<div class="col-md-4 col-sm-6 col-xs-12" t-call="website_slides.course_card"/>
<div class="col-md-4 col-sm-6 px-2 col-xs-12 d-flex flex-grow-1" t-call="website_slides.course_card"/>
</t>
</div>
</div>
</t>
<div class="o_wslides_home_content_section">
<div class="row mt16 o_wslides_home_content_section_title align-items-center">
<div class="o_wslides_home_content_section mb-5">
<div class="row o_wslides_home_content_section_title align-items-center">
<div class="col">
<a href="slides/all" class="float-right">View all</a>
<h3><i class="fa fa-fire"/> Most popular courses</h3>
<hr/>
<h5 class="m-0">Most popular courses</h5>
<hr class="mt-2"/>
</div>
</div>
<div class="row o_wslides_row mt8">
<div class="row o_wslides_row mx-n2 mt8">
<t t-foreach="channels_popular[:3]" t-as="channel">
<div class="col-md-4 col-sm-6 col-xs-12" t-call="website_slides.course_card"/>
<div class="col-md-4 col-sm-6 px-2 col-xs-12 d-flex flex-grow-1" t-call="website_slides.course_card"/>
</t>
</div>
</div>
<div class="o_wslides_home_content_section">
<div class="row mt16 o_wslides_home_content_section_title align-items-center">
<div class="o_wslides_home_content_section mb-5">
<div class="row o_wslides_home_content_section_title align-items-center">
<div class="col">
<a href="slides/all" class="float-right">View all</a>
<h3><i class="fa fa-plus-square"/> Newest courses</h3>
<hr/>
<h5 class="m-0">Newest courses</h5>
<hr class="mt-2"/>
</div>
</div>
<div class="row o_wslides_row mt8">
<div class="row o_wslides_row mx-n2 mt8">
<t t-foreach="channels_newest[:3]" t-as="channel">
<div class="col-md-4 col-sm-6 col-xs-12" t-call="website_slides.course_card"/>
<div class="col-md-4 col-sm-6 px-2 col-xs-12 d-flex flex-grow-1" t-call="website_slides.course_card"/>
</t>
</div>
</div>
</div>
<div class="col-md-3">
<div class="col-lg-3">
<t t-if="is_public_user">
<t t-call="website_slides.slides_home_achievements_small"/>
<t t-call="website_slides.slides_home_users_small"/>
@@ -120,9 +126,9 @@
<template id='courses_all' name="Odoo All Courses">
<t t-call="website.layout">
<div class="wrap o_wslides_wrap">
<section class="s_banner bg-900" style="background-image: url(&quot;/website_slides/static/src/img/channel_banner_mountain_all.jpg&quot;); background-size: cover; background-position: 65% 45%">
<section class="s_banner bg-900" style="background-image: url(&quot;/website_slides/static/src/img/banner_default_all.svg&quot;); background-size: cover; background-position: 80% 20%">
<div class="container py-5">
<h1 class="display-3 mb-0">All Courses</h1>
<h1 class="display-3 mb-0"><t t-if="search_my">My Courses</t><t t-else="">All Courses</t></h1>
<h3 class="mb-4">Start your online course today !</h3>
</div>
</section>
@@ -159,10 +165,18 @@
</t>
<!-- karma / profile -->
<li class="nav-item ml-auto" t-if="not is_public_user">
<a t-att-href="'/profile/user/%s' % user.id" class="nav-link mr-2">
<a t-att-href="'/profile/user/%s' % user.id" class="nav-link mr-2"
role="button" title="My Profile">
<i class="fa fa-diamond"/> <t t-esc="user_id.karma"/>
</a>
</li>
<!-- clear filtering -->
<li class="nav-item" t-if="search_slide_type or search_my or search_tags or search_channel_tag_id">
<a t-att-href="'/slides/all?%s' % keep_query('debug')" class="nav-link mr-2"
role="button" title="Clear filters">
<i class="fa fa-eraser"/>
</a>
</li>
</ul>
<!-- search box -->
<form method="GET" class="form-inline o_wslides_nav_navbar_right">
@@ -180,31 +194,17 @@
</div>
</nav>
</div>
<div class="container o_wslides_home_main">
<t t-if="search_term and not channels_layouted">
<div class="row">
<div class="col">
<p class="alert alert-info">No channel was found matching your search <t t-esc="search_term"/>.</p>
</div>
</div>
</t>
<t t-foreach="channels_layouted" t-as="channels_row">
<t t-set="missing" t-value="0"/>
<div class="card-deck mb32">
<t t-foreach="channels_row" t-as="channel">
<t t-if="channel">
<t t-call="website_slides.course_card"/>
</t>
<t t-else="">
<t t-set="missing" t-value="missing + 1"/>
</t>
</t>
<t t-if="missing">
<div t-att-class="'col-%s' % (missing * 3)"/>
</t>
</div>
</t>
<div class="container o_wslides_home_main pb-5">
<div t-if="search_term and not channels" class="alert alert-info mb-5">
No channel was found matching your search <code><t t-esc="search_term"/></code>.
</div>
<div t-else="" class="row mx-n2">
<t t-foreach="channels" t-as="channel">
<div class="col-12 col-sm-6 col-md-4 col-lg-3 px-2 d-flex flex-grow-1" t-call="website_slides.course_card"/>
</t>
</div>
</div>
<section class="s_banner bg-900" style="background-image: url(&quot;/website/static/src/img/backgrounds/people.jpg&quot;); background-size: cover; background-position: 65% 55%">
<div class="container py-5">
<h1 class="display-3 mb-0">Meet the people who transformed their company</h1>
@@ -224,47 +224,32 @@
</template>
<template id='course_card' name="Course Card">
<div class="card o_wslides_course_card">
<img class="card-img-top img-fuild o_wslides_img_center_crop"
style="max-height: 160px;"
t-att-src="'/web/image/slide.channel/%s/image_large' % channel.id"
t-att-alt="channel.name"/>
<div class="card w-100 o_wslides_course_card mb-4">
<t t-set="course_image" t-value="'/web/image/slide.channel/%s/image_large' % channel.id"/>
<a t-attf-href="/slides/#{slug(channel)}" t-title="channel.name">
<div class="card-img-top" t-attf-style="padding-top: 50%; background-image: url(#{course_image}); background-size: cover; background-position:center"/>
</a>
<div class="card-body">
<h5 class="card-title" t-att-title="channel.name">
<a t-attf-href="/slides/#{slug(channel)}" t-esc="channel.name"/>
</h5>
<div class="card-text">
<div class="text-muted oe_no_empty" t-field="channel.description"/>
<div t-if="channel.tag_ids">
<a class="card-title h5" t-attf-href="/slides/#{slug(channel)}" t-esc="channel.name"/>
<div class="card-text mt-1">
<div class="font-weight-light oe_no_empty" t-field="channel.description"/>
<div t-if="channel.tag_ids" class="border-top mt-2 pt-1">
<t t-foreach="channel.tag_ids" t-as="tag">
<a t-att-href="'/slides/all?channel_tag_id=%s' % tag.id" class="badge badge-secondary">
<t t-esc="tag.name"/>
</a>
<a t-att-href="'/slides/all?channel_tag_id=%s' % tag.id" class="badge badge-light" t-esc="tag.name"/>
</t>
</div>
</div>
</div>
<div class="card-footer">
<div class="row justify-content-between align-items-center">
<small class="text-muted col-3" t-field="channel.total_time" t-options='{"widget": "float_time"}'/>
<div class="card-footer border-top-0 text-600">
<div class="d-flex justify-content-between align-items-center small">
<span class="font-weight-bold" t-field="channel.total_time" t-options='{"widget": "float_time"}'/>
<t t-if="channel.is_member and channel.completed">
<div class="col-5">
<span class="badge badge-pill badge-success pull-right"><i class="fa fa-check"/> Completed</span>
</div>
</t>
<t t-elif="channel.is_member">
<div class="col">
<div class="progress">
<div class="progress-bar" role="progressbar" t-att-aria-valuenow="channel.completion" aria-valuemin="0" aria-valuemax="100" t-attf-style="width:#{channel.completion}%;">
</div>
</div>
</div>
</t>
<t t-else="">
<div class="col-5">
<small class="text-muted pull-right"><span t-esc="channel.total_slides"/> steps</small>
</div>
<span class="badge badge-pill badge-success pull-right"><i class="fa fa-check"/> Completed</span>
</t>
<div t-elif="channel.is_member" class="progress w-50">
<div class="progress-bar" role="progressbar" t-att-aria-valuenow="channel.completion" aria-valuemin="0" aria-valuemax="100" t-attf-style="width:#{channel.completion}%;"/>
</div>
<t t-else=""><span class="font-weight-bold" t-esc="channel.total_slides"/> steps</t>
</div>
</div>
</div>
@@ -272,13 +257,11 @@
<template id='slides_home_achievements_small' name="Latest Achievements">
<div class="o_wslides_home_aside">
<div class="row mt16 o_wslides_home_aside_title">
<div class="row o_wslides_home_aside_title">
<div class="col">
<a href="slides" class="text-muted float-right">
<small class="float-right">View all</small>
</a>
<h3>Latest achievements</h3>
<hr/>
<a href="/slides" class="float-right">View all</a>
<h5 class="m-0">Latest achievements</h5>
<hr class="mt-2"/>
</div>
</div>
<div class="row">
@@ -309,13 +292,11 @@
<template id='slides_home_users_small' name="Latest Users">
<div class="o_wslides_home_aside">
<div class="row mt16 o_wslides_home_aside_title">
<div class="row o_wslides_home_aside_title">
<div class="col">
<a href="slides" class="text-muted float-right">
<small class="float-right">View all</small>
</a>
<h3>Heroes of the week</h3>
<hr/>
<a href="/profile/users" class="float-right">View all</a>
<h5 class="m-0">Heroes of the week</h5>
<hr class="mt-2"/>
</div>
</div>
<div class="row">
@@ -352,41 +333,47 @@
<template id='slides_home_user_profile_small' name="User Profile">
<div class="o_wslides_home_aside">
<div class="row mt16 o_wslides_home_aside_title align-items-center">
<div class="col-12">
<t t-call="website_slides.slides_misc_user_image"/>
<a t-att-href="'/profile/user/%s' % user.id" class="float-right">Edit</a>
<h4 t-esc="user.name" class="font-weight-bold"/>
<hr/>
<div class="o_wslides_home_aside_title">
<div class="d-flex align-items-center">
<t t-call="website_slides.slides_misc_user_image">
<t t-set="img_class" t-value="'rounded-circle mr-1'"/>
<t t-set="img_style" t-value="'height: 22px'"/>
</t>
<h5 t-esc="user.name" class="d-flex flex-grow-1 mb-0"/>
<a t-att-href="'/profile/user/%s' % user.id">Edit</a>
</div>
<div class="col-12 mt8">
<span class="font-weight-bold text-muted">Current rank:</span>
<a href="/profile/ranks" class="float-right" t-field="user.rank_id"/>
<t t-call="website_profile.profile_next_rank_card"/>
<div t-field="user.next_rank_id.description_motivational"/>
</div>
<hr class="mt-2"/>
<div class="mt8">
<div class="d-flex align-items-center">
<span class="font-weight-bold text-muted mr-2">Current rank:</span>
<img t-attf-src="/web/image/gamification.karma.rank/#{user.rank_id.id}/image_small" height="16" alt="" class="mr-1"/>
<a href="/profile/ranks" t-field="user.rank_id"/>
</div>
<div class="font-weight-bold text-muted mt-1">Next rank:</div>
<t t-call="website_profile.profile_next_rank_card">
<t t-set="bg_class">bg-200</t>
<t t-set="img_max_width">50%</t>
</t>
<div t-field="user.next_rank_id.description_motivational"/>
</div>
</div>
</template>
<template id='slides_home_user_achievements_small' name="User Achievements">
<div class="o_wslides_home_aside mt16">
<div class="o_wslides_home_aside mb-2">
<t t-foreach="challenges" t-as="challenge">
<t t-set="challenge_done" t-value="challenge in challenges_done if challenges_done else False"/>
<div class="row no-gutters mt8 align-items-center">
<div class="col-3">
<img t-att-class="'rounded-circle float-left %s' % 'o_wslides_img_muted' if not challenge_done else ''"
style="max-height: 36px;"
t-att-src="'/web/image/gamification.badge/%s/image_medium' % challenge.reward_id.id"
t-att-alt="challenge.reward_id.name"/>
</div>
<div class="col-7">
<span t-att-class="'font-weight-bold text_small_caps %s' % 'text-muted' if not challenge_done else ''" t-esc="challenge.reward_id.name"/><br/>
<div t-attf-class="d-flex mt-3 align-items-center #{'o_wslides_entry_muted' if not challenge_done else ''}">
<img class="mr-2"
style="max-height: 36px;"
t-att-src="'/web/image/gamification.badge/%s/image_medium' % challenge.reward_id.id"
t-att-alt="challenge.reward_id.name"/>
<div class="flex-grow-1">
<b class="text_small_caps" t-esc="challenge.reward_id.name"/><br/>
<span class="text-muted" t-esc="challenge.reward_id.description"/>
</div>
<div class="col-2" t-if="challenge_done">
<i class="fa fa-check float-right" aria-label="Done" title="Done" role="img"></i>
</div>
<i t-if="challenge_done" class="fa fa-check" aria-label="Done" title="Done" role="img"></i>
</div>
</t>
</div>
@@ -399,13 +386,4 @@
t-att-alt="user.name"/>
</template>
<template id='slides_misc_float_time' name="Float Time">
<t t-set="hours" t-value="int(value)"/>
<t t-set="minuts" t-value="int(60.0 * (value - int(value)))"/>
<span t-esc="hours"/> hr
<t t-if="minuts">
<span t-esc="minuts"/> mins
</t>
</template>
</data></odoo>