[IMP] website_slides: new homepage and main channel page

This commit proposes a new homepage for slides module evolving towards
an eLearning platform. It includes

* a new main page displaying top courses / channels. It displays 3 most
  popular and newest channels as well as its ongoing courses (if logged).
  Achievements and karma update done by eLearning community allow to give
  a gamification look and feel;
* an 'All' page displaying all courses / channels. Tag groups and tags
  allow to search / filter displayed courses;
* a new pimped and revamped display for main course / channel page;

For more details about specifications, send an email to aware people that
will be able to send you mockups. Or see the related task. Thanks to
@qmo-odoo who helped developing this homepage based on its work.

This commit is linked to task ID 1936153 and PR #30770.

Co-Authored-By: qmo-odoo <qmo@odoo.com>
Co-Authored-By: Thibault Delavallee <tde@odoo.com>
This commit is contained in:
Thibault Delavallée
2019-02-15 09:58:26 +00:00
co-authored by qmo-odoo
parent c437832e55
commit 2cfacfc632
6 changed files with 1179 additions and 407 deletions
+1
View File
@@ -25,6 +25,7 @@ Share and Publish Videos, Presentations and Documents'
'data': [
'views/assets.xml',
'views/res_config_settings_views.xml',
'views/website_slides_templates_homepage.xml',
'views/website_slides_templates.xml',
'views/website_slides_embed_templates.xml',
'views/slide_slide_views.xml',
+178 -70
View File
@@ -2,6 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import base64
import itertools
import logging
import werkzeug
@@ -21,10 +22,10 @@ _logger = logging.getLogger(__name__)
class WebsiteSlides(WebsiteProfile):
_slides_per_page = 12
_slides_per_list = 20
_order_by_criterion = {
'date': 'date_published desc',
_channel_order_by_criterion = {
'vote': 'total_votes desc',
'view': 'total_views desc',
'vote': 'likes desc',
'date': 'create_date desc',
}
def sitemap_slide(env, rule, qs):
@@ -47,13 +48,6 @@ class WebsiteSlides(WebsiteProfile):
slide.action_set_viewed()
return True
def _prepare_user_values(self, **kwargs):
values = super(WebsiteSlides, self)._prepare_user_values(**kwargs)
channel = self._get_channels(**kwargs)
if channel:
values['channel'] = channel
return values
def _get_slide_detail(self, slide):
most_viewed_slides = slide.get_most_viewed_slides(self._slides_per_list)
related_slides = slide.get_related_slides(self._slides_per_list)
@@ -66,57 +60,144 @@ class WebsiteSlides(WebsiteProfile):
'comments': slide.website_message_ids or [],
}
def _extract_channel_tag_search(self, **post):
tags = request.env['slide.channel.tag']
for key in (_key for _key in post if _key.startswith('channel_tag_group_id_')):
group_id, tag_id = False, False
try:
group_id = int(key.lstrip('channel_tag_group_id_'))
tag_id = int(post[key])
except:
pass
else:
search_tag = request.env['slide.channel.tag'].search([('id', '=', tag_id), ('group_id', '=', group_id)]).exists()
if search_tag:
tags |= search_tag
return tags
def _build_channel_domain(self, base_domain, **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)
domain = base_domain
if search_term:
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:
domain = expression.AND([domain, [('tag_ids', 'in', [channel_tag_id])]])
elif tags:
domain = expression.AND([domain, [('tag_ids', 'in', tags.ids)]])
return domain
# --------------------------------------------------
# MAIN / SEARCH
# --------------------------------------------------
@http.route('/slides', type='http', auth="public", website=True)
def slides_index(self, *args, **post):
""" Returns a list of available channels: if only one is available,
redirects directly to its slides
"""
def slides_channel_home(self, **post):
""" Home page for eLearning platform. Is mainly a container page, does not allow search / filter. """
domain = request.website.website_domain()
# search bar
search_term = post.get('search')
if search_term:
domain = expression.AND([domain, ['|', ('name', 'ilike', search_term), ('description', 'ilike', search_term)]])
channels_all = request.env['slide.channel'].search(domain)
if not request.env.user._is_public():
channels_my = channels_all.filtered(lambda channel: channel.completion > 0).sorted('completion', reverse=True)[:3]
else:
channels_my = request.env['slide.channel']
channels_popular = channels_all.sorted('total_votes', reverse=True)[:3]
channels_newest = channels_all.sorted('create_date', reverse=True)[:3]
channels = request.env['slide.channel'].search(domain, order='sequence, id')
if not channels:
return request.render("website_slides.channel_not_found", {'search_term': search_term})
elif len(channels) == 1 and not search_term: # don't auto redirect to only result when searching
return request.redirect("/slides/%s" % channels.id)
return request.render('website_slides.channels', {
'channels': channels,
# fetch 'latests achievements' for non logged people
if request.env.user._is_public():
achievements = request.env['gamification.badge.user'].sudo().search([], limit=5)
challenges = None
challenges_done = None
else:
achievements = None
challenges = request.env['gamification.challenge'].sudo().search([('category', '=', 'slides')], order='id asc', limit=5)
challenges_done = request.env['gamification.badge.user'].sudo().search([
('challenge_id', 'in', challenges.ids),
('user_id', '=', request.env.user.id)
]).mapped('challenge_id')
# fetch 'heroes of the week' for non logged people
if request.env.user._is_public():
users = request.env['res.users'].sudo().search([
('karma', '>', 0),
('website_published', '=', True)], limit=5, order='create_date desc')
else:
users = None
return request.render('website_slides.courses_home', {
'user': request.env.user,
'is_public_user': request.website.is_public_user(),
'search_term': search_term,
'channels_my': channels_my,
'channels_popular': channels_popular,
'channels_newest': channels_newest,
'achievements': achievements,
'users': users,
'challenges': challenges,
'challenges_done': challenges_done,
})
@http.route('/slides/all', type='http', auth="public", website=True)
def slides_channel_all(self, **post):
""" Home page displaying a list of courses displayed according to some
criterion and search terms.
: 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;
"""
domain = request.website.website_domain()
domain = self._build_channel_domain(domain, **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))
tag_groups = request.env['slide.channel.tag.group'].search(['&', ('tag_ids', '!=', False), ('website_published', '=', True)])
search_tags = self._extract_channel_tag_search(**post)
return request.render('website_slides.courses_all', {
'user': request.env.user,
'is_public_user': request.website.is_public_user(),
'channels_layouted': channels_layouted,
'tag_groups': tag_groups,
'search_term': post.get('search'),
'search_tags': search_tags,
})
def _prepare_additional_channel_values(self, values, **kwargs):
return values
@http.route([
'''/slides/<model("slide.channel"):channel>''',
'''/slides/<model("slide.channel"):channel>/page/<int:page>''',
'''/slides/<model("slide.channel"):channel>/<string:slide_type>''',
'''/slides/<model("slide.channel"):channel>/<string:slide_type>/page/<int:page>''',
'''/slides/<model("slide.channel"):channel>/tag/<model("slide.tag"):tag>''',
'''/slides/<model("slide.channel"):channel>/tag/<model("slide.tag"):tag>/page/<int:page>''',
'''/slides/<model("slide.channel"):channel>/category/<model("slide.category"):category>''',
'''/slides/<model("slide.channel"):channel>/category/<model("slide.category"):category>/page/<int:page>''',
'''/slides/<model("slide.channel"):channel>/category/<model("slide.category"):category>/<string:slide_type>''',
'''/slides/<model("slide.channel"):channel>/category/<model("slide.category"):category>/<string:slide_type>/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='creation', search=None, **kw):
'/slides/<model("slide.channel"):channel>',
'/slides/<model("slide.channel"):channel>/page/<int:page>',
'/slides/<model("slide.channel"):channel>/tag/<model("slide.tag"):tag>',
'/slides/<model("slide.channel"):channel>/tag/<model("slide.tag"):tag>/page/<int:page>',
'/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):
if not channel.can_access_from_current_website():
raise werkzeug.exceptions.NotFound()
user = request.env.user
Slide = request.env['slide.slide']
domain = [('channel_id', '=', channel.id)]
if not request.env.user.has_group('website.group_website_publisher'):
domain = expression.AND([
domain,
['&', ('website_published', '=', True), ('channel_id.website_published', '=', True)]
])
pager_url = "/slides/%s" % (channel.id)
pager_args = {}
slide_types = dict(request.env['slide.slide']._fields['slide_type']._description_selection(request.env))
if search:
domain += [
@@ -135,35 +216,42 @@ class WebsiteSlides(WebsiteProfile):
pager_url += "/tag/%s" % tag.id
if slide_type:
domain += [('slide_type', '=', slide_type)]
pager_url += "/%s" % slide_type
pager_url += "?slide_type=%s" % slide_type
if not sorting or sorting not in self._order_by_criterion:
sorting = 'date'
order = self._order_by_criterion[sorting]
pager_args['sorting'] = sorting
# sorting criterion
actual_sorting = sorting if sorting and sorting in request.env['slide.slide']._order_by_strategy else channel.promote_strategy
order = request.env['slide.slide']._order_by_strategy[actual_sorting]
pager_args['sorting'] = actual_sorting
pager_count = Slide.search_count(domain)
pager_count = request.env['slide.slide'].sudo().search_count(domain)
pager = request.website.pager(url=pager_url, total=pager_count, page=page,
step=self._slides_per_page, scope=self._slides_per_page,
url_args=pager_args)
slides = Slide.search(domain, limit=self._slides_per_page, offset=pager['offset'], order=order)
values = {
'channel': channel,
'category': category,
'slides': slides,
'tag': tag,
'slide_type': slide_type,
'sorting': sorting,
# search
'search_category': category,
'search_tag': tag,
'search_slide_type': slide_type,
'slide_types': slide_types,
'sorting': actual_sorting,
'search': search,
'user': user,
'pager': pager,
'is_public_user': request.website.is_public_user(),
# chatter
'rating_avg': channel.rating_avg,
'rating_count': channel.rating_count,
# display data
'user': request.env.user,
'pager': pager,
'is_public_user': request.website.is_public_user(),
}
if not request.env.user._is_public():
last_message_values = request.env['mail.message'].search([('model', '=', channel._name), ('res_id', '=', channel.id), ('author_id', '=', user.partner_id.id), ('website_published', '=', True)], order='write_date DESC', limit=1).read(['body', 'rating_value'])
last_message_values = request.env['mail.message'].search([
('model', '=', channel._name),
('res_id', '=', channel.id),
('author_id', '=', request.env.user.partner_id.id),
('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 {}
values.update({
'message_post_hash': channel._sign_token(request.env.user.partner_id.id),
@@ -174,21 +262,32 @@ class WebsiteSlides(WebsiteProfile):
})
# Display uncategorized slides
if not slide_type and not category:
category_datas = []
for category in Slide.read_group(domain, ['category_id'], ['category_id']):
# 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:
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'))
category_datas.append({
category_data.append({
'id': category_id,
'name': name,
'total_slides': category['category_id_count'],
'slides': Slide.search(category['__domain'], limit=4, offset=0, order=order)
'slides': request.env['slide.slide'].sudo().search(category['__domain'], limit=4, offset=0, order=order)
})
values.update({
'category_datas': category_datas,
})
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,
'total_slides': len(slides_sudo),
'slides': slides_sudo,
}]
values['slide_promoted'] = request.env['slide.slide'].sudo().search(domain, limit=1, order=order)
values['category_data'] = category_data
return request.render('website_slides.home', values)
values = self._prepare_additional_channel_values(values, **kw)
return request.render('website_slides.course_main', values)
@http.route(['/slides/channel/add'], type='http', auth='user', methods=['POST'], website=True)
def slide_channel_create(self, *args, **kw):
@@ -411,8 +510,17 @@ class WebsiteSlides(WebsiteProfile):
# if the slide can be embedded, and properly display the error message.
return request.render('website_slides.embed_slide_forbidden', {})
# Profile
# ---------------------------------------------------
# --------------------------------------------------
# PROFILE
# --------------------------------------------------
def _prepare_user_values(self, **kwargs):
values = super(WebsiteSlides, self)._prepare_user_values(**kwargs)
channel = self._get_channels(**kwargs)
if channel:
values['channel'] = channel
return values
def _get_channels(self, **kwargs):
channels = []
if kwargs.get('channel'):
Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

@@ -15,6 +15,266 @@ $MAX-Z-INDEX : 2147483647 !default;
object-position: top;
}
// Common to new slides pages
// **************************************************
.o_wslides_wrap {
.text_small_caps {
font-variant: small-caps;
}
.image-muted {
opacity: 0.5;
}
.cta-title {
font-size: 1.2em;
}
}
// New home page
// **************************************************
div .o_wslides_home_nav {
position: relative;
top: -50px;
}
div .o_wslides_home_main {
.o_wslides_home_header {
}
.o_wslides_home_footer {
}
.o_wslides_home_aside {
.fa-check {
color: #00A09D;
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_wslides_course_card {
.card-body {
padding: 0.75em 1em;
}
.card-footer {
padding-top: 0.75em 1em;
}
}
// responsive card-deck like
.row.o_wslides_row > div[class*='col-'] {
display: flex;
flex:1 0 auto;
.card {
width:100%;
}
}
// tools
// ****************************************
.text_small_caps {
font-variant: small-caps;
}
.image-muted {
opacity: 0.5;
}
.o_wslides_home_circled {
border-radius: 50%;
width: 1.2em;
height: 1.2em;
padding: 0.25em 0.5em 0.25em 0.5em;
border: 0.2em solid #666;
text-align: center;
box-sizing: initial;
}
.progress {
overflow: visible;
margin-bottom: 0em;
height: 4px;
.progress-bar {
background-color: #00A09D;
position: relative;
border-radius: 4px;
span {
background-color: #00A09D;
position: absolute;
bottom: -20px;
font-size: 10px;
line-height: 10px;
padding: 2px 3px 2px 4px;
right: -1.4em;
border-radius: 2px;
}
span:after {
bottom: 100%;
left: 50%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
border-color: rgba(255, 255, 255, 0);
border-bottom-color: #00A09D;
border-width: 5px;
margin-left: -5px;
}
}
}
}
// New course page
// **************************************************
.o_wslides_container {
margin: auto;
width: 100%;
max-width: 1140px;
padding: 32px 32px 0px 32px;
}
.o_wslides_course_nav {
margin: auto;
background: linear-gradient(150deg, #875A7B 20%, #62495B 80%) !important;
opacity: 0.95;
nav {
.breadcrumb-item::before {
font-weight: bold;
color: #FFFFFF;
opacity: 0.8;
}
.breadcrumb-item {
a {
font-weight: bold;
color: #FFFFFF;
opacity: 0.8;
transition: opacity linear .3s;
}
&.active {
a {
opacity: 1.0;
}
}
}
ol {
background-color: transparent;
}
}
}
.o_wslides_course_header {
margin: auto;
background: linear-gradient(150deg, #875A7B 20%, #62495B 80%) !important;
border-bottom: 48px solid #66445e;
.o_wslides_course_header_container {
top: 48px;
padding-top: 0px;
.o_wslides_course_header_content {
font-size: 16px;
color: #FFFFFF;
.o_wslides_course_description {
margin-right: 36px;
}
}
.o_wslides_course_header_aside {
img {
width: 100%;
max-width: 256px;
}
}
}
}
.o_wslides_course_content {
margin: auto;
background-color: #f2f2f2;
.o_wslides_content_container {
position: relative;
top: -80px;
}
// responsive card-deck like
.row.o_wslides_row > div[class*='col-'] {
display: flex;
flex:1 0 auto;
.card {
width:100%;
}
}
.o_wslides_course_content_nav {
.o_wslides_nav_tabs {
border-bottom: 0px;
}
.o_wslides_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_wslides_navlink.active {
color: #212529;
background-color: #f2f2f2;
border: 0px;
}
}
.o_wslides_course_content_main {
h3 {
font-size: 1.4em;
}
h4 {
font-size: 1.3em;
}
.o_wslides_course_content_aside {
top: 1px;
width: 256px;
max-width: 256px;
margin-left: 15px;
background-color: white;
.o_wslides_course_aside_line {
}
}
.o_wslides_lesson_card {
.card-footer {
padding-top: 0.75em;
padding-bottom: 0.75em;
}
}
}
}
// Old brol to clean someday
// **************************************************
.oe_slides_panel_footer .fa, .oe_slide_js_embed_option_link{
color : theme-color('primary');
cursor: pointer;
@@ -61,10 +321,6 @@ img.oe_slides_promote_image {
display: block;
}
.oe_slides_box_shadow {
box-shadow: 0 0 5px #CCC;
}
.oe_slides_apart_small {
@include top-covered-image;
height: 60px;
@@ -5,85 +5,12 @@
<!-- Tools template: share on social networkds -->
<template id='slides_share' name="Slides Media Share">
<div>
<a t-attf-href="https://www.facebook.com/sharer/sharer.php?u=#{slide.website_url}" class="o_slides_social_share" social-key="facebook" aria-label="Share on Facebook" title="Share on Facebook"><i class="fa fa-facebook-square fa-2x"/></a>
<a t-attf-href="https://twitter.com/intent/tweet?text=#{slide.name}&amp;url=#{slide.website_url}" class="o_slides_social_share" social-key="twitter" aria-label="Share on Twitter" title="Share on Twitter"><i class="fa fa-twitter fa-2x"/></a>
<a t-attf-href="http://www.linkedin.com/shareArticle?mini=true&amp;url=#{slide.website_url}&amp;title=#{slide.name}&amp;" social-key="linkedin" class="o_slides_social_share" aria-label="Share on LinkedIn" title="Share on LinkedIn"><i class="fa fa-linkedin fa-2x"/></a>
<a t-attf-href="https://plus.google.com/share?url=#{slide.website_url}" class="o_slides_social_share" social-key="gplus" aria-label="Share on Google Plus" title="Share on Google Plus"><i class="fa fa-google-plus-square fa-2x"/></a>
<a t-attf-href="https://www.facebook.com/sharer/sharer.php?u=#{record.website_url}" class="o_slides_social_share" social-key="facebook" aria-label="Share on Facebook" title="Share on Facebook"><i class="fa fa-facebook-square fa-2x"/></a>
<a t-attf-href="https://twitter.com/intent/tweet?text=#{record.name}&amp;url=#{record.website_url}" class="o_slides_social_share" social-key="twitter" aria-label="Share on Twitter" title="Share on Twitter"><i class="fa fa-twitter fa-2x"/></a>
<a t-attf-href="http://www.linkedin.com/shareArticle?mini=true&amp;url=#{record.website_url}&amp;title=#{record.name}&amp;" social-key="linkedin" class="o_slides_social_share" aria-label="Share on LinkedIn" title="Share on LinkedIn"><i class="fa fa-linkedin fa-2x"/></a>
</div>
</template>
<!-- Channels View: main template, display list of available channels -->
<template id="channels" name="Channels">
<t t-call="website.layout">
<div class="container mt16">
<div class="d-flex justify-content-between mb-4">
<h1 t-if="not search_term">Select a Channel</h1>
<h1 t-if="search_term">Channel found for "<i><t t-esc="search_term"/></i>"</h1>
<div class="form-inline justify-content-center">
<form method="GET" class="o_website_slides_search">
<div class="input-group">
<input type="text" name="search" class="search-query form-control oe_search_box" placeholder="Search..." t-att-value="search_term"/>
<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"/>
</button>
</div>
</div>
</form>
</div>
</div>
<div class="row">
<t t-foreach="channels" t-as="channel">
<div t-if="channel_index % 3 == 0" class="clearfix"/>
<div class="col-md-4 mb32">
<div class="card card-body">
<div>
<a t-attf-href="/slides/#{slug(channel)}">
<img t-if="channel.image" t-attf-src="/web/image/slide.channel/#{channel.id}/image_medium"
class="img-fluid oe_slides_channel_thumbnail"
t-att-alt="channel.name"/>
<img t-if="not channel.image" src="/website_slides/static/src/img/channel-default.jpg"
class="img-fluid oe_slides_opacity"
t-att-alt="channel.name"/>
</a>
</div>
<div>
<div class="mt4 float-right">
<t t-call="website.publish_management">
<t t-set="object" t-value="channel"/>
<t t-set="publish_edit" t-value="True"/>
<t t-set="action" t-value="'website_slides.action_slide_channels'"/>
</t>
</div>
<h3 class="mt4 mb4 lead" t-att-title="channel.name" style="width:55%">
<a t-attf-href="/slides/#{slug(channel)}" t-esc="channel.name"/>
</h3>
</div>
<div>
<p class="text-muted oe_no_empty" t-field="channel.description"/>
</div>
<t t-call="website_rating.rating_widget_stars_static">
<t t-set="rating_avg" t-value="channel.rating_avg"/>
<t t-set="rating_count" t-value="channel.rating_count"/>
</t>
</div>
</div>
</t>
</div>
</div>
</t>
</template>
<!-- Channels View: option: subscription -->
<template id="opt_subscription_on_channels" name="Subscription" customize_show="True" inherit_id="website_slides.channels">
<xpath expr="//p[@t-field='channel.description']" position="after">
<t t-call="website_mail.follow">
<t t-set="email" t-value="user.email"/>
<t t-set="object" t-value="channel"/>
</t>
</xpath>
</template>
<!-- Channels: no channel found -->
<template id="channel_not_found" name="Private Presentation">
<t t-call="website.layout">
@@ -101,52 +28,40 @@
</template>
<!-- Channels sub-template: header -->
<template id="slides_channel_header" name="Slides Channel Header">
<section class="oe_slides_box_shadow">
<template id="course_nav" name="Course Navigation Header">
<div class="o_wslides_course_nav">
<div class="container">
<div class="row">
<!-- Name and breadcrumb -->
<div class="float-left col-lg-8 col-md-12 col-12 mt16 mb8">
<div class="row">
<h3 class="mt0">
<a t-attf-href="/slides/#{slug(channel)}" t-esc="channel.name"/>
<span t-if="not channel.is_member" class="o_wslides_join_channel">
<a role="button" class="btn btn-primary o_wslides_join_channel_link"
title="Join Channel" aria-label="Join Channel" href="#"
t-att-data-channel-id="channel.id">
<i class="fa fa-chevron-right"></i> Join
</a>
</span>
<span t-if="channel.is_member">
<a role="button" class="btn btn-primary o_wslides_join_channel_link"
title="Join Channel" aria-label="Join Channel" href="#">
<i class="fa fa-chevron-right"></i> Joined
</a>
</span>
<t t-if="category">
/ <a t-attf-href="/slides/#{slug(channel)}/category/#{slug(category)}" t-esc="category.name"/>
</t>
<t t-if="channel.can_upload">
<a role="button" class="btn btn-primary oe_slide_js_upload" title="Upload Presentation"
aria-label="Upload Presentation" href="#"
t-att-data-channel-id="channel.id"
t-att-data-can-upload="channel.can_upload"
t-att-data-can-publish="channel.can_publish">
<i class="fa fa-cloud-upload"></i> Upload
</a>
</t>
</h3>
<ul class="navbar-nav ml-auto float-right pt-2">
<li class="nav-item">
<a t-attf-href="/profile/user/#{user.id}?channel_id=#{channel.id}" t-attf-class="text-dark font-weight-bold nav-link}">
<i class="fa fa-diamond text-secondary" /> <t t-esc="user_id.karma"/>
</a>
</li>
</ul>
</div>
</div>
<!-- Search box -->
<div class="col-lg-4 col-md-12 col-12 pul-right mt16 mb8">
<div class="row align-items-center justify-content-between">
<!-- nav -->
<nav aria-label="breadcrumb" class="col-6">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="/slides">Home</a>
</li>
<li t-att-class="'breadcrumb-item %s' % ('active' if not search_category and not search_tag and not search_slide_type else '')">
<a t-att-href="'/slides/%s' % slug(channel)"><span t-esc="channel.name"/></a>
</li>
<li class="breadcrumb-item active" t-att-aria-current="'page' and search_category" t-if="search_category">
<a t-att-href="'/slides/%s/category/%s' % (slug(channel), slug(search_category))"><span t-esc="search_category.name"/></a>
</li>
<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_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>
</ol>
</nav>
<div class="col-4 d-flex flex-row">
<!-- karma -->
<!-- <ul class="navbar-nav ml-auto float-right pt-2">
<li class="nav-item">
<a t-attf-href="/profile/user/#{user.id}?channel_id=#{channel.id}" t-attf-class="text-dark font-weight-bold nav-link}">
<i class="fa fa-diamond text-secondary" /> <t t-esc="user_id.karma"/>
</a>
</li>
</ul> -->
<!-- search -->
<form t-attf-action="/slides/#{slug(channel)}" role="search" method="get">
<div class="input-group">
<input type="text" class="form-control" name="search" t-attf-placeholder="Search in #{channel.name}" t-att-value="search"/>
@@ -157,247 +72,308 @@
</span>
</div>
</form>
</div>
</div>
<div class="row" t-if="not search">
<!-- Navigation -->
<div class="col-lg-8 col-md-12 col-12">
<ul class="nav nav-tabs nav-tabs-border" role="tablist">
<li class="nav-item">
<a t-attf-href="/slides/#{slug(channel)}#{category and '/category/' + slug(category) or ''}"
t-attf-class="nav-link#{not slide_type and ' active' or ''}"> <i class="fa fa-home"/> Home</a>
</li>
<li t-if="header_object.nbr_presentations" class="nav-item">
<a t-attf-href="/slides/#{slug(channel)}#{category and '/category/' + slug(category) or ''}/presentation"
t-attf-class="nav-link#{slide_type == 'presentation' and ' active' or ''}">
<span class="badge badge-pill" t-esc="header_object.nbr_presentations"/> Presentations
</a>
</li>
<li t-if="header_object.nbr_videos" class="nav-item">
<a t-attf-href="/slides/#{slug(channel)}#{category and '/category/' + slug(category) or ''}/video"
t-attf-class="nav-link#{slide_type == 'video' and ' active' or ''}">
<span class="badge badge-pill" t-esc="header_object.nbr_videos"/> Videos
</a>
</li>
<li t-if="header_object.nbr_documents" class="li nav-item">
<a t-attf-href="/slides/#{slug(channel)}#{category and '/category/' + slug(category) or ''}/document"
t-attf-class="nav-link#{slide_type == 'document' and ' active' or ''}">
<span class="badge badge-pill" t-esc="header_object.nbr_documents"/> Documents
</a>
</li>
<li t-if="header_object.nbr_infographics" class="nav-item">
<a t-attf-href="/slides/#{slug(channel)}#{category and '/category/' + slug(category) or ''}/infographic"
t-attf-class="nav-link#{slide_type == 'infographic' and ' active' or ''}">
<span class="badge badge-pill" t-esc="header_object.nbr_infographics"/> Infographics
</a>
</li>
<li t-if="header_object.nbr_webpages" class="nav-item">
<a t-attf-href="/slides/#{slug(channel)}#{category and '/category/' + slug(category) or ''}/webpage"
t-attf-class="nav-link#{slide_type == 'webpage' and ' active' or ''}">
<span class="badge badge-pill" t-esc="header_object.nbr_webpages"/> Pages
</a>
</li>
</ul>
</div>
<!-- Sort options -->
<div class="col-lg-4 col-md-12 col-12">
<span class="float-right">
<span class="text-muted">Sort by:</span>
<a href="?sorting=date">
<span t-attf-style="#{sorting and sorting == 'date' and 'font-weight:bold;' ''}">Newest</span>
</a>
<span class="text-muted">|</span>
<a href="?sorting=view">
<span t-attf-style="#{sorting and sorting == 'view' and 'font-weight:bold;' ''}">Most Viewed</span>
</a>
<span class="text-muted">|</span>
<a href="?sorting=vote">
<span t-attf-style="#{sorting and sorting == 'vote' and 'font-weight:bold;' ''}">Most Voted</span>
</a>
</span>
<!-- sort / filter -->
<small class="dropdown float-right ml-2">
<h6 class="m-0">
<button class="btn btn-primary dropdown-toggle" type="button" id="slidesChannelDropdownSort"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<t t-if="sorting == 'most_voted'">Most Voted</t>
<t t-elif="sorting == 'most_viewed'">Most Viewed</t>
<t t-else="">Newest</t>
</button>
<div class="dropdown-menu" aria-labelledby="slidesChannelDropdownSort" role="menu">
<div role="separator" class="dropdown-header">Sort by</div>
<a role="menuitem" t-att-href="'/slides/%s?%s' % (slug(channel), keep_query('debug', 'slide_type', sorting='latest'))"
t-att-class="'dropdown-item %s' % ('active' if sorting and sorting == 'latest' else '')">Newest</a>
<a role="menuitem" t-att-href="'/slides/%s?%s' % (slug(channel), keep_query('debug', 'slide_type', sorting='most_voted'))"
t-att-class="'dropdown-item %s' % ('active' if sorting and sorting == 'most_voted' else '')">Most Voted</a>
<a role="menuitem" t-att-href="'/slides/%s?%s' % (slug(channel), keep_query('debug', 'slide_type', sorting='most_viewed'))"
t-att-class="'dropdown-item %s' % ('active' if sorting and sorting == 'most_viewed' else '')">Most Viewed</a>
</div>
</h6>
</small>
</div>
</div>
</div>
</section>
</div>
</template>
<!-- Channel main template -->
<template id='home' name="Odoo Slides">
<t t-set="head">
<template id='course_main' name="Course Main">
<t t-set="head">
<t t-call-assets="web.pdf_js_lib" t-css="false"/>
<script type="text/javascript" src="/website_slides/static/lib/pdfslidesviewer/PDFSlidesViewer.js"></script>
</t>
<t t-call="website.layout">
<div class="wrap">
<div class="wrap o_wslides_wrap">
<t t-set="main_object" t-value="channel"/>
<t t-call="website_slides.slides_channel_header">
<t t-if="not category">
<t t-set="header_object" t-value="channel"/>
</t>
<t t-if="category">
<t t-set="header_object" t-value="category"/>
</t>
</t>
<section t-if="not tag and not slide_type and not search and not is_public_user and not category" class="wrap mt16 mb0" groups="website.group_website_designer">
<div class="container">
<div class="alert alert-danger alert-dismissable mb0" role="alert">
<p>
Review your channel settings to promote your most viewed or recent published presentation
</p>
<a t-attf-href="/web#return_label=Website&amp;id=#{channel.id}&amp;view_type=form&amp;model=slide.channel&amp;action=#{request.env.ref('website_slides.action_slide_channels').id }" class="fa fa-arrow-right mt8">
Channel Settings
</a>
</div>
</div>
</section>
<section class="oe_slides_promote_box" t-if="channel.slide_ids and not tag and not slide_type and not search and not category">
<div class="container">
<div class="row">
<div class="col-lg-5 mt16 mb16">
<a t-attf-href="/slides/slide/#{slug(channel.slide_ids[0])}">
<img t-attf-src="/web/image/slide.slide/#{channel.slide_ids[0].id}/image_medium" class="img-fluid shadow oe_slides_promote_image" alt="Promoted slide"/>
</a>
<t t-call="website_slides.course_nav"/>
<div class="row o_wslides_course_header">
<div class="col-lg-12 o_wslides_container o_wslides_course_header_container">
<div class="row align-items-end">
<div class="col o_wslides_course_header_aside">
<img class="mr-3" t-att-src="'/web/image/slide.channel/%s/image' % channel.id"/>
</div>
<div class="col-lg-7 mt16 mb16">
<h3 class="row">
<a t-attf-href="/slides/slide/#{slug(channel.slide_ids[0])}">
<t t-esc="channel.slide_ids[0].name"/>
</a>
</h3>
<div class="row mt8 mb8" t-if="channel.slide_ids[0].tag_ids">
<t t-foreach="channel.slide_ids[0].tag_ids" t-as="slide_tag">
<a t-attf-href="/slides/#{slug(channel)}/tag/#{slug(slide_tag)}" t-attf-class="badge badge-secondary #{tag and tag.id == slide_tag.id and 'badge-primary' ''}" t-field="slide_tag.name"/>
</t>
</div>
<div class="row oe_no_empty" t-esc="channel.slide_ids[0].description"/>
<p class="row mt8">
<t t-call="website_rating.rating_stars_static_popup_composer">
<t t-set="rating_avg" t-value="rating_avg"/>
<t t-set="rating_total" t-value="rating_count"/>
<t t-set="object" t-value="channel"/>
<t t-set="hash" t-value="message_post_hash"/>
<t t-set="pid" t-value="message_post_pid"/>
<t t-set="default_message_id" t-value="last_message_id"/>
<t t-set="default_message" t-value="last_message"/>
<t t-set="default_rating_value" t-value="last_rating_value"/>
<t t-set="force_submit_url" t-value="'/slides/mail/update_comment' if last_message_id else False"/>
</t>
</p>
<p class="row mt8">
<b>Share:</b>
<t t-call="website_slides.slides_share">
<t t-set="slide" t-value="channel.slide_ids[0]"/>
</t>
</p>
</div>
</div>
</div>
</section>
<section class="o_slides_discussion_rating">
<div class="container mt16 mb16">
<hr/>
<div class="row">
<div class="col-lg-8 offset-lg-2">
<t t-call="portal.message_thread">
<t t-set="object" t-value="channel"/>
<t t-set="display_rating" t-value="True"/>
<t t-set="disable_composer" t-value="True"/>
</t>
</div>
</div>
</div>
</section>
<section>
<div class="container mt16">
<div class="row">
<div class="col-12" t-if="search and not slides">
<div class="alert alert-danger" role="alert">
Sorry, but nothing matches your search criteria <b><t t-esc="search"/></b>. Please try again with different keywords.
<div class="col-8 o_wslides_course_header_content">
<h3 class="row"><span class="font-weight-bold col" t-field="channel.name"/></h3>
<div class="row">
<div class="col-12 o_wslides_course_description" t-field="channel.description"/>
<div class="col-6 mt8 mb64">
<t t-call="website_rating.rating_stars_static_popup_composer">
<t t-set="rating_avg" t-value="rating_avg"/>
<t t-set="rating_total" t-value="rating_count"/>
<t t-set="object" t-value="channel"/>
<t t-set="hash" t-value="message_post_hash"/>
<t t-set="pid" t-value="message_post_pid"/>
<t t-set="default_message_id" t-value="last_message_id"/>
<t t-set="default_message" t-value="last_message"/>
<t t-set="default_rating_value" t-value="last_rating_value"/>
<t t-set="force_submit_url" t-value="'/slides/mail/update_comment' if last_message_id else False"/>
</t>
</div>
</div>
</div>
<div class="col-xl-12 col-lg-12 col-md-12 col-12" style="padding-left: 15px;padding-right: 15px;" t-if="channel.total_slides == 0">
<div class="jumbotron">
<h1>No presentation published yet.</h1>
<p>
The <i><t t-esc="channel.name"/></i> is empty.
<span t-if="channel.can_upload">
Upload PDF presentations, documents, videos or infographic using the button below.
</div>
</div>
</div>
<div class="row o_wslides_course_content">
<div class="col-lg-12 o_wslides_container o_wslides_content_container">
<div class="row justify-content-end o_wslides_course_content_nav">
<div class="col-8">
<ul class="nav nav-tabs o_wslides_nav_tabs" role="tablist" id="profile_extra_info_tablist">
<li class="nav-item o_wslides_nav_tabs_item_home">
<a t-att-href="'/slides/%s%s?%s' % (slug(channel), '/%s' % slug(category) if category else '', keep_query('debug'))"
t-att-class="'nav-link o_wslides_navlink %s' % ('' if search_slide_type else 'active')">
<i class="fa fa-home"/> Home</a>
</li>
<li class="nav-item">
<a t-att-href="'/slides/%s' % (slug(channel))"
t-att-class="'nav-link o_wslides_navlink'">Review</a>
</li>
</ul>
</div>
</div>
<div class="row o_wslides_course_content_main">
<!-- Channel presentation, aka general information + CTAs -->
<div class="col-4 o_wslides_course_content_aside">
<div class="row justify-content-center mt8 o_wslides_join_channel">
<a t-if="not channel.is_member and channel.visibility == 'public'" role="button"
class="col-10 btn btn-primary o_wslides_join_channel_link"
title="Start Course" aria-label="Start Course Channel"
t-att-href="'#'"
t-att-data-channel-id="channel.id">
<span class="cta-title text_small_caps">
<t t-if="channel.channel_type == 'documentation'">Start Course</t>
<t t-else="">Join Course</t>
</span>
</p>
<p>
<!-- Public user has not right to create/write on slide-->
<a role="button" t-if="channel.can_upload" class="btn btn-primary oe_slide_js_upload" t-attf-channel-id="#{channel.id}" aria-label="Upload Presentation" title="Upload Presentation" href="#">
<i class="fa fa-cloud-upload"></i> Upload
</a>
<t t-if="not channel.is_member and channel.visibility == 'invite'">
<a role="button" class="col-10 btn btn-light" t-att-href="'#'">
<span class="cta-title text_small_caps">Private Course</span>
</a>
</p>
<div t-if="channel.enroll_msg" t-field="channel.enroll_msg"/>
<span t-if="not channel.enroll_msg">Contact website administrator.</span>
</t>
<span t-if="channel.is_member" class="col-10 btn btn-primary">
<i class="fa fa-chevron-right"></i>
<span class="cta-title text_small_caps">Already member</span>
</span>
</div>
</div>
<div class="col-xl-12 col-lg-12 col-md-12 col-12" t-if="slide_type or category or tag">
<div class="alert alert-success mb0" role="status" t-if="tag">
Results for <strong t-esc="tag.name"/>
<div class="row mt8">
<span class="col-5">Last Update</span>
<span class="col"><t t-esc="channel.slide_last_update" t-options="{'widget': 'date'}"/></span>
</div>
<t t-call="website_slides.slides_grid_view"/>
</div>
<div class="row text-center" t-if="slide_type or category or tag">
<t t-call="website.pager"/>
</div>
<div class="col-xl-12 col-lg-12 col-md-12 col-12 mb16" t-if="not slide_type and category_datas and not tag">
<t t-foreach="category_datas" t-as="category">
<div class="col-xl-12 col-lg-12 col-md-12 col-12">
<div class="col-xl-12 col-lg-12 col-md-12 col-12 mt8 oe_slides_bottom_border">
<a t-if="category['id']" t-attf-href="/slides/#{slug(channel)}/category/#{category['id']}" class="h3 mt0 mb0">
<t t-esc="category['name']"/>
</a>
<span t-if="not category['id']" title="Uncategorized presentation" class="h3 mt0 mb0">
<t t-esc="category['name']"/>
</span>
<a t-attf-href="/slides/#{slug(channel)}/category/#{category['id']}" class="float-right" t-if="category['total_slides'] - 4 > 0 and category['id']">
<i class="fa fa-arrow-right"/> See all
</a>
<div class="row mt8">
<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-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">
<span class="col-5">
<t t-if="search_category">
<a t-att-href="'/slides/%s/category/%s?%s' % (slug(channel), slug(search_category), keep_query('debug', slide_type=slide_type_key))"
t-att-class="'%s' % ('active' if search_slide_type == slide_type_key else '')">
<t t-esc="slide_types[slide_type_key]"/>
</a>
</t>
<t t-else="">
<a t-att-href="'/slides/%s?%s' % (slug(channel), keep_query('debug', slide_type=slide_type_key))"
t-att-class="'%s' % ('active' if search_slide_type == slide_type_key else '')">
<t t-esc="slide_types[slide_type_key]"/>
</a>
</t>
</span>
<span class="col">
<t t-if="search_category">
<span class="badge badge-pill badge-info" t-esc="search_category['nbr_%ss' % slide_type_key]"/>
</t>
<t t-else="">
<span class="badge badge-pill badge-info" t-esc="channel['nbr_%ss' % slide_type_key]"/>
</t>
</span>
</div>
</t>
<div class="row mt8"><div class="col"><hr/></div></div>
<div class="row justify-content-center mt8">
<button role="button" class="btn btn-primary col-10" title="Share Channel"
aria-label="Share Channel"
data-toggle="modal" data-target="#slideChannelShareModal">
<i class="fa fa-share-square"></i> Share
</button>
<div class="modal fade" id="slideChannelShareModal" tabindex="-1" role="dialog" aria-labelledby="slideChannelShareModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="slideChannelShareModalLabel">
Share
</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span>&amp;times;</span>
</button>
</div>
<div class="modal-body">
<h5>Share on Social Networks</h5>
<t t-call="website_slides.slides_share">
<t t-set="record" t-value="channel"/>
</t>
<h5 class="mt16">Share Link</h5>
<input type="text" class="form-control" readonly="readonly" onclick="this.select();"
t-att-value="channel.website_url"/>
</div>
</div>
</div>
</div>
<t t-if="not search">
<t t-call="website_slides.slides_grid_view">
<t t-set="slides" t-value="category['slides']"/>
</div>
<div t-if="channel.can_upload" class="row justify-content-center mt8 mb8">
<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"
t-att-data-can-upload="channel.can_upload"
t-att-data-can-publish="channel.can_publish">
<i class="fa fa-cloud-upload"></i> Upload
</a>
</div>
</div>
<!-- Channel content, aka slides (lessons in documentation mode) -->
<div class="col-8 mt24 ml32 o_wslides_channel_content_promoted">
<div class="row mt16 align-items-center">
<div class="col">
<h3><i class="fa fa-plus-square"/> Featured lesson</h3>
<hr/>
</div>
</div>
<div class="row" t-if="slide_promoted">
<div class="col-4">
<img class="img img-fluid" t-att-src="'/web/image/slide.slide/%s/image' % slide_promoted.id"/>
</div>
<div class="col-8">
<div class="row">
<h3 class="col-6" t-att-title="slide_promoted.name">
<t t-if="slide_promoted.is_preview or channel.is_member">
<a t-attf-href="/slides/slide/#{slug(slide_promoted)}" class="font-weight-bold text-muted" t-field="slide_promoted.name"/>
</t>
<t t-else="">
<span class="font-weight-bold text-muted" t-field="slide_promoted.name"/>
</t>
</h3>
<div class="col-12" t-field="slide_promoted.description"/>
</div>
</div>
</div>
</div>
<!-- Channel content, aka slides (lessons in documentation mode) -->
<div class="col-12">
<div class="tab-content mt16" id="slideChannelContent">
<div class="tab-pane active" role="tabpanel" aria-labelledby="profile-tab" id="slideChannelContentAbout">
<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><i class="fa fa-plus-square"/> <t t-esc="category['name']"/></h3>
<hr/>
</div>
</div>
<t t-call="website_slides.slides_grid_view">
<t t-set="slides" t-value="category['slides']"/>
</t>
</t>
</t>
<t t-else="">
<t t-call="website_slides.slides_grid_view"/>
</t>
</t>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
</t>
</template>
<!-- Embedded template: display a list of slides in a grid view -->
<template id='slides_grid_view' name="Slides (Grid View)">
<div class="row">
<div class="col-xl-3 col-lg-4 col-md-6 col-12 mt16 mb16" t-foreach="slides" t-as="slide">
<div class="card card-body">
<a t-attf-href="/slides/slide/#{slug(slide)}">
<img t-attf-src="/web/image/slide.slide/#{slide.id}/image_medium" class="img-fluid img-thumbnail oe_slides_grid_thumbnail" alt="slide.name"/>
</a>
<span t-if="not slide.website_published" class="badge badge-danger" style="position: absolute;right: 20px;top: 7px;">Unpublished</span>
<div style="padding: 5px;">
<h4 class="mt4 mb8 oe_slides_ellipsis">
<a t-att-title="slide.name" t-attf-href="/slides/slide/#{slug(slide)}" t-esc="slide.name"/>
</h4>
<div class="text-muted clearfix">
<small class="float-left"><t t-esc="slide.total_views"/> Views</small>
<small class="float-right"><timeago class="timeago" t-att-datetime="slide.create_date"></timeago></small>
</div>
<p t-if="slide.tag_ids" style="padding-top: 5px;">
<t t-foreach="slide.tag_ids" t-as="att_tag">
<a t-attf-href="/slides/#{slug(channel)}/tag/#{slug(att_tag)}" t-attf-class="badge badge-secondary #{tag and tag.name == att_tag.name and 'badge-primary' ''}" t-field="att_tag.name"/>
<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="'/web/image/slide.slide/%s/image_large' % slide.id if slide.image else '/website_slides/static/src/img/channel-default.jpg'"
t-att-alt="channel.name"/>
<div class="card-body">
<h5 class="card-title" t-att-title="slide.name">
<t t-if="slide.is_preview or channel.is_member">
<a t-attf-href="/slides/slide/#{slug(slide)}" t-esc="slide.name"/>
</t>
</p>
<t t-else="">
<span class="text-muted" t-esc="slide.name"/>
</t>
</h5>
<h6 class="card-subtitle mb-2 text-muted">
<t t-if="user.has_group('website.group_website_publisher')">
<i class="fa fa-globe float-right o_wslides_slide_manage_publish"></i>
<i class="fa fa-pencil float-right o_wslides_slide_manage_edit"></i>
<i class="fa fa-trash float-right o_wslides_slide_manage_unlink"></i>
</t>
<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>
<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 t-call="website_slides.slides_misc_float_time">
<t t-set="value" t-value="slide.completion_time"/>
</t>
<!-- <t t-esc="slide.total_views"/> Views
<timeago class="timeago" t-att-datetime="slide.create_date"></timeago> -->
</small>
</div>
</div>
</div>
@@ -435,7 +411,7 @@
<div clas="row">
<div class="col-lg-8" t-if="slide.tag_ids">
<t t-foreach="slide.tag_ids" t-as="slide_tag">
<a t-attf-href="/slides/#{slug(slide.channel_id)}/tag/#{slug(slide_tag)}" t-attf-class="badge badge-secondary ml4 mb4 float-right #{tag and tag.id == slide_tag.id and 'badge-primary' ''}" t-field="slide_tag.name"/>
<a t-attf-href="/slides/#{slug(slide.channel_id)}/tag/#{slug(slide_tag)}" t-attf-class="badge badge-secondary ml4 mb4 float-right #{search_tag and search_tag.id == slide_tag.id and 'badge-primary' ''}" t-field="slide_tag.name"/>
</t>
</div>
</div>
@@ -723,7 +699,9 @@
<!-- Slide sub-template: share: social media -->
<template id='slide_social_media' name="Share on Social Networks">
<h4 class="mt0">Share on Social Networks</h4>
<t t-call="website_slides.slides_share"/>
<t t-call="website_slides.slides_share">
<t t-set="record" t-value="slide"/>
</t>
<h4 class="mt-3">Share Link</h4>
<input type="text" class="form-control" t-att-value="slide.website_url" readonly="readonly" onClick="this.select();"/>
<span class="form-text">Use permanent link to share in social media</span>
@@ -0,0 +1,429 @@
<?xml version="1.0" ?>
<odoo><data>
<!-- Channels home template -->
<template id='courses_home' name="Odoo Courses Homepage">
<t t-call="website.layout">
<t t-call="website_slides.courses_header">
<t t-set="header_title" t-value="'Reach new heights'"/>
<t t-set="header_subtitle" t-value="'Start your online course today !'"/>
<t t-set="header_text" t-value="'Skill up and have an impact! Your business career starts here. Time to start a course.'"/>
<t t-set="header_cta_url" t-value="'slides'"/>
<t t-set="header_cta_title" t-value="'Pick a course'"/>
</t>
<div class="wrap">
<div class="container mt16 o_wslides_home_nav">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<button class="navbar-toggler" type="button"
data-toggle="collapse" data-target="#navbarSlidesHomepage"
aria-controls="navbarSlidesHomepage" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSlidesHomepage">
<a class="navbar-brand" href="#"></a>
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<h4><a class="nav-link" href="/slides/all"><i class="fa fa-rocket"/> All courses</a></h4>
</li>
<li class="nav-item ml16">
<h4><a class="nav-link" href="/slides/all"><i class="fa fa-certificate"/> Certifications</a></h4>
</li>
<li class="nav-item ml16">
<h4><a class="nav-link" href="/slides/all"><i class="fa fa-file"/> My courses</a></h4>
</li>
</ul>
</div>
</nav>
</div>
<div class="container mt16 o_wslides_home_main">
<div class="row">
<div class="col-md-9">
<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="col">
<a href="slides/all" class="float-right">View all</a>
<h3><i class="fa fa-certificate"/> Complete latest courses</h3>
<hr/>
</div>
</div>
<div class="row o_wslides_row 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"/>
</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="col">
<a href="slides/all" class="float-right">View all</a>
<h3><i class="fa fa-fire"/> Most popular courses</h3>
<hr/>
</div>
</div>
<div class="row o_wslides_row 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"/>
</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="col">
<a href="slides/all" class="float-right">View all</a>
<h3><i class="fa fa-plus-square"/> Newest courses</h3>
<hr/>
</div>
</div>
<div class="row o_wslides_row 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"/>
</t>
</div>
</div>
</div>
<div class="col-md-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"/>
</t>
<t t-else="">
<t t-call="website_slides.slides_home_user_profile_small"/>
<hr />
<t t-call="website_slides.slides_home_user_achievements_small"/>
</t>
</div>
</div>
</div>
</div>
<t t-call="website_slides.courses_footer">
<t t-set="footer_title" t-value="'Meet the people who transformed their company'"/>
<t t-set="footer_text" t-value="'Nobody gave me a real text so instead of a lorem ipsum I put that one.'"/>
<t t-set="footer_cta_url" t-value="'slides'"/>
<t t-set="footer_cta_title" t-value="'Go meet them'"/>
</t>
</t>
</template>
<!-- Channel all/main template -->
<template id='courses_all' name="Odoo All Courses">
<t t-call="website.layout">
<t t-call="website_slides.courses_header">
<t t-set="header_title" t-value="'All Courses'"/>
<t t-set="header_subtitle" t-value="'Start your online course today !'"/>
</t>
<div class="wrap">
<div class="container mt16 o_wslides_home_nav">
<!-- Navbar dynamically composed using displayed channel tag groups. -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" t-att-href="'/slides?%s' % keep_query('debug')"><i class="fa fa-chevron-left"/></a>
<button class="navbar-toggler" type="button"
data-toggle="collapse" data-target="#navbarTagGroups"
aria-controls="navbarTagGroups" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarTagGroups">
<t t-set="search_tag_groups" t-value="search_tags.mapped('group_id')"/>
<ul class="navbar-nav mr-auto">
<t t-foreach="tag_groups" t-as="tag_group">
<li t-att-class="'nav-item dropdown ml16 %s' % ('active' if tag_group in search_tag_groups else '')">
<h4><a class="nav-link dropdown-toggle"
href="/slides/all"
t-att-data-target="'#navToogleTagGroup%s' % tag_group.id"
role="button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false"
t-esc="tag_group.name"/></h4>
<div class="dropdown-menu" t-att-id="'navToogleTagGroup%s' % tag_group.id">
<t t-foreach="tag_group.tag_ids" t-as="tag">
<a t-att-class="'dropdown-item %s' % ('active' if tag in search_tags else '')"
t-att-href="'/slides/all?%s' % keep_query('*', **{'channel_tag_group_id_%s' % tag_group.id: tag.id if tag not in search_tags else False})"
t-esc="tag.name"/>
</t>
</div>
</li>
</t>
</ul>
<form method="GET" class="form-inline justify-content-center">
<div class="input-group">
<input type="search" name="search" class="form-control"
placeholder="Search something" aria-label="Search"
t-att-value="search_term"/>
<div class="input-group-append">
<button class="btn btn-outline-success oe_search_button" type="submit" aria-label="Search" title="Search">
<i class="fa fa-search"/>
</button>
</div>
</div>
</form>
</div>
</nav>
</div>
<div class="container mt16 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>
</div>
<t t-call="website_slides.courses_footer">
<t t-set="footer_title" t-value="'Meet the people who transformed their company'"/>
<t t-set="footer_text" t-value="'Nobody gave me a real text so instead of a lorem ipsum I put that one.'"/>
<t t-set="footer_cta_url" t-value="'slides'"/>
<t t-set="footer_cta_title" t-value="'Go meet them'"/>
</t>
</t>
</template>
<template id="courses_header">
<section class="s_cover parallax bg-black-50 pt40 pb40 o_wslides_home_header" data-scroll-background-ratio="0" style="background-image: none;">
<span class="s_parallax_bg oe_img_bg oe_custom_bg" style="background-image: url(&quot;/website_slides/static/src/img/channel_banner_moutain.jpg&quot;); background-position: 52.1% 17.69%;"></span>
<div class="container">
<div class="row s_nb_column_fixed">
<div class="s_title pt0 col-lg-12" data-name="Title">
<h1 class="s_title_small_caps" style="font-size: 62px; text-align: left;"><t t-esc="header_title"/></h1>
<h4 t-if="header_subtitle" class="s_title_small_caps" style="font-size: 34px; text-align: left;"><t t-esc="header_subtitle"/></h4>
</div>
</div>
<div class="row s_nb_column_fixed">
<div t-if="header_text" class="col-lg-3 s_text pt16 pb16" data-name="Text">
<p class="lead" style="text-align: left;"><t t-esc="header_text"/></p>
</div>
<div t-if="header_cta_title" class="col-lg-3 s_btn text-center pt16 pb16" data-name="Buttons">
<a t-att-href="header_cta_url"
class="btn btn-primary o_default_snippet_text text_small_caps"><t t-esc="header_cta_title"/></a>
</div>
</div>
</div>
</section>
</template>
<template id="courses_footer">
<section class="s_cover parallax bg-black-50 pt40 pb40 o_wslides_home_footer mt16" data-scroll-background-ratio="0" style="background-image: none;">
<span class="s_parallax_bg oe_img_bg oe_custom_bg" style="background-image: url(&quot;/website/static/src/img/backgrounds/people.jpg&quot;); background-position: 50.11% 29.1%;"></span>
<div class="container">
<div class="row s_nb_column_fixed">
<div class="col-lg-12 s_title" data-name="Title">
<h1 class="s_title_small_caps" style="font-size: 40px; text-align: left;"><t t-esc="footer_title"/></h1>
<h4 t-if="footer_subtitle" class="s_title_small_caps" style="font-size: 26px; text-align: left;"><t t-esc="footer_subtitle"/></h4>
</div>
</div>
<div class="row s_nb_column_fixed">
<div t-if="footer_text" class="col-lg-3 s_text pt16 pb16" data-name="Text">
<p class="lead o_default_snippet_text" style="text-align: left;"><t t-esc="footer_text"/></p>
</div>
<div t-if="footer_cta_title" class="col-lg-3 s_btn text-center pt16 pb16" data-name="Buttons">
<a t-att-href="footer_cta_url"
class="btn btn-primary o_default_snippet_text text_small_caps"><t t-esc="footer_cta_title"/></a>
</div>
</div>
</div>
</section>
</template>
<template id='course_card' name="Course Card">
<div class="card o_wslides_course_card">
<img class="card-img-top img-fuild"
style="max-height: 160px;"
t-att-src="'/web/image/slide.channel/%s/image_large' % channel.id if channel.image else '/website_slides/static/src/img/channel-default.jpg'"
t-att-alt="channel.name"/>
<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">
<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>
</t>
</div>
</div>
</div>
<div class="card-footer">
<div class="row justify-content-between align-items-center">
<small class="text-muted col-3">
<t t-call="website_slides.slides_misc_float_time">
<t t-set="value" t-value="channel.total_time"/>
</t>
</small>
<t t-if="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}%;">
<!-- <span><t t-esc="channel.completion"/>%</span> -->
</div>
</div>
</div>
</t>
<t t-else="">
<small class="text-muted col-5"><span t-esc="len(channel.slide_ids)"/> steps</small>
</t>
</div>
</div>
</div>
</template>
<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="col">
<a href="slides" class="text-muted float-right">
<small class="float-right">View all</small>
</a>
<h3>Latest achievements</h3>
<hr/>
</div>
</div>
<div class="row">
<t t-if="achievements">
<div class="col">
<t t-foreach="achievements" t-as="achievement">
<t t-call="website_slides.achievement_card"/>
</t>
</div>
</t>
<t t-else=""><p class="col mt8">No achievements currently :(</p></t>
</div>
</div>
</template>
<template id='achievement_card' name="Achivement Card">
<div class="row no-gutters mt8 align-items-center">
<div class="col-2">
<t t-call="website_slides.slides_misc_user_image">
<t t-set="user" t-value="achievement.user_id"/>
<t t-set="record" t-value="channels_popular[:1]"/>
</t>
</div>
<div class="col">
<span class="font-weight-bold" t-esc="achievement.user_id.name"/> achieved <a href="/slides"><span t-esc="achievement.badge_id.name"/></a>
</div>
</div>
</template>
<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="col">
<a href="slides" class="text-muted float-right">
<small class="float-right">View all</small>
</a>
<h3>Heroes of the week</h3>
<hr/>
</div>
</div>
<div class="row">
<t t-if="users">
<div class="col">
<t t-set="counter" t-value="1"/>
<t t-foreach="users" t-as="user">
<t t-call="website_slides.user_quickkarma_card"/>
<t t-set="counter" t-value="counter + 1"/>
</t>
</div>
</t>
<t t-else=""><p class="col mt8">No heroes currently :(</p></t>
</div>
</div>
</template>
<template id='user_quickkarma_card' name="User QuickKarma Card">
<div class="row no-gutters mt8 align-items-center">
<div class="col-2">
<span class="font-weight-bold text-muted o_wslides_home_circled" t-esc="counter"/>
</div>
<div class="col-2">
<t t-call="website_slides.slides_misc_user_image">
<t t-set="record" t-value="channels_popular[:1]"/>
</t>
</div>
<div class="col">
<span class="font-weight-bold" t-esc="user.name"/>
<div class="d-flex align-items-center">
<span class="text-muted" t-esc="user.rank_id.name"/><span class="ml8 mr8">&#8226;</span><span t-esc="user.karma" class="badge badge-success"/>
</div>
</div>
</div>
</template>
<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">
<t t-call="website_slides.slides_misc_user_image">
<t t-set="record" t-value="channels_popular[:1]"/>
</t>
<a t-att-href="'/slides'" class="float-right">Edit</a>
<h4 t-esc="user.name" class="font-weight-bold"/>
<hr/>
</div>
</div>
<div class="row mt8">
<p class="col">INSERT DBE TEMPLATE HERE</p>
</div>
</div>
</template>
<template id='slides_home_user_achievements_small' name="User Achievements">
<div class="o_wslides_home_aside mt16">
<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' % 'image-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/>
<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>
</div>
</t>
</div>
</template>
<template id='slides_misc_user_image' name="Float Time">
<img t-att-class="img_class if img_class else 'rounded-circle float-left'"
t-att-style="img_style if img_style else 'max-height: 32px;'"
t-att-src="'/profile/avatar/%s?field=image_medium%s' % (user.id, '&amp;res_model=%s&amp;res_id=%s' % (record._name, record.id) if record else '')"
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>