[REF] website_slides: rename slide.slide fields

This commit simply applies the following renames in the slide.slide model:

- 'datas' is renamed into 'binary_content'
- 'slide_type' is renamed into 'slide_category'

This changes intend to ease the next bug refactoring of the website_slide
module content management.

There should be no functional changes applied in this commit.

Task-2510174

Part-of: odoo/odoo#71477
This commit is contained in:
Aurélien Warnon
2021-11-30 15:21:47 +00:00
parent 8861c45ff2
commit ab0b36e8a5
39 changed files with 320 additions and 311 deletions
@@ -148,7 +148,7 @@ class TestUi(TestUICommon):
(0, 0, {
'name': 'DIY Furniture Certification',
'sequence': 1,
'slide_type': 'certification',
'slide_category': 'certification',
'category_id': False,
'is_published': True,
'is_preview': False,
+34 -25
View File
@@ -67,7 +67,7 @@ class WebsiteSlides(WebsiteProfile):
def _set_completed_slide(self, slide):
# quiz use their specific mechanism to be marked as done
if slide.slide_type == 'quiz' or slide.question_ids:
if slide.slide_category == 'quiz' or slide.question_ids:
raise werkzeug.exceptions.Forbidden(_("Slide with questions must be marked as done when submitting all good answers "))
if slide.website_published and slide.channel_id.is_member:
slide.action_set_completed()
@@ -331,12 +331,12 @@ class WebsiteSlides(WebsiteProfile):
return request.render('website_slides.courses_home', values)
@http.route(['/slides/all', '/slides/all/tag/<string:slug_tags>'], type='http', auth="public", website=True, sitemap=True)
def slides_channel_all(self, slide_type=None, slug_tags=None, my=False, **post):
def slides_channel_all(self, slide_category=None, slug_tags=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 course to contain at
least one slide of type 'slide_type'. Used notably to display courses
:param string slide_category: if provided, filter the course to contain at
least one slide of type 'slide_category'. Used notably to display courses
with certifications;
:param string slug_tags: if provided, filter the slide.channels having
the tag(s) (in comma separated slugified form);
@@ -347,6 +347,10 @@ class WebsiteSlides(WebsiteProfile):
* ``search``: filter on course description / name;
"""
# retro-compatibility for older links, 'slide_category' field was previously named 'slide_type'
# can be safely removed after 15.3 (I swear though, don't be afraid, remove it!)
slide_category = slide_category or post.get('slide_type')
if slug_tags and request.httprequest.method == 'GET':
# Redirect `tag-1,tag-2` to `tag-1` to disallow multi tags
# in GET request for proper bot indexation;
@@ -355,7 +359,7 @@ class WebsiteSlides(WebsiteProfile):
# request and so clearly it's not SEO bot.
tag_list = slug_tags.split(',')
if len(tag_list) > 1 and not post.get('search'):
url = QueryURL('/slides/all', ['tag'], tag=tag_list[0], my=my, slide_type=slide_type)()
url = QueryURL('/slides/all', ['tag'], tag=tag_list[0], my=my, slide_category=slide_category)()
return request.redirect(url, code=302)
options = {
@@ -367,7 +371,7 @@ class WebsiteSlides(WebsiteProfile):
'allowFuzzy': not post.get('noFuzzy'),
'my': my,
'tag': slug_tags or post.get('tag'),
'slide_type': slide_type,
'slide_category': slide_category,
}
search = post.get('search')
order = self._channel_order_by_criterion.get(post.get('sorting'))
@@ -390,7 +394,7 @@ class WebsiteSlides(WebsiteProfile):
'tag_groups': tag_groups,
'search_term': fuzzy_search_term or search,
'original_search': fuzzy_search_term and search,
'search_slide_type': slide_type,
'search_slide_category': slide_category,
'search_my': my,
'search_tags': search_tags,
'top3_users': self._get_top3_users(),
@@ -416,15 +420,20 @@ class WebsiteSlides(WebsiteProfile):
'/slides/<model("slide.channel"):channel>/category/<model("slide.slide"):category>',
'/slides/<model("slide.channel"):channel>/category/<model("slide.slide"):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, uncategorized=False, sorting=None, search=None, **kw):
def channel(self, channel, category=None, tag=None, page=1, slide_category=None, uncategorized=False, sorting=None, search=None, **kw):
"""
Will return all necessary data to display the requested slide_channel along with a possible category.
"""
# retro-compatibility for older links, 'slide_category' field was previously named 'slide_type'
# can be safely removed after 15.3 (I swear though, don't be afraid, remove it!)
slide_category = slide_category or kw.get('slide_type')
domain = self._get_channel_slides_base_domain(channel)
pager_url = "/slides/%s" % (channel.id)
pager_args = {}
slide_types = dict(request.env['slide.slide']._fields['slide_type']._description_selection(request.env))
slide_categories = dict(request.env['slide.slide']._fields['slide_category']._description_selection(request.env))
if search:
domain += [
@@ -443,9 +452,9 @@ class WebsiteSlides(WebsiteProfile):
if uncategorized:
domain += [('category_id', '=', False)]
pager_args['uncategorized'] = 1
elif slide_type:
domain += [('slide_type', '=', slide_type)]
pager_url += "?slide_type=%s" % slide_type
elif slide_category:
domain += [('slide_category', '=', slide_category)]
pager_url += "?slide_category=%s" % slide_category
# sorting criterion
if channel.channel_type == 'documentation':
@@ -467,8 +476,8 @@ class WebsiteSlides(WebsiteProfile):
query_string = "?search_category=%s" % category.id
elif tag:
query_string = "?search_tag=%s" % tag.id
elif slide_type:
query_string = "?search_slide_type=%s" % slide_type
elif slide_category:
query_string = "?search_slide_category=%s" % slide_category
elif uncategorized:
query_string = "?search_uncategorized=1"
@@ -479,10 +488,10 @@ class WebsiteSlides(WebsiteProfile):
# search
'search_category': category,
'search_tag': tag,
'search_slide_type': slide_type,
'search_slide_category': slide_category,
'search_uncategorized': uncategorized,
'query_string': query_string,
'slide_types': slide_types,
'slide_categories': slide_categories,
'sorting': actual_sorting,
'search': search,
# display data
@@ -704,8 +713,8 @@ class WebsiteSlides(WebsiteProfile):
values.update({
'search_category': slide.category_id if kwargs.get('search_category') else None,
'search_tag': request.env['slide.tag'].browse(int(kwargs.get('search_tag'))) if kwargs.get('search_tag') else None,
'slide_types': dict(request.env['slide.slide']._fields['slide_type']._description_selection(request.env)) if kwargs.get('search_slide_type') else None,
'search_slide_type': kwargs.get('search_slide_type'),
'slide_categories': dict(request.env['slide.slide']._fields['slide_category']._description_selection(request.env)) if kwargs.get('search_slide_category') else None,
'search_slide_category': kwargs.get('search_slide_category'),
'search_uncategorized': kwargs.get('search_uncategorized')
})
@@ -724,7 +733,7 @@ class WebsiteSlides(WebsiteProfile):
type='http', auth="public", website=True, sitemap=False)
def slide_get_pdf_content(self, slide):
response = werkzeug.wrappers.Response()
response.data = slide.datas and base64.b64decode(slide.datas) or b''
response.data = slide.binary_content and base64.b64decode(slide.binary_content) or b''
response.mimetype = 'application/pdf'
return response
@@ -1064,8 +1073,8 @@ class WebsiteSlides(WebsiteProfile):
@http.route(['/slides/add_slide'], type='json', auth='user', methods=['POST'], website=True)
def create_slide(self, *args, **post):
# check the size only when we upload a file.
if post.get('datas'):
file_size = len(post['datas']) * 3 / 4 # base64
if post.get('binary_content'):
file_size = len(post['binary_content']) * 3 / 4 # base64
if (file_size / 1024.0 / 1024.0) > 25:
return {'error': _('File is too big. File size cannot exceed 25MB')}
@@ -1117,9 +1126,9 @@ class WebsiteSlides(WebsiteProfile):
channel._resequence_slides(slide, force_category=category)
redirect_url = "/slides/slide/%s" % (slide.id)
if channel.channel_type == "training" and not slide.slide_type == "webpage":
if channel.channel_type == "training" and not slide.slide_category == "webpage":
redirect_url = "/slides/%s" % (slug(channel))
if slide.slide_type == 'webpage':
if slide.slide_category == 'webpage':
redirect_url += "?enable_editor=1"
return {
'url': redirect_url,
@@ -1129,8 +1138,8 @@ class WebsiteSlides(WebsiteProfile):
}
def _get_valid_slide_post_values(self):
return ['name', 'url', 'tag_ids', 'slide_type', 'channel_id', 'is_preview',
'mime_type', 'datas', 'description', 'image_1920', 'is_published']
return ['name', 'url', 'tag_ids', 'slide_category', 'channel_id', 'is_preview',
'mime_type', 'binary_content', 'description', 'image_1920', 'is_published']
@http.route(['/slides/tag/search_read'], type='json', auth='user', methods=['POST'], website=True)
def slide_tag_search_read(self, fields, domain):
@@ -4,7 +4,7 @@
<record id="slide_template_published" model="mail.template">
<field name="name">Slide Published</field>
<field name="model_id" ref="model_slide_slide"/>
<field name="subject">New {{ object.slide_type }} published on {{ object.channel_id.name }}</field>
<field name="subject">New {{ object.slide_category }} published on {{ object.channel_id.name }}</field>
<field name="body_html" type="html">
<div style="margin: 0px; padding: 0px;">
<p style="margin: 0px; padding: 0px; font-size: 13px;">
@@ -36,14 +36,14 @@
<record id="slide_template_shared" model="mail.template">
<field name="name">Slide Shared</field>
<field name="model_id" ref="model_slide_slide"/>
<field name="subject">{{ user.name }} shared a {{ object.slide_type }} with you!</field>
<field name="subject">{{ user.name }} shared a {{ object.slide_category }} with you!</field>
<field name="email_from">{{ user.email_formatted }}</field>
<field name="email_to">{{ ctx.get('email', '') }}</field>
<field name="body_html" type="html">
<div style="margin: 0px; padding: 0px;">
<p style="margin: 0px; padding: 0px; font-size: 13px;">
Hello<br/><br/>
<t t-out="user.name or ''">Mitchell Admin</t> shared the <t t-out="object.slide_type or ''">document</t> <strong t-out="object.name or ''">Trees</strong> with you!
<t t-out="user.name or ''">Mitchell Admin</t> shared the <t t-out="object.slide_category or ''">document</t> <strong t-out="object.name or ''">Trees</strong> with you!
<div style="margin: 16px 8px 16px 8px; text-align: center;">
<a t-att-href="(object.website_url + '?fullscreen=1') if ctx.get('fullscreen') else object.website_url">
<img t-att-alt="object.name" t-attf-src="{{ ctx.get('base_url') }}/web/image/slide.slide/{{ object.id }}/image_1024" style="height:auto; width:150px; margin: 16px;"/>
+34 -34
View File
@@ -6,9 +6,9 @@
<record id="slide_slide_demo_0_0" model="slide.slide">
<field name="name">Gardening: The Know-How</field>
<field name="sequence">1</field>
<field name="datas" type="base64" file="website_slides/static/src/img/presentation.pdf"/>
<field name="binary_content" type="base64" file="website_slides/static/src/img/presentation.pdf"/>
<field name="image_1920" type="base64" file="website_slides/static/src/img/channel-training-default.jpg"/>
<field name="slide_type">presentation</field>
<field name="slide_category">presentation</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_0_gard_0"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
@@ -29,7 +29,7 @@
<field name="name">Home Gardening</field>
<field name="sequence">2</field>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_gardening_1.jpg"/>
<field name="slide_type">infographic</field>
<field name="slide_category">infographic</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_0_gard_0"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
@@ -43,7 +43,7 @@
<field name="name">Mighty Carrots</field>
<field name="sequence">3</field>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_gardening_2.jpg"/>
<field name="slide_type">infographic</field>
<field name="slide_category">infographic</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_0_gard_0"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
@@ -56,9 +56,9 @@
<record id="slide_slide_demo_0_3" model="slide.slide">
<field name="name">How to Grow and Harvest The Best Strawberries | Basics</field>
<field name="sequence">4</field>
<field name="datas" type="base64" file="website_slides/static/src/img/presentation.pdf"/>
<field name="binary_content" type="base64" file="website_slides/static/src/img/presentation.pdf"/>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_thumb_l0JZ25VvbwE.jpg"/>
<field name="slide_type">document</field>
<field name="slide_category">document</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_0_gard_0"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
@@ -72,7 +72,7 @@
<field name="name">Test your knowledge</field>
<field name="sequence">5</field>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_owl.jpg"/>
<field name="slide_type">quiz</field>
<field name="slide_category">quiz</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_0_gard_0"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
@@ -149,7 +149,7 @@
<field name="name">Tree Infographic</field>
<field name="sequence">1</field>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_tree_infographic_1.jpg"/>
<field name="slide_type">infographic</field>
<field name="slide_category">infographic</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_1_gard1"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
@@ -163,7 +163,7 @@
<field name="name">Interesting Tree Facts</field>
<field name="sequence">2</field>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_tree_infographic_2.jpg"/>
<field name="slide_type">infographic</field>
<field name="slide_category">infographic</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_1_gard1"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
@@ -177,7 +177,7 @@
<field name="name">Energy Efficiency Facts</field>
<field name="sequence">3</field>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_tree_infographic_3.jpg"/>
<field name="slide_type">infographic</field>
<field name="slide_category">infographic</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_1_gard1"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
@@ -212,7 +212,7 @@
<field name="url">https://www.youtube.com/watch?v=QYmgrw0PgLU</field>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_thumb_QYmgrw0PgLU.jpg"/>
<field name="document_id">QYmgrw0PgLU</field>
<field name="slide_type">video</field>
<field name="slide_category">video</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_1_gard1"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
@@ -226,7 +226,7 @@
<field name="name">A little chat with Harry Potted</field>
<field name="sequence">6</field>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_tree_img_1.jpg"/>
<field name="slide_type">webpage</field>
<field name="slide_category">webpage</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_1_gard1"/>
<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;" data-snippet="s_cover">
@@ -327,9 +327,9 @@
<record id="slide_slide_demo_1_5" model="slide.slide">
<field name="name">3 Main Methodologies</field>
<field name="sequence">6</field>
<field name="datas" type="base64" file="website_slides/static/src/img/presentation.pdf"/>
<field name="binary_content" type="base64" file="website_slides/static/src/img/presentation.pdf"/>
<field name="image_1920" type="base64" file="website_slides/static/src/img/channel-training-default.jpg"/>
<field name="slide_type">presentation</field>
<field name="slide_category">presentation</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_1_gard1"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
@@ -345,7 +345,7 @@
<field name="url">https://www.youtube.com/watch?v=l0JZ25VvbwE</field>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_thumb_l0JZ25VvbwE.jpg"/>
<field name="document_id">l0JZ25VvbwE</field>
<field name="slide_type">video</field>
<field name="slide_category">video</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_1_gard1"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
@@ -378,9 +378,9 @@
<record id="slide_slide_demo_2_0" model="slide.slide">
<field name="name">Main Trees Categories</field>
<field name="sequence">1</field>
<field name="datas" type="base64" file="website_slides/static/src/img/presentation.pdf"/>
<field name="binary_content" type="base64" file="website_slides/static/src/img/presentation.pdf"/>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_tree_img_2.jpg"/>
<field name="slide_type">presentation</field>
<field name="slide_category">presentation</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_2_gard2"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
@@ -464,7 +464,7 @@
<field name="name">A Mighty Forest from Ages</field>
<field name="sequence">2</field>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_tree_img_3.jpg"/>
<field name="slide_type">webpage</field>
<field name="slide_category">webpage</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_2_gard2"/>
<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;" data-snippet="s_cover">
@@ -513,7 +513,7 @@
<field name="url">https://www.youtube.com/watch?v=ebBez6bcSEc</field>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_thumb_ebBez6bcSEc.jpg"/>
<field name="document_id">ebBez6bcSEc</field>
<field name="slide_type">video</field>
<field name="slide_category">video</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_2_gard2"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
@@ -527,9 +527,9 @@
<record id="slide_slide_demo_2_3" model="slide.slide">
<field name="name">Wood Characteristics</field>
<field name="sequence">5</field>
<field name="datas" type="base64" file="website_slides/static/src/img/presentation.pdf"/>
<field name="binary_content" type="base64" file="website_slides/static/src/img/presentation.pdf"/>
<field name="image_1920" type="base64" file="website_slides/static/src/img/channel-documentation-default.jpg"/>
<field name="slide_type">presentation</field>
<field name="slide_category">presentation</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_2_gard2"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
@@ -561,7 +561,7 @@
<field name="name">Comparing Hardness of Wood Species</field>
<field name="sequence">2</field>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_wood_infographic_1.jpg"/>
<field name="slide_type">infographic</field>
<field name="slide_category">infographic</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_3_furn0"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
@@ -577,7 +577,7 @@
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_thumb_PYr1rK8pS30.jpg"/>
<field name="url">https://www.youtube.com/watch?v=PYr1rK8pS30</field>
<field name="document_id">PYr1rK8pS30</field>
<field name="slide_type">video</field>
<field name="slide_category">video</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_3_furn0"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
@@ -609,7 +609,7 @@
<field name="name">Foreword</field>
<field name="sequence">1</field>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_furniture_2.jpg"/>
<field name="slide_type">webpage</field>
<field name="slide_category">webpage</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_4_furn1"/>
<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;" data-snippet="s_cover">
@@ -656,7 +656,7 @@
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_thumb_bvSe6r5BpaY.jpg"/>
<field name="url">https://www.youtube.com/watch?v=bvSe6r5BpaY</field>
<field name="document_id">bvSe6r5BpaY</field>
<field name="slide_type">video</field>
<field name="slide_category">video</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_4_furn1"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=3)"/>
@@ -670,7 +670,7 @@
<field name="name">Drawing 1</field>
<field name="sequence">11</field>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_furniture_img.jpg"/>
<field name="slide_type">infographic</field>
<field name="slide_category">infographic</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_4_furn1"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=4)"/>
@@ -684,7 +684,7 @@
<field name="name">Drawing 2</field>
<field name="sequence">12</field>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_furniture_img_1.jpg"/>
<field name="slide_type">infographic</field>
<field name="slide_category">infographic</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_4_furn1"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=4)"/>
@@ -697,9 +697,9 @@
<record id="slide_slide_demo_4_12" model="slide.slide">
<field name="name">Presentation</field>
<field name="sequence">13</field>
<field name="datas" type="base64" file="website_slides/static/src/img/presentation.pdf"/>
<field name="binary_content" type="base64" file="website_slides/static/src/img/presentation.pdf"/>
<field name="image_1920" type="base64" file="website_slides/static/src/img/channel-documentation-default.jpg"/>
<field name="slide_type">document</field>
<field name="slide_category">document</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_4_furn1"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=4)"/>
@@ -739,9 +739,9 @@
<record id="slide_slide_demo_5_0" model="slide.slide">
<field name="name">Unforgettable Tools</field>
<field name="sequence">1</field>
<field name="datas" type="base64" file="website_slides/static/src/img/presentation.pdf"/>
<field name="binary_content" type="base64" file="website_slides/static/src/img/presentation.pdf"/>
<field name="image_1920" type="base64" file="website_slides/static/src/img/channel-training-default.jpg"/>
<field name="slide_type">presentation</field>
<field name="slide_category">presentation</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_5_furn2"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
@@ -769,7 +769,7 @@
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_thumb_5WMqwTnZ-qs.jpg"/>
<field name="url">https://www.youtube.com/watch?v=5WMqwTnZ-qs</field>
<field name="document_id">5WMqwTnZ-qs</field>
<field name="slide_type">video</field>
<field name="slide_category">video</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_5_furn2"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
@@ -784,7 +784,7 @@
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_thumb_grrXe1QZNzQ.jpg"/>
<field name="url">https://www.youtube.com/watch?v=grrXe1QZNzQ</field>
<field name="document_id">grrXe1QZNzQ</field>
<field name="slide_type">video</field>
<field name="slide_category">video</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_5_furn2"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
@@ -797,7 +797,7 @@
<field name="name">Test your knowledge !</field>
<field name="sequence">6</field>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_owl.jpg"/>
<field name="slide_type">quiz</field>
<field name="slide_category">quiz</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_5_furn2"/>
<field name="is_published" eval="True"/>
<field name="date_published" eval="datetime.now() - timedelta(days=8)"/>
+18 -18
View File
@@ -300,18 +300,18 @@ class Channel(models.Model):
channel.slide_category_ids = channel.slide_ids.filtered(lambda slide: slide.is_category)
channel.slide_content_ids = channel.slide_ids - channel.slide_category_ids
@api.depends('slide_ids.slide_type', 'slide_ids.is_published', 'slide_ids.completion_time',
@api.depends('slide_ids.slide_category', 'slide_ids.is_published', 'slide_ids.completion_time',
'slide_ids.likes', 'slide_ids.dislikes', 'slide_ids.total_views', 'slide_ids.is_category', 'slide_ids.active')
def _compute_slides_statistics(self):
default_vals = dict(total_views=0, total_votes=0, total_time=0, total_slides=0)
keys = ['nbr_%s' % slide_type for slide_type in self.env['slide.slide']._fields['slide_type'].get_values(self.env)]
keys = ['nbr_%s' % slide_category for slide_category in self.env['slide.slide']._fields['slide_category'].get_values(self.env)]
default_vals.update(dict((key, 0) for key in keys))
result = dict((cid, dict(default_vals)) for cid in self.ids)
read_group_res = self.env['slide.slide'].read_group(
[('active', '=', True), ('is_published', '=', True), ('channel_id', 'in', self.ids), ('is_category', '=', False)],
['channel_id', 'slide_type', 'likes', 'dislikes', 'total_views', 'completion_time'],
groupby=['channel_id', 'slide_type'],
['channel_id', 'slide_category', 'likes', 'dislikes', 'total_views', 'completion_time'],
groupby=['channel_id', 'slide_category'],
lazy=False)
for res_group in read_group_res:
cid = res_group['channel_id'][0]
@@ -320,25 +320,25 @@ class Channel(models.Model):
result[cid]['total_votes'] -= res_group.get('dislikes', 0)
result[cid]['total_time'] += res_group.get('completion_time', 0)
type_stats = self._compute_slides_statistics_type(read_group_res)
for cid, cdata in type_stats.items():
category_stats = self._compute_slides_statistics_category(read_group_res)
for cid, cdata in category_stats.items():
result[cid].update(cdata)
for record in self:
record.update(result.get(record.id, default_vals))
def _compute_slides_statistics_type(self, read_group_res):
""" Compute statistics based on all existing slide types """
slide_types = self.env['slide.slide']._fields['slide_type'].get_values(self.env)
keys = ['nbr_%s' % slide_type for slide_type in slide_types]
def _compute_slides_statistics_category(self, read_group_res):
""" Compute statistics based on all existing slide categories """
slide_categories = self.env['slide.slide']._fields['slide_category'].get_values(self.env)
keys = ['nbr_%s' % slide_category for slide_category in slide_categories]
result = dict((cid, dict((key, 0) for key in keys + ['total_slides'])) for cid in self.ids)
for res_group in read_group_res:
cid = res_group['channel_id'][0]
slide_type = res_group.get('slide_type')
if slide_type:
slide_type_count = res_group.get('__count', 0)
result[cid]['nbr_%s' % slide_type] = slide_type_count
result[cid]['total_slides'] += slide_type_count
slide_category = res_group.get('slide_category')
if slide_category:
slide_category_count = res_group.get('__count', 0)
result[cid]['nbr_%s' % slide_category] = slide_category_count
result[cid]['total_slides'] += slide_category_count
return result
def _compute_rating_stats(self):
@@ -847,7 +847,7 @@ class Channel(models.Model):
with_date = options['displayDetail']
my = options.get('my')
search_tags = options.get('tag')
slide_type = options.get('slide_type')
slide_category = options.get('slide_category')
domain = [website.website_domain()]
if my:
domain.append([('partner_ids', '=', self.env.user.partner_id.id)])
@@ -865,8 +865,8 @@ class Channel(models.Model):
# OR inside a group, AND between groups.
for group in grouped_tags:
domain.append([('tag_ids', 'in', [tag.id for tag in grouped_tags[group]])])
if slide_type and 'nbr_%s' % slide_type in self:
domain.append([('nbr_%s' % slide_type, '>', 0)])
if slide_category and 'nbr_%s' % slide_category in self:
domain.append([('nbr_%s' % slide_category, '>', 0)])
search_fields = ['name']
fetch_fields = ['name', 'website_url']
mapping = {
+46 -46
View File
@@ -118,23 +118,23 @@ class Slide(models.Model):
quiz_third_attempt_reward = fields.Integer("Reward: third attempt", default=5,)
quiz_fourth_attempt_reward = fields.Integer("Reward: every attempt after the third try", default=2)
# content
slide_type = fields.Selection([
slide_category = fields.Selection([
('infographic', 'Infographic'),
('webpage', 'Web Page'),
('presentation', 'Presentation'),
('document', 'Document'),
('video', 'Video'),
('quiz', "Quiz")],
string='Type', required=True,
string='Category', required=True,
default='document',
help="The document type will be set automatically based on the document URL and properties (e.g. height and width for presentation and document).")
datas = fields.Binary('File', attachment=True)
help="The document category will be set automatically based on the document URL and properties (e.g. height and width for presentation and document).")
binary_content = fields.Binary('File', attachment=True)
url = fields.Char('Document URL', help="Youtube or Google Document URL")
document_id = fields.Char('Document ID', help="Youtube or Google Document ID")
slide_resource_ids = fields.One2many('slide.slide.resource', 'slide_id', string="Additional Resource for this slide")
slide_resource_downloadable = fields.Boolean('Allow Download', default=True, help="Allow the user to download the content of the slide.")
mime_type = fields.Char('Mime-type')
html_content = fields.Html("HTML Content", help="Custom HTML content for slides of type 'Web Page'.", translate=True, sanitize_attributes=False, sanitize_form=False)
html_content = fields.Html("HTML Content", help="Custom HTML content for slides of category 'Web Page'.", translate=True, sanitize_attributes=False, sanitize_form=False)
# website
website_id = fields.Many2one(related='channel_id.website_id', readonly=True)
date_published = fields.Datetime('Publish Date', readonly=True, tracking=False)
@@ -270,35 +270,35 @@ class Slide(models.Model):
for slide in self:
slide.embed_count = mapped_data.get(slide.id, 0)
@api.depends('slide_ids.sequence', 'slide_ids.slide_type', 'slide_ids.is_published', 'slide_ids.is_category')
@api.depends('slide_ids.sequence', 'slide_ids.slide_category', 'slide_ids.is_published', 'slide_ids.is_category')
def _compute_slides_statistics(self):
# Do not use dict.fromkeys(self.ids, dict()) otherwise it will use the same dictionnary for all keys.
# Therefore, when updating the dict of one key, it updates the dict of all keys.
keys = ['nbr_%s' % slide_type for slide_type in self.env['slide.slide']._fields['slide_type'].get_values(self.env)]
keys = ['nbr_%s' % slide_category for slide_category in self.env['slide.slide']._fields['slide_category'].get_values(self.env)]
default_vals = dict((key, 0) for key in keys + ['total_slides'])
res = self.env['slide.slide'].read_group(
[('is_published', '=', True), ('category_id', 'in', self.ids), ('is_category', '=', False)],
['category_id', 'slide_type'], ['category_id', 'slide_type'],
['category_id', 'slide_category'], ['category_id', 'slide_category'],
lazy=False)
type_stats = self._compute_slides_statistics_type(res)
category_stats = self._compute_slides_statistics_category(res)
for record in self:
record.update(type_stats.get(record._origin.id, default_vals))
record.update(category_stats.get(record._origin.id, default_vals))
def _compute_slides_statistics_type(self, read_group_res):
""" Compute statistics based on all existing slide types """
slide_types = self.env['slide.slide']._fields['slide_type'].get_values(self.env)
keys = ['nbr_%s' % slide_type for slide_type in slide_types]
def _compute_slides_statistics_category(self, read_group_res):
""" Compute statistics based on all existing slide categories """
slide_categories = self.env['slide.slide']._fields['slide_category'].get_values(self.env)
keys = ['nbr_%s' % slide_category for slide_category in slide_categories]
result = dict((cid, dict((key, 0) for key in keys + ['total_slides'])) for cid in self.ids)
for res_group in read_group_res:
cid = res_group['category_id'][0]
slide_type = res_group.get('slide_type')
if slide_type:
slide_type_count = res_group.get('__count', 0)
result[cid]['nbr_%s' % slide_type] = slide_type_count
result[cid]['total_slides'] += slide_type_count
slide_category = res_group.get('slide_category')
if slide_category:
slide_category_count = res_group.get('__count', 0)
result[cid]['nbr_%s' % slide_category] = slide_category_count
result[cid]['total_slides'] += slide_category_count
return result
@api.depends('slide_partner_ids.partner_id')
@@ -315,7 +315,7 @@ class Slide(models.Model):
self.env['slide.slide.partner']
)
@api.depends('document_id', 'slide_type', 'mime_type')
@api.depends('document_id', 'slide_category', 'mime_type')
def _compute_embed_code(self):
base_url = request and request.httprequest.url_root
@@ -326,13 +326,13 @@ class Slide(models.Model):
base_url = record.get_base_url()
if base_url[-1] == '/':
base_url = base_url[:-1]
if record.datas and (not record.document_id or record.slide_type in ['document', 'presentation']):
if record.binary_content and (not record.document_id or record.slide_category in ['document', 'presentation']):
slide_url = base_url + url_for('/slides/embed/%s?page=1' % record.id)
slide_url_external = base_url + url_for('/slides/embed_external/%s?page=1' % record.id)
base_embed_code = Markup('<iframe src="%s" class="o_wslides_iframe_viewer" allowFullScreen="true" height="%s" width="%s" frameborder="0"></iframe>')
record.embed_code = base_embed_code % (slide_url, 315, 420)
embed_code_external = base_embed_code % (slide_url_external, 315, 420)
elif record.slide_type == 'video' and record.document_id:
elif record.slide_category == 'video' and record.document_id:
if not record.mime_type:
# embed youtube video
query = urls.url_parse(record.url).query
@@ -359,15 +359,15 @@ class Slide(models.Model):
for key, value in values.items():
self[key] = value
@api.onchange('datas')
def _on_change_datas(self):
@api.onchange('binary_content')
def _on_change_binary_content(self):
""" For PDFs, we assume that it takes 5 minutes to read a page.
If the selected file is not a PDF, it is an image (You can
only upload PDF or Image file) then the slide_type is changed
into infographic and the uploaded dataS is transfered to the
only upload PDF or Image file) then the slide_category is changed
into infographic and the uploaded binary_content is transfered to the
image field. (It avoids the infinite loading in PDF viewer)"""
if self.datas:
data = base64.b64decode(self.datas)
if self.binary_content:
data = base64.b64decode(self.binary_content)
if data.startswith(b'%PDF-'):
pdf = PyPDF2.PdfFileReader(io.BytesIO(data), overwriteWarnings=False, strict=False)
try:
@@ -376,9 +376,9 @@ class Slide(models.Model):
return
self.completion_time = (5 * len(pdf.pages)) / 60
else:
self.slide_type = 'infographic'
self.image_1920 = self.datas
self.datas = None
self.slide_category = 'infographic'
self.image_1920 = self.binary_content
self.binary_content = None
@api.depends('name', 'channel_id.website_id.domain')
def _compute_website_url(self):
@@ -409,8 +409,8 @@ class Slide(models.Model):
# 'website_published' is handled by mixin
values['date_published'] = False
if values.get('slide_type') == 'infographic' and not values.get('image_1920'):
values['image_1920'] = values['datas']
if values.get('slide_category') == 'infographic' and not values.get('image_1920'):
values['image_1920'] = values['binary_content']
if values.get('is_category'):
values['is_preview'] = True
values['is_published'] = True
@@ -794,7 +794,7 @@ class Slide(models.Model):
if fetch_res.get('error'):
return {'error': self._extract_google_error_message(fetch_res.get('error'))}
values = {'slide_type': 'video', 'document_id': document_id}
values = {'slide_category': 'video', 'document_id': document_id}
items = fetch_res['values'].get('items')
if not items:
return {'error': _('Please enter valid Youtube or Google Doc URL')}
@@ -845,15 +845,15 @@ class Slide(models.Model):
@api.model
def _parse_google_document(self, document_id, only_preview_fields):
def get_slide_type(vals):
def get_slide_category(vals):
# TDE FIXME: WTF ??
slide_type = 'presentation'
slide_category = 'presentation'
if vals.get('image_1920'):
image = Image.open(io.BytesIO(base64.b64decode(vals['image_1920'])))
width, height = image.size
if height > width:
return 'document'
return slide_type
return slide_category
# Google drive doesn't use a simple API key to access the data, but requires an access
# token. However, this token is generated in module google_drive, which is not in the
@@ -886,18 +886,18 @@ class Slide(models.Model):
'document_id': document_id,
}
if google_values['mimeType'].startswith('video/'):
values['slide_type'] = 'video'
values['slide_category'] = 'video'
elif google_values['mimeType'].startswith('image/'):
values['datas'] = values['image_1920']
values['slide_type'] = 'infographic'
values['binary_content'] = values['image_1920']
values['slide_category'] = 'infographic'
elif google_values['mimeType'].startswith('application/vnd.google-apps'):
values['slide_type'] = get_slide_type(values)
values['slide_category'] = get_slide_category(values)
if 'exportLinks' in google_values:
values['datas'] = self._fetch_data(google_values['exportLinks']['application/pdf'], params, 'pdf')['values']
values['binary_content'] = self._fetch_data(google_values['exportLinks']['application/pdf'], params, 'pdf')['values']
elif google_values['mimeType'] == 'application/pdf':
# TODO: Google Drive PDF document doesn't provide plain text transcript
values['datas'] = self._fetch_data(google_values['webContentLink'], {}, 'pdf')['values']
values['slide_type'] = get_slide_type(values)
values['binary_content'] = self._fetch_data(google_values['webContentLink'], {}, 'pdf')['values']
values['slide_category'] = get_slide_category(values)
return {'values': values}
@@ -942,7 +942,7 @@ class Slide(models.Model):
}
def _search_render_results(self, fetch_fields, mapping, icon, limit):
icon_per_type = {
icon_per_category = {
'infographic': 'fa-file-picture-o',
'webpage': 'fa-file-text',
'presentation': 'fa-file-pdf-o',
@@ -953,7 +953,7 @@ class Slide(models.Model):
}
results_data = super()._search_render_results(fetch_fields, mapping, icon, limit)
for slide, data in zip(self, results_data):
data['_fa'] = icon_per_type.get(slide.slide_type, 'fa-file-pdf-o')
data['_fa'] = icon_per_category.get(slide.slide_category, 'fa-file-pdf-o')
data['url'] = slide.website_url
data['course'] = _('Course: %s', slide.channel_id.name)
data['course_url'] = slide.channel_id.website_url
@@ -225,7 +225,7 @@
//--------------------------------------------------------------------------
/**
* Handler called whenever the user clicks on a sub-quiz which is linked to a slide.
* This does NOT handle the case of a slide of type "quiz".
* This does NOT handle the case of a slide of category "quiz".
* By going through this handler, the widget will be able to determine that it has to render
* the associated quiz and not the main content.
*
@@ -455,7 +455,7 @@
// Private
//--------------------------------------------------------------------------
/**
* Fetches content with an rpc call for slides of type "webpage"
* Fetches content with an rpc call for slides of category "webpage"
*
* @private
*/
@@ -474,14 +474,14 @@
});
},
/**
* Fetches slide content depending on its type.
* Fetches slide content depending on its category.
* If the slide doesn't need to fetch any content, return a resolved deferred
*
* @private
*/
_fetchSlideContent: function (){
var slide = this.get('slide');
if (slide.type === 'webpage' && !slide.isQuiz) {
if (slide.category === 'webpage' && !slide.isQuiz) {
return this._fetchHtmlContent();
}
return Promise.resolve();
@@ -494,14 +494,14 @@
},
/**
* Extend the slide data list to add informations about rendering method, and other
* specific values according to their slide_type.
* specific values according to their slide_category.
*/
_preprocessSlideData: function (slidesDataList) {
slidesDataList.forEach(function (slideData, index) {
// compute hasNext slide
slideData.hasNext = index < slidesDataList.length-1;
// compute embed url
if (slideData.type === 'video') {
if (slideData.category === 'video') {
slideData.embedCode = $(slideData.embedCode).attr('src') || ""; // embedCode contains an iframe tag, where src attribute is the url (youtube or embed document from odoo)
var separator = slideData.embedCode.indexOf("?") !== -1 ? "&" : "?";
var scheme = slideData.embedCode.indexOf('//') === 0 ? 'https:' : '';
@@ -510,16 +510,16 @@
params.autoplay = 1;
}
slideData.embedUrl = slideData.embedCode ? scheme + slideData.embedCode + separator + $.param(params) : "";
} else if (slideData.type === 'infographic') {
} else if (slideData.category === 'infographic') {
slideData.embedUrl = _.str.sprintf('/web/image/slide.slide/%s/image_1024', slideData.id);
} else if (_.contains(['document', 'presentation'], slideData.type)) {
} else if (_.contains(['document', 'presentation'], slideData.category)) {
slideData.embedUrl = $(slideData.embedCode).attr('src');
}
// fill empty property to allow searching on it with _.filter(list, matcher)
slideData.isQuiz = !!slideData.isQuiz;
slideData.hasQuestion = !!slideData.hasQuestion;
// technical settings for the Fullscreen to work
slideData._autoSetDone = _.contains(['infographic', 'presentation', 'document', 'webpage'], slideData.type) && !slideData.hasQuestion;
slideData._autoSetDone = _.contains(['infographic', 'presentation', 'document', 'webpage'], slideData.category) && !slideData.hasQuestion;
});
return slidesDataList;
},
@@ -542,7 +542,7 @@
history.pushState(null, '', fullscreenUrl);
},
/**
* Render the current slide content using specific mecanism according to slide type:
* Render the current slide content using specific mecanism according to slide category:
* - simply append content (for webpage)
* - template rendering (for image, document, ....)
* - using a sub widget (quiz and video)
@@ -556,19 +556,19 @@
$content.empty();
// display quiz slide, or quiz attached to a slide
if (slide.type === 'quiz' || slide.isQuiz) {
if (slide.category === 'quiz' || slide.isQuiz) {
$content.addClass('bg-white');
var QuizWidget = new Quiz(this, slide, this.channel);
return QuizWidget.appendTo($content);
}
// render slide content
if (_.contains(['document', 'presentation', 'infographic'], slide.type)) {
if (_.contains(['document', 'presentation', 'infographic'], slide.category)) {
$content.html(QWeb.render('website.slides.fullscreen.content', {widget: this}));
} else if (slide.type === 'video') {
} else if (slide.category === 'video') {
this.videoPlayer = new VideoPlayer(this, slide);
return this.videoPlayer.appendTo($content);
} else if (slide.type === 'webpage'){
} else if (slide.category === 'webpage'){
var $wpContainer = $('<div>').addClass('o_wslide_fs_webpage_content bg-white block w-100 overflow-auto');
$(slide.htmlContent).appendTo($wpContainer);
$content.append($wpContainer);
@@ -608,7 +608,7 @@
* Triggered whenever the user changes slides.
* When the current slide is changed, widget will be automatically updated
* and allowed to: fetch the content if needed, render it, update the url,
* and set slide as "completed" according to its type requirements. In
* and set slide as "completed" according to its category requirements. In
* mobile case (i.e. limited screensize), sidebar will be toggled since
* sidebar will block most or all of new slide visibility.
*
@@ -627,7 +627,7 @@
return self._renderSlide();
}).then(function() {
if (slide._autoSetDone && !session.is_website_user) { // no useless RPC call
if (['document', 'presentation'].includes(slide.type)) {
if (['document', 'presentation'].includes(slide.category)) {
// only set the slide as completed after iFrame is loaded to avoid concurrent execution with 'embedUrl' controller
self.el.querySelector('iframe.o_wslides_iframe_viewer').addEventListener('load', () => self._setCompleted(slide.id));
} else {
@@ -18,7 +18,7 @@
* This widget is responsible of displaying quiz questions and propositions. Submitting the quiz will fetch the
* correction and decorate the answers according to the result. Error message or modal can be displayed.
*
* This widget can be attached to DOM rendered server-side by `website_slides.slide_type_quiz` or
* This widget can be attached to DOM rendered server-side by `website_slides.slide_category_quiz` or
* used client side (Fullscreen).
*
* Triggered events are :
@@ -10,7 +10,7 @@ var SlideUploadDialog = Dialog.extend({
template: 'website.slide.upload.modal',
events: _.extend({}, Dialog.prototype.events, {
'click .o_wslides_js_upload_install_button': '_onClickInstallModule',
'click .o_wslides_select_type': '_onClickSlideTypeIcon',
'click .o_wslides_select_category': '_onClickSlideCategoryIcon',
'change input#upload': '_onChangeSlideUpload',
'change input#url': '_onChangeSlideUrl',
}),
@@ -120,7 +120,7 @@ var SlideUploadDialog = Dialog.extend({
return form[0].checkValidity() && this.isValidUrl;
},
/**
* Extract values to submit from form, force the slide_type according to
* Extract values to submit from form, force the slide_category according to
* filled values.
*
* @private
@@ -136,30 +136,30 @@ var SlideUploadDialog = Dialog.extend({
'is_published': forcePublished,
}, this._getSelect2DropdownValues()); // add tags and category
// default slide_type (for webpage for instance)
if (_.contains(this.slide_type_data), this.get('state')) {
values['slide_type'] = this.get('state');
// default slide_category (for webpage for instance)
if (_.contains(this.slide_category_data), this.get('state')) {
values['slide_category'] = this.get('state');
}
if (this.file.type === 'application/pdf') {
_.extend(values, {
'image_1920': canvas.toDataURL().split(',')[1],
'slide_type': canvas.height > canvas.width ? 'document' : 'presentation',
'slide_category': canvas.height > canvas.width ? 'document' : 'presentation',
'mime_type': this.file.type,
'datas': this.file.data
'binary_content': this.file.data
});
} else if (values['slide_type'] === 'webpage') {
} else if (values['slide_category'] === 'webpage') {
_.extend(values, {
'mime_type': 'text/html',
'image_1920': this.file.type === 'image/svg+xml' ? await this._svgToPNG() : this.file.data,
});
} else if (/^image\/.*/.test(this.file.type)) {
const fileData = this.file.type === 'image/svg+xml' ? await this._svgToPNG() : this.file.data;
if (values['slide_type'] === 'presentation') {
if (values['slide_category'] === 'presentation') {
_.extend(values, {
'slide_type': 'infographic',
'slide_category': 'infographic',
'mime_type': this.file.type === 'image/svg+xml' ? 'image/png' : this.file.type,
'datas': fileData,
'binary_content': fileData,
});
} else {
_.extend(values, {
@@ -321,12 +321,12 @@ var SlideUploadDialog = Dialog.extend({
return values;
},
/**
* Init the data relative to the support slide type to upload
* Init the data relative to the support slide category to upload
*
* @private
*/
_setup: function () {
this.slide_type_data = {
this.slide_category_data = {
presentation: {
icon: 'fa-file-pdf-o',
label: _t('Presentation'),
@@ -393,7 +393,7 @@ var SlideUploadDialog = Dialog.extend({
} else if (currentType === '_import') {
tmpl = 'website.slide.upload.modal.import';
} else {
tmpl = this.slide_type_data[currentType]['template'];
tmpl = this.slide_category_data[currentType]['template'];
this.$modal.find('.modal-dialog').addClass('modal-lg');
}
this.$('.o_w_slide_upload_modal_container').empty();
@@ -623,10 +623,10 @@ var SlideUploadDialog = Dialog.extend({
}
},
_onClickSlideTypeIcon: function (ev) {
_onClickSlideCategoryIcon: function (ev) {
var $elem = this.$(ev.currentTarget);
var slideType = $elem.data('slideType');
this.set('state', slideType);
var slideCategory = $elem.data('slideCategory');
this.set('state', slideCategory);
this._bindSelect2Dropdown(); // rebind select2 at each modal body rendering
},
@@ -45,7 +45,7 @@ tour.register('slides_tour', {
content: Markup(_t("Your first section is created, now it's time to add lessons to your course. Click on <b>Add Content</b> to upload a document, create a web page or link a video.")),
position: 'bottom',
}, {
trigger: 'a[data-slide-type="presentation"]',
trigger: 'a[data-slide-category="presentation"]',
content: Markup(_t("First, let's add a <b>Presentation</b>. It can be a .pdf or an image.")),
position: 'bottom',
}, {
@@ -29,7 +29,7 @@ var addVideoToSection = function (sectionName, saveAsDraft) {
trigger: 'div.o_wslides_slide_list_category_header:contains("' + sectionName + '") a:contains("Add Content")',
}, {
content: 'eLearning: click on video',
trigger: 'a[data-slide-type=video]',
trigger: 'a[data-slide-category=video]',
}, {
content: 'eLearning: fill video link',
trigger: 'input[name=url]',
@@ -63,7 +63,7 @@ var addWebPageToSection = function (sectionName, pageName) {
trigger: 'div.o_wslides_slide_list_category_header:contains("' + sectionName + '") a:contains("Add Content")',
}, {
content: 'eLearning: click on webpage',
trigger: 'a[data-slide-type=webpage]',
trigger: 'a[data-slide-category=webpage]',
}, {
content: 'eLearning: fill webpage title',
trigger: 'input[name=name]',
@@ -1,12 +1,12 @@
<templates id="template" xml:space="preserve">
<t t-name="website.slides.fullscreen.content">
<t t-if="_.contains(['document', 'presentation'], widget.get('slide').type)">
<t t-if="_.contains(['document', 'presentation'], widget.get('slide').category)">
<div class="embed-responsive h-100">
<iframe t-att-src="widget.get('slide').embedUrl" class="o_wslides_iframe_viewer" allowFullScreen="true" frameborder="0"/>
</div>
</t>
<t t-if="widget.get('slide').type === 'infographic'">
<t t-if="widget.get('slide').category === 'infographic'">
<div class="o_wslides_fs_player w-100 h-100 overflow-auto d-flex align-items-start justify-content-center">
<img t-att-src="'/web/image/slide.slide/'+ widget.get('slide').id +'/image_1024'" class="img-fluid position-relative m-auto" alt="Slide image"/>
</div>
@@ -52,7 +52,7 @@
</form>
</div>
<div t-if="widget.session.is_website_user" class="alert alert-info d-inline-block">
<p class="mb-0">Please <a t-attf-href="/web?redirect=#{window.location.href}" class="font-weight-bold"> login </a> to share this <t t-esc="widget.slide.type"/> by email.</p>
<p class="mb-0">Please <a t-attf-href="/web?redirect=#{window.location.href}" class="font-weight-bold"> login </a> to share this <t t-esc="widget.slide.category"/> by email.</p>
</div>
</t>
@@ -10,17 +10,17 @@
</t>
<!--
Slide Type Selection template
Slide Category Selection template
-->
<t t-name="website.slide.upload.modal.select">
<div class="row p-1 mt-4 mb-2">
<div t-foreach="widget.slide_type_data" t-as="slide_type" class="col-6 col-md-3">
<t t-set="type_data" t-value="widget.slide_type_data[slide_type]"/>
<div t-foreach="widget.slide_category_data" t-as="slide_category" class="col-6 col-md-3">
<t t-set="category_data" t-value="widget.slide_category_data[slide_category]"/>
<a href="#" t-att-data-slide-type="slide_type"
class="content-type d-flex flex-column align-items-center mb-4 o_wslides_select_type btn rounded border text-600 p-3">
<i t-attf-class="fa #{type_data['icon']} mb-2 fa-3x"/>
<t t-esc="type_data['label']"/>
<a href="#" t-att-data-slide-category="slide_category"
class="content-type d-flex flex-column align-items-center mb-4 o_wslides_select_category btn rounded border text-600 p-3">
<i t-attf-class="fa #{category_data['icon']} mb-2 fa-3x"/>
<t t-esc="category_data['label']"/>
</a>
</div>
</div>
@@ -55,7 +55,7 @@
</t>
<!--
Slide Type common form part template
Slide Category common form part template
-->
<t t-name="website.slide.upload.modal.common">
<div class="form-group">
@@ -82,7 +82,7 @@
</t>
<!--
Slide Type templates
Slide Category templates
-->
<t t-name="website.slide.upload.modal.presentation">
<div>
+3 -3
View File
@@ -55,7 +55,7 @@ class SlidesCase(common.TransactionCase):
cls.slide = cls.env['slide.slide'].with_user(cls.user_officer).create({
'name': 'How To Cook Humans',
'channel_id': cls.channel.id,
'slide_type': 'presentation',
'slide_category': 'presentation',
'is_published': True,
'completion_time': 2.0,
'sequence': 1,
@@ -70,7 +70,7 @@ class SlidesCase(common.TransactionCase):
cls.slide_2 = cls.env['slide.slide'].with_user(cls.user_officer).create({
'name': 'How To Cook For Humans',
'channel_id': cls.channel.id,
'slide_type': 'presentation',
'slide_category': 'presentation',
'is_published': True,
'completion_time': 3.0,
'sequence': 3,
@@ -78,7 +78,7 @@ class SlidesCase(common.TransactionCase):
cls.slide_3 = cls.env['slide.slide'].with_user(cls.user_officer).create({
'name': 'How To Cook Humans For Humans',
'channel_id': cls.channel.id,
'slide_type': 'document',
'slide_category': 'document',
'is_published': True,
'completion_time': 1.5,
'sequence': 4,
+2 -2
View File
@@ -28,14 +28,14 @@ class TestKarmaGain(common.SlidesCase):
self.slide_2_0 = self.env['slide.slide'].with_user(self.user_officer).create({
'name': 'How to travel through space and time',
'channel_id': self.channel_2.id,
'slide_type': 'presentation',
'slide_category': 'presentation',
'is_published': True,
'completion_time': 2.0,
})
self.slide_2_1 = self.env['slide.slide'].with_user(self.user_officer).create({
'name': 'How to duplicate yourself',
'channel_id': self.channel_2.id,
'slide_type': 'presentation',
'slide_category': 'presentation',
'is_published': True,
'completion_time': 2.0,
})
@@ -39,12 +39,12 @@ class TestChannelStatistics(common.SlidesCase):
@mute_logger('odoo.models')
def test_channel_statistics(self):
channel_publisher = self.channel.with_user(self.user_officer)
# slide type computation
# slide category computation
self.assertEqual(channel_publisher.total_slides, len(channel_publisher.slide_content_ids))
self.assertEqual(channel_publisher.nbr_infographic, len(channel_publisher.slide_content_ids.filtered(lambda s: s.slide_type == 'infographic')))
self.assertEqual(channel_publisher.nbr_presentation, len(channel_publisher.slide_content_ids.filtered(lambda s: s.slide_type == 'presentation')))
self.assertEqual(channel_publisher.nbr_document, len(channel_publisher.slide_content_ids.filtered(lambda s: s.slide_type == 'document')))
self.assertEqual(channel_publisher.nbr_video, len(channel_publisher.slide_content_ids.filtered(lambda s: s.slide_type == 'video')))
self.assertEqual(channel_publisher.nbr_infographic, len(channel_publisher.slide_content_ids.filtered(lambda s: s.slide_category == 'infographic')))
self.assertEqual(channel_publisher.nbr_presentation, len(channel_publisher.slide_content_ids.filtered(lambda s: s.slide_category == 'presentation')))
self.assertEqual(channel_publisher.nbr_document, len(channel_publisher.slide_content_ids.filtered(lambda s: s.slide_category == 'document')))
self.assertEqual(channel_publisher.nbr_video, len(channel_publisher.slide_content_ids.filtered(lambda s: s.slide_category == 'video')))
# slide statistics computation
self.assertEqual(float_compare(channel_publisher.total_time, sum(s.completion_time for s in channel_publisher.slide_content_ids), 3), 0)
# members computation
@@ -156,14 +156,14 @@ class TestSlideStatistics(common.SlidesCase):
self.assertEqual(slide_emp.total_views, 5)
@users('user_officer')
def test_slide_statistics_types(self):
def test_slide_statistics_categories(self):
category = self.category.with_user(self.env.user)
self.assertEqual(
category.nbr_presentation,
len(category.channel_id.slide_ids.filtered(lambda s: s.category_id == category and s.slide_type == 'presentation')))
len(category.channel_id.slide_ids.filtered(lambda s: s.category_id == category and s.slide_category == 'presentation')))
self.assertEqual(
category.nbr_document,
len(category.channel_id.slide_ids.filtered(lambda s: s.category_id == category and s.slide_type == 'document')))
len(category.channel_id.slide_ids.filtered(lambda s: s.category_id == category and s.slide_category == 'document')))
self.assertEqual(self.channel.total_slides, 3, 'The channel should contain 3 slides')
self.assertEqual(category.total_slides, 2, 'The first category should contain 2 slides')
@@ -33,32 +33,32 @@ class TestUICommon(HttpCaseWithUserDemo, HttpCaseWithUserPortal):
(0, 0, {
'name': 'Gardening: The Know-How',
'sequence': 1,
'datas': pdf_content,
'slide_type': 'presentation',
'binary_content': pdf_content,
'slide_category': 'presentation',
'is_published': True,
'is_preview': True,
}), (0, 0, {
'name': 'Home Gardening',
'sequence': 2,
'image_1920': img_content,
'slide_type': 'infographic',
'slide_category': 'infographic',
'is_published': True,
}), (0, 0, {
'name': 'Mighty Carrots',
'sequence': 3,
'image_1920': img_content,
'slide_type': 'infographic',
'slide_category': 'infographic',
'is_published': True,
}), (0, 0, {
'name': 'How to Grow and Harvest The Best Strawberries | Basics',
'sequence': 4,
'datas': pdf_content,
'slide_type': 'document',
'binary_content': pdf_content,
'slide_category': 'document',
'is_published': True,
}), (0, 0, {
'name': 'Test your knowledge',
'sequence': 5,
'slide_type': 'quiz',
'slide_category': 'quiz',
'is_published': True,
'question_ids': [
(0, 0, {
@@ -77,9 +77,9 @@
<tree decoration-bf="is_category" editable="bottom">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="slide_type" attrs="{'invisible': [('slide_type', '=', 'category')]}"/>
<field name="completion_time" attrs="{'invisible': [('slide_type', '=', 'category')]}" string="Duration" widget="float_time"/>
<field name="total_views" attrs="{'invisible': [('slide_type', '=', 'category')]}"/>
<field name="slide_category" attrs="{'invisible': [('slide_category', '=', 'category')]}"/>
<field name="completion_time" attrs="{'invisible': [('slide_category', '=', 'category')]}" string="Duration" widget="float_time"/>
<field name="total_views" attrs="{'invisible': [('slide_category', '=', 'category')]}"/>
<field name="is_preview" string="Preview"/>
<field name="is_published" string="Published"/>
<field name="is_category" invisible="1"/>
@@ -116,14 +116,14 @@
<group name="lesson_details">
<field name="active" invisible="1"/>
<field name="channel_id"/>
<field name="slide_type" string="Content Type"/>
<field name="slide_category" string="Content Type"/>
<field name="url" attrs="{
'required': [('slide_type', 'in', ('video'))],
'invisible': [('slide_type', 'not in', ('video'))]}" />
'required': [('slide_category', 'in', ('video'))],
'invisible': [('slide_category', 'not in', ('video'))]}" />
<field name="document_id" invisible="1"/>
<field name="mime_type" force_save="1" readonly="1" groups="base.group_no_one"/>
<field name="datas" string="Attachment"
attrs="{'invisible': [('slide_type', 'not in', ('document', 'presentation'))]}"/>
<field name="binary_content" string="Attachment"
attrs="{'invisible': [('slide_category', 'not in', ('document', 'presentation'))]}"/>
</group>
<group name="related_details">
<field name="user_id" string="Responsible" widget="many2one_avatar"/>
@@ -133,7 +133,7 @@
<span> hours</span>
</div>
<field name="is_preview"/>
<field name="slide_resource_downloadable" attrs="{'invisible': [('slide_type', 'not in', ['presentation', 'document'])]}"/>
<field name="slide_resource_downloadable" attrs="{'invisible': [('slide_category', 'not in', ['presentation', 'document'])]}"/>
<field name="date_published" string="Published Date" attrs="{'invisible': [('date_published', '=', False)]}" groups="base.group_no_one"/>
</group>
</group>
@@ -211,7 +211,7 @@
sample="1">
<field name="id"/>
<field name="channel_id"/>
<field name="slide_type"/>
<field name="slide_category"/>
<field name="user_id"/>
<field name="image_128"/>
<templates>
@@ -242,20 +242,20 @@
</div>
<div class="o_kanban_record_bottom mt-auto d-flex justify-content-between align-items-end">
<span>
<t t-if="record.slide_type.raw_value == 'infographic'">
<t t-if="record.slide_category.raw_value == 'infographic'">
<i class="fa fa-file-image-o mr-2" aria-label="Infographic" role="img" title="Infographic"/>
</t>
<t t-elif="record.slide_type.raw_value == 'webpage'">
<t t-elif="record.slide_category.raw_value == 'webpage'">
<i class="fa fa-file-code-o mr-2" aria-label="Webpage" role="img" title="Webpage"/>
</t>
<t t-elif="record.slide_type.raw_value == 'video'">
<t t-elif="record.slide_category.raw_value == 'video'">
<i class="fa fa-file-video-o mr-2" aria-label="Video" role="img" title="Video"/>
</t>
<t t-elif="record.slide_type.raw_value == 'quiz'">
<t t-elif="record.slide_category.raw_value == 'quiz'">
<i class="fa fa-flag mr-2" aria-label="Quiz" role="img" title="Quiz"/>
</t>
<t t-else=""><i class="fa fa-file-pdf-o mr-2" aria-label="Document" role="img" title="Document"/></t>
<field name="slide_type"/>
<field name="slide_category"/>
</span>
<span>
<i class="fa fa-clock-o mr-2" aria-label="Duration" role="img" title="Duration"/><field name="completion_time" widget="float_time"/>
@@ -324,7 +324,7 @@
<group expand="0" string="Group By">
<filter string="Course" name="groupby_channel" domain="[]" context="{'group_by': 'channel_id'}"/>
<filter string="Category" name="groupby_category" domain="[]" context="{'group_by': 'category_id'}"/>
<filter string="Type" name="groupby_type" domain="[]" context="{'group_by': 'slide_type'}"/>
<filter string="Type" name="groupby_type" domain="[]" context="{'group_by': 'slide_category'}"/>
</group>
</search>
</field>
@@ -336,7 +336,7 @@
<field name="arch" type="xml">
<graph string="Graph of Contents" stacked="0" sample="1">
<field name="channel_id"/>
<field name="slide_type"/>
<field name="slide_category"/>
<field name="total_views" type="measure"/>
<field name="quiz_first_attempt_reward" invisible="1"/>
<field name="quiz_second_attempt_reward" invisible="1"/>
@@ -13,7 +13,7 @@
<a href="/slides">Courses</a>
</li>
<t t-set="breadcrumb_class" t-value="'breadcrumb-item %s' % ('active' if not slide else '')" />
<li t-att-class="'breadcrumb-item %s' % ('active' if not search_category and not search_tag and not search_slide_type and not slide else '')">
<li t-att-class="'breadcrumb-item %s' % ('active' if not search_category and not search_tag and not search_slide_category and not slide else '')">
<a t-att-href="'/slides/%s' % slug(channel)"><span t-esc="channel.name"/></a>
</li>
<li t-att-class="breadcrumb_class" t-att-aria-current="'page' and search_category" t-if="search_category">
@@ -25,8 +25,8 @@
<li t-att-class="breadcrumb_class" 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 t-att-class="breadcrumb_class" 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 t-att-class="breadcrumb_class" t-att-aria-current="'page' and search_slide_category" t-if="search_slide_category">
<a t-att-href="'/slides/%s?slide_category=%s' % (slug(channel), search_slide_category)"><span t-esc="slide_categories[search_slide_category]"/></a>
</li>
<li t-if="slide" class="breadcrumb-item active">
<a t-att-href="'/slides/slide/%s' % slug(slide)"><span t-esc="slide.name"/></a>
@@ -58,7 +58,7 @@
<div class="dropdown-menu">
<a class="dropdown-item" href="/slides">Home</a>
<t t-set="dropdown_class" t-value="'dropdown-item %s' % ('active' if not slide else '')"/>
<a t-att-class="'dropdown-item %s' % ('active' if not search_category and not search_tag and not search_slide_type else '')" t-att-href="'/slides/%s' % slug(channel)">
<a t-att-class="'dropdown-item %s' % ('active' if not search_category and not search_tag and not search_slide_category else '')" t-att-href="'/slides/%s' % slug(channel)">
&#9492;<span class="ml-1" t-esc="channel.name"/>
</a>
<a t-att-class="dropdown_class" t-att-aria-current="'page' and search_category" t-if="search_category" t-att-href="'/slides/%s/category/%s' % (slug(channel), slug(search_category))">
@@ -70,8 +70,8 @@
<a t-att-class="dropdown_class" t-att-aria-current="'page' and search_uncategorized" t-if="search_uncategorized" t-att-href="'/slides/%s?search_uncategorized=1' % (slug(channel))">
&#9492;<span class="ml-1">Uncategorized</span>
</a>
<a t-att-class="dropdown_class" t-att-aria-current="'page' and search_slide_type" t-if="search_slide_type" t-att-href="'/slides/%s?slide_type=%s' % (slug(channel), search_slide_type)">
&#9492;<span class="ml-1" t-esc="slide_types[search_slide_type]"/>
<a t-att-class="dropdown_class" t-att-aria-current="'page' and search_slide_category" t-if="search_slide_category" t-att-href="'/slides/%s?slide_category=%s' % (slug(channel), search_slide_category)">
&#9492;<span class="ml-1" t-esc="slide_categories[search_slide_category]"/>
</a>
<a t-if="slide" class="dropdown-item active" t-att-href="'/slides/slide/%s' % (slug(slide))">
&#9492;<span class="ml-1" t-esc="slide.name"/>
@@ -526,7 +526,7 @@
<i t-else="" class="check-done text-success fa fa-check-circle px-2"></i>
</t>
<span t-if="channel.can_publish" class="d-none d-md-flex">
<a t-if="slide.slide_type == 'webpage'" class="px-2 o_text_link text-primary" target="_blank" t-attf-href="/slides/slide/#{slug(slide)}?enable_editor=1"><span class="fa fa-pencil"/></a>
<a t-if="slide.slide_category == 'webpage'" class="px-2 o_text_link text-primary" target="_blank" t-attf-href="/slides/slide/#{slug(slide)}?enable_editor=1"><span class="fa fa-pencil"/></a>
<a t-else="" class="px-2 o_text_link text-primary" target="_blank" t-attf-href="/web#id=#{slide.id}&amp;action=#{slide_action}&amp;model=slide.slide&amp;view_type=form" title="Edit in backend"><span class="fa fa-pencil"/></a>
<a href="#" t-att-data-slide-id="slide.id" class="o_text_link text-danger px-2 o_wslides_js_slide_archive"><span class="fa fa-trash"/></a>
</span>
@@ -537,7 +537,7 @@
<!-- ======= Documentation Course content: cards / categories======= -->
<template id="course_promoted_slide" name="Documentation Course content: promoted slide">
<div class="o_wslides_promoted_slide">
<div t-if="not search and not search_slide_type and slide_promoted" class="container py-1 mb-2">
<div t-if="not search and not search_slide_category and slide_promoted" class="container py-1 mb-2">
<div class="card flex-column flex-lg-row">
<t t-set="image_url" t-value="website.image_url(slide_promoted, 'image_1024')"/>
@@ -574,7 +574,7 @@
<nav class="navbar navbar-expand-lg navbar-light bg-transparent col">
<a class="navbar-brand d-lg-none" href="#">Filter &amp; order</a>
<div class="form-inline ml-auto d-lg-none" t-if="search_slide_type or search">
<div class="form-inline ml-auto d-lg-none" t-if="search_slide_category or search">
<a t-att-href="'/slides/%s' % (slug(channel))" class="btn btn-info mr-3">
<i class="fa fa-eraser mr-1"/>Clear filters
</a>
@@ -588,23 +588,23 @@
<div class="col-12">
<ul class="navbar-nav mr-lg-auto align-items-lg-center">
<t t-set="slide_type_keys" t-value="slide_types.keys()"/>
<t t-foreach="slide_type_keys" t-as="slide_type_key">
<t t-set="slide_category_keys" t-value="slide_categories.keys()"/>
<t t-foreach="slide_category_keys" t-as="slide_category_key">
<t t-if="search_category">
<li t-if="search_category['nbr_%s' % slide_type_key] > 0" class="nav-item">
<a t-att-href="'/slides/%s/category/%s?%s' % (slug(channel), slug(search_category), keep_query(slide_type=slide_type_key))"
t-att-class="'nav-link d-flex align-items-center justify-content-between mr-1 %s' % ('active' if search_slide_type == slide_type_key else '')">
<t t-esc="slide_types[slide_type_key]"/>
<span t-attf-class="badge badge-pill ml-1 #{'badge-info' if search_slide_type == slide_type_key else 'bg-400'}" t-esc="search_category['nbr_%s' % slide_type_key]"/>
<li t-if="search_category['nbr_%s' % slide_category_key] > 0" class="nav-item">
<a t-att-href="'/slides/%s/category/%s?%s' % (slug(channel), slug(search_category), keep_query(slide_category=slide_category_key))"
t-att-class="'nav-link d-flex align-items-center justify-content-between mr-1 %s' % ('active' if search_slide_category == slide_category_key else '')">
<t t-esc="slide_categories[slide_category_key]"/>
<span t-attf-class="badge badge-pill ml-1 #{'badge-info' if search_slide_category == slide_category_key else 'bg-400'}" t-esc="search_category['nbr_%s' % slide_category_key]"/>
</a>
</li>
</t>
<t t-else="">
<li t-if="channel['nbr_%s' % slide_type_key] > 0" class="nav-item">
<a t-att-href="'/slides/%s?%s' % (slug(channel), keep_query(slide_type=slide_type_key))"
t-att-class="'nav-link d-flex align-items-center justify-content-between mr-1 %s' % ('active' if search_slide_type == slide_type_key else '')">
<t t-esc="slide_types[slide_type_key]"/>
<span t-attf-class="badge badge-pill ml-1 #{'badge-info' if search_slide_type == slide_type_key else 'bg-400'}" t-esc="channel['nbr_%s' % slide_type_key]"/>
<li t-if="channel['nbr_%s' % slide_category_key] > 0" class="nav-item">
<a t-att-href="'/slides/%s?%s' % (slug(channel), keep_query(slide_category=slide_category_key))"
t-att-class="'nav-link d-flex align-items-center justify-content-between mr-1 %s' % ('active' if search_slide_category == slide_category_key else '')">
<t t-esc="slide_categories[slide_category_key]"/>
<span t-attf-class="badge badge-pill ml-1 #{'badge-info' if search_slide_category == slide_category_key else 'bg-400'}" t-esc="channel['nbr_%s' % slide_category_key]"/>
</a>
</li>
</t>
@@ -626,18 +626,18 @@
</a>
<div class="dropdown-menu" aria-labelledby="slidesChannelDropdownSort" role="menu">
<h6 class="dropdown-header">Sort by</h6>
<a role="menuitem" t-att-href="'/slides/%s?%s' % (slug(channel), keep_query('slide_type', sorting='latest'))"
<a role="menuitem" t-att-href="'/slides/%s?%s' % (slug(channel), keep_query('slide_category', 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('slide_type', sorting='most_voted'))"
<a role="menuitem" t-att-href="'/slides/%s?%s' % (slug(channel), keep_query('slide_category', 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('slide_type', sorting='most_viewed'))"
<a role="menuitem" t-att-href="'/slides/%s?%s' % (slug(channel), keep_query('slide_category', sorting='most_viewed'))"
t-att-class="'dropdown-item %s' % ('active' if sorting and sorting == 'most_viewed' else '')">Most Viewed</a>
</div>
</li>
</ul>
<div class="form-inline mr-3 d-none d-lg-inline-block">
<a t-if="search_slide_type or search" t-att-href="'/slides/%s' % (slug(channel))" class="btn btn-sm btn-info ml-1">
<a t-if="search_slide_category or search" t-att-href="'/slides/%s' % (slug(channel))" class="btn btn-sm btn-info ml-1">
<i class="fa fa-eraser mr-1"/>Clear filters
</a>
</div>
@@ -805,13 +805,13 @@
<template id="slide_icon">
<t t-set="icon_class" t-value="icon_class if icon_class else 'mr-2 text-muted'"/>
<i t-if="slide.slide_type == 'document'" t-att-class="'fa fa-file-pdf-o %s' % icon_class"></i>
<i t-if="slide.slide_type == 'presentation'" t-att-class="'fa fa-file-pdf-o %s' % icon_class"></i>
<i t-if="slide.slide_type == 'infographic'" t-att-class="'fa fa-file-picture-o %s' % icon_class"></i>
<i t-if="slide.slide_type == 'video'" t-att-class="'fa fa-play-circle %s' % icon_class"></i>
<i t-if="slide.slide_type == 'link'" t-att-class="'fa fa-file-code-o %s' % icon_class"></i>
<i t-if="slide.slide_type == 'webpage'" t-att-class="'fa fa-file-text %s' % icon_class"></i>
<i t-if="slide.slide_type == 'quiz'" t-att-class="'fa fa-question-circle %s' % icon_class"></i>
<i t-if="slide.slide_category == 'document'" t-att-class="'fa fa-file-pdf-o %s' % icon_class"></i>
<i t-if="slide.slide_category == 'presentation'" t-att-class="'fa fa-file-pdf-o %s' % icon_class"></i>
<i t-if="slide.slide_category == 'infographic'" t-att-class="'fa fa-file-picture-o %s' % icon_class"></i>
<i t-if="slide.slide_category == 'video'" t-att-class="'fa fa-play-circle %s' % icon_class"></i>
<i t-if="slide.slide_category == 'link'" t-att-class="'fa fa-file-code-o %s' % icon_class"></i>
<i t-if="slide.slide_category == 'webpage'" t-att-class="'fa fa-file-text %s' % icon_class"></i>
<i t-if="slide.slide_category == 'quiz'" t-att-class="'fa fa-question-circle %s' % icon_class"></i>
</template>
</data></odoo>
@@ -173,7 +173,7 @@
<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%" data-snippet="s_banner">
<div class="container py-5">
<h1 t-if="search_my" class="display-3 mb-0">My Courses</h1>
<h1 t-elif="search_slide_type=='certification'" class="display-3 mb-0">Certifications</h1>
<h1 t-elif="search_slide_category=='certification'" class="display-3 mb-0">Certifications</h1>
<h1 t-else="" class="display-3 mb-0">All Courses</h1>
</div>
</section>
@@ -184,7 +184,7 @@
<a class="nav-link nav-item px-3" href="/slides"><i class="fa fa-chevron-left"/></a>
</div>
<!-- Clear filtering (mobile)-->
<div class="form-inline text-nowrap ml-auto d-md-none" t-if="search_slide_type or search_my or search_tags">
<div class="form-inline text-nowrap ml-auto d-md-none" t-if="search_slide_category or search_my or search_tags">
<a href="/slides/all" class="btn btn-info mr-2" role="button" title="Clear filters">
<i class="fa fa-eraser"/> Clear filters
</a>
@@ -199,7 +199,7 @@
<t t-set="placeholder">Search courses</t>
<t t-set="search" t-value="original_search or search_term"/>
<input t-if="search_my" type="hidden" name="my" t-att-value="1"/>
<input t-if="search_slide_type" type="hidden" name="slide_type" t-att-value="search_slide_type" />
<input t-if="search_slide_category" type="hidden" name="slide_category" t-att-value="search_slide_category" />
</t>
<button class="navbar-toggler px-1" type="button"
data-toggle="collapse" data-target="#navbarTagGroups"
@@ -221,7 +221,7 @@
<div class="dropdown-menu" t-att-id="'navToogleTagGroup%s' % tag_group.id">
<t t-foreach="group_frontend_tags" t-as="tag">
<a rel="nofollow" t-att-class="'dropdown-item post_link %s' % ('active' if tag in search_tags else '')"
t-att-href="slide_query_url(tag=slugify_tags(search_tags.ids, toggle_tag_id=tag.id), my=search_my, search=search_term, slide_type=search_slide_type)"
t-att-href="slide_query_url(tag=slugify_tags(search_tags.ids, toggle_tag_id=tag.id), my=search_my, search=search_term, slide_category=search_slide_category)"
t-esc="tag.name"/>
</t>
</div>
@@ -229,7 +229,7 @@
</t>
</ul>
<!-- Clear filtering (desktop)-->
<div class="form-inline ml-auto d-none d-md-flex" t-if="search_slide_type or search_my or search_tags">
<div class="form-inline ml-auto d-none d-md-flex" t-if="search_slide_category or search_my or search_tags">
<a href="/slides/all" class="btn btn-info text-nowrap mr-2" role="button" title="Clear filters">
<i class="fa fa-eraser"/> Clear filters
</a>
@@ -244,7 +244,7 @@
<t t-set="placeholder">Search courses</t>
<t t-set="search" t-value="original_search or search_term"/>
<input t-if="search_my" type="hidden" name="my" t-att-value="1"/>
<input t-if="search_slide_type" type="hidden" name="slide_type" t-att-value="search_slide_type" />
<input t-if="search_slide_category" type="hidden" name="slide_category" t-att-value="search_slide_category" />
</t>
</div>
</nav>
@@ -267,19 +267,19 @@
<span class="align-items-baseline border d-inline-flex pl-2 rounded mb-2">
<i class="fa fa-tag mr-2 text-muted"/>
<t t-esc="search_term"/>
<a t-att-href="slide_query_url(tag=slugify_tags(search_tags.ids), my=search_my, slide_type=search_slide_type)" class="btn border-0 py-1 post_link" t-att-rel="search_tags and 'nofollow'">&#215;</a>
<a t-att-href="slide_query_url(tag=slugify_tags(search_tags.ids), my=search_my, slide_category=search_slide_category)" class="btn border-0 py-1 post_link" t-att-rel="search_tags and 'nofollow'">&#215;</a>
</span>
</t>
<t t-foreach="search_tags" t-as="tag">
<span class="align-items-baseline border d-inline-flex pl-2 rounded mb-2">
<i class="fa fa-tag mr-2 text-muted"/>
<t t-esc="tag.display_name"/>
<a t-att-href='slide_query_url(tag=slugify_tags(search_tags.ids, tag.id), my=search_my, search=search_term, slide_type=search_slide_type)' class="btn border-0 py-1 post_link" t-att-rel="search_tags and 'nofollow'">&#215;</a>
<a t-att-href='slide_query_url(tag=slugify_tags(search_tags.ids, tag.id), my=search_my, search=search_term, slide_category=search_slide_category)' class="btn border-0 py-1 post_link" t-att-rel="search_tags and 'nofollow'">&#215;</a>
</span>
</t>
</div>
<div class="container o_wslides_home_main pb-5">
<div t-if="not channels and not search_term and not search_slide_type and not search_my and not search_tags">
<div t-if="not channels and not search_term and not search_slide_category and not search_my and not search_tags">
<p class="h2">No Course created yet.</p>
<p groups="website_slides.group_website_slides_officer">Click on "New" in the top-right corner to write your first course.</p>
</div>
@@ -333,10 +333,10 @@
<div t-if="channel_frontend_tags" class="mt-2 pt-1 o_wslides_desc_truncate_2">
<t t-foreach="channel_frontend_tags" t-as="tag">
<t t-if="search_tags">
<a t-att-href="slide_query_url(tag=slugify_tags(search_tags.ids, toggle_tag_id=tag.id), my=search_my, search=search_term, slide_type=search_slide_type)" t-attf-class="badge post_link #{'badge-primary' if tag in search_tags else 'o_wslides_channel_tag o_tag_color_0'}" t-att-rel="search_tags and 'nofollow'" t-esc="tag.name"/>
<a t-att-href="slide_query_url(tag=slugify_tags(search_tags.ids, toggle_tag_id=tag.id), my=search_my, search=search_term, slide_category=search_slide_category)" t-attf-class="badge post_link #{'badge-primary' if tag in search_tags else 'o_wslides_channel_tag o_tag_color_0'}" t-att-rel="search_tags and 'nofollow'" t-esc="tag.name"/>
</t>
<t t-else="">
<a t-att-href="slide_query_url(tag=slugify_tags(search_tags.ids, toggle_tag_id=tag.id), my=search_my, search=search_term, slide_type=search_slide_type)" t-attf-class="badge post_link o_wslides_channel_tag #{'o_tag_color_'+str(tag.color)}" t-att-rel="search_tags and 'nofollow'" t-esc="tag.name"/>
<a t-att-href="slide_query_url(tag=slugify_tags(search_tags.ids, toggle_tag_id=tag.id), my=search_my, search=search_term, slide_category=search_slide_category)" t-attf-class="badge post_link o_wslides_channel_tag #{'o_tag_color_'+str(tag.color)}" t-att-rel="search_tags and 'nofollow'" t-esc="tag.name"/>
</t>
</t>
</div>
@@ -187,10 +187,10 @@
</li>
</div>
<li class="pl-4">
<a t-if="can_access and aside_slide.question_ids and aside_slide.slide_type != 'quiz'" t-att-href="'/slides/slide/%s#lessonQuiz' % (slug(aside_slide))" class="o_wslides_lesson_aside_list_link text-decoration-none small text-600">
<a t-if="can_access and aside_slide.question_ids and aside_slide.slide_category != 'quiz'" t-att-href="'/slides/slide/%s#lessonQuiz' % (slug(aside_slide))" class="o_wslides_lesson_aside_list_link text-decoration-none small text-600">
<i class="fa fa-flag text-warning"/> Quiz
</a>
<span t-elif="not can_access and aside_slide.question_ids and aside_slide.slide_type != 'quiz'"
<span t-elif="not can_access and aside_slide.question_ids and aside_slide.slide_category != 'quiz'"
class="o_wslides_lesson_aside_list_link text-decoration-none small text-600 text-muted">
<i class="fa fa-flag text-warning"/> Quiz
</span>
@@ -231,7 +231,7 @@
t-att-href="'/slides/slide/%s' % (slug(previous_slide)) if previous_slide else '#'">
<i class="fa fa-chevron-left mr-2"></i> <span class="d-none d-sm-inline-block">Prev</span>
</a>
<t t-set="allow_done_btn" t-value="slide.slide_type in ['infographic', 'presentation', 'document', 'webpage', 'video'] and not slide.question_ids and not channel_progress[slide.id].get('completed') and slide.channel_id.is_member"/>
<t t-set="allow_done_btn" t-value="slide.slide_category in ['infographic', 'presentation', 'document', 'webpage', 'video'] and not slide.question_ids and not channel_progress[slide.id].get('completed') and slide.channel_id.is_member"/>
<a t-att-class="'btn btn-primary border text-white %s' % ('disabled' if not allow_done_btn else '')"
role="button" t-att-aria-disabled="'true' if not allow_done_btn else None"
t-att-href="'/slides/slide/%s/set_completed?%s' % (slide.id, 'next_slide_id=%s' % (next_slide.id) if next_slide else '') if allow_done_btn else '#'">
@@ -255,15 +255,15 @@
</t>
</div>
<div class="o_wslides_lesson_content_type">
<img t-if="slide.slide_type == 'infographic'"
<img t-if="slide.slide_category == 'infographic'"
t-att-src="website.image_url(slide, 'image_1024')" class="img-fluid" style="width:100%" t-att-alt="slide.name"/>
<div t-if="slide.slide_type in ('presentation', 'document')" class="embed-responsive embed-responsive-4by3 embed-responsive-item mb8" style="height: 600px;">
<div t-if="slide.slide_category in ('presentation', 'document')" class="embed-responsive embed-responsive-4by3 embed-responsive-item mb8" style="height: 600px;">
<t t-out="slide.embed_code"/>
</div>
<div t-if="slide.slide_type == 'video' and slide.document_id" class="embed-responsive embed-responsive-16by9 embed-responsive-item mb8">
<div t-if="slide.slide_category == 'video' and slide.document_id" class="embed-responsive embed-responsive-16by9 embed-responsive-item mb8">
<t t-out="slide.embed_code"/>
</div>
<div t-if="slide.slide_type == 'webpage'" class="bg-white p-3">
<div t-if="slide.slide_category == 'webpage'" class="bg-white p-3">
<div t-field="slide.html_content"/>
</div>
</div>
@@ -400,7 +400,7 @@
</div>
</div>
<div class="o_wslides_js_quiz_container" t-att-data-slide-id="slide.id">
<div class="row" t-if="slide.slide_type != 'certification'">
<div class="row" t-if="slide.slide_category != 'certification'">
<t t-if="slide.question_ids">
<t t-call="website_slides.lesson_content_quiz"/>
</t>
@@ -444,7 +444,7 @@
<div class="o_wslides_js_lesson_quiz col" id="lessonQuiz"
t-att-data-id="slide.id"
t-att-data-name="slide.name"
t-att-data-slide-type="slide.slide_type"
t-att-data-slide-category="slide.slide_category"
t-att-data-is-member="slide.channel_id.is_member"
t-att-data-completed="1 if slide_completed else 0"
t-att-data-quiz-attempts-count="quiz_attempts_count"
@@ -543,7 +543,7 @@
</form>
</div>
<div t-if="is_public_user" class="alert alert-info d-inline-block">
<p class="mb-0">Please <a t-attf-href="/web?redirect=#{request.httprequest.url}" class="font-weight-bold"> login </a> to share this <t t-esc="slide.slide_type"/> by email.</p>
<p class="mb-0">Please <a t-attf-href="/web?redirect=#{request.httprequest.url}" class="font-weight-bold"> login </a> to share this <t t-esc="slide.slide_category"/> by email.</p>
</div>
</template>
@@ -557,7 +557,7 @@
<t t-esc="slide.embed_code_external"/>
</textarea>
</div>
<div t-if="slide.slide_type in ('presentation', 'document') and not embed_hide_starting_page" class="form-group d-flex">
<div t-if="slide.slide_category in ('presentation', 'document') and not embed_hide_starting_page" class="form-group d-flex">
<div class="form-text p-0 col-xs-5 col-sm-5 col-md-5 col-lg-5">Select page to start with</div>
<div class="input-group col-xs-3 col-sm-2 col-md-2 col-lg-3">
<input type="number" value="1" class="form-control"/>
@@ -71,7 +71,7 @@
</t>
</div>
</div>
<t t-if="slide.slide_type in ('presentation', 'document')">
<t t-if="slide.slide_category in ('presentation', 'document')">
<div id="PDFViewerLoader" class="oe_slides_loader mt-3 mx-2 w-100">
<div class="toast show mx-auto">
<div class="toast-header">
@@ -84,12 +84,12 @@
</div>
<canvas id="PDFViewerCanvas" class="mx-auto" style="display: none;"></canvas>
</t>
<t t-if="slide.slide_type == 'infographic'">
<t t-if="slide.slide_category == 'infographic'">
<img t-att-src="website.image_url(slide, 'image_1024')" class="img-fluid" style="width: 100%" alt="Slide image"/>
</t>
</div>
<!-- Fixed bottom navbar -->
<div id="PDFViewerNav" class="pt-2 pb-2 bg-light text-white" role="navigation" t-if="slide.slide_type in ('presentation', 'document')">
<div id="PDFViewerNav" class="pt-2 pb-2 bg-light text-white" role="navigation" t-if="slide.slide_category in ('presentation', 'document')">
<div class="container-fluid oe_slides_panel_footer">
<div class="row align-items-center">
<div class="col-3 d-flex align-items-center">
@@ -111,7 +111,7 @@
<span id="previous" class="mx-1 mx-sm-2" title="Previous slide" aria-label="Previous slide" role="button"><i class="fa fa-arrow-circle-left"/></span>
<span id="next" class="mx-1 mx-sm-2" title="Next slide" aria-label="Next slide" role="button"><i class="fa fa-arrow-circle-right"/></span>
<span id="last" class="mx-1 mx-sm-2" title="Last slide" aria-label="Last slide" role="button"><i class="fa fa-step-forward"/></span>
<a t-if="slide.slide_resource_downloadable" id="download" t-attf-href="/web/content/slide.slide/#{slide.id}/datas?download=true"
<a t-if="slide.slide_resource_downloadable" id="download" t-attf-href="/web/content/slide.slide/#{slide.id}/binary_content?download=true"
class="ml-1 ml-sm-2" title="Download Content" aria-label="Download" role="button">
<i class="fa fa-download" />
</a>
@@ -109,12 +109,12 @@
t-att-data-id="slide.id"
t-att-data-can-access="can_access"
t-att-data-name="slide.name"
t-att-data-type="slide.slide_type"
t-att-data-category="slide.slide_category"
t-att-data-slug="slug(slide)"
t-att-data-has-question="1 if slide.question_ids else 0"
t-att-data-is-quiz="0"
t-att-data-completed="1 if slide_completed else 0"
t-att-data-embed-code="slide.embed_code if slide.slide_type in ['video', 'document', 'presentation', 'infographic'] else False"
t-att-data-embed-code="slide.embed_code if slide.slide_category in ['video', 'document', 'presentation', 'infographic'] else False"
t-att-data-is-member="is_member"
t-att-data-session-answers="session_answers">
<span class="ml-3">
@@ -135,7 +135,7 @@
<div class="o_wslides_fs_slide_name text-600" t-esc="slide.name"/>
</div>
</span>
<ul class="list-unstyled w-100 pt-2 small" t-if="slide.slide_resource_ids or (slide.question_ids and not slide.slide_type =='quiz')" >
<ul class="list-unstyled w-100 pt-2 small" t-if="slide.slide_resource_ids or (slide.question_ids and not slide.slide_category =='quiz')" >
<t t-set="links" t-value="slide.slide_resource_ids.filtered(lambda res: res.resource_type == 'url')"/>
<li t-if="links" t-foreach="links" t-as="link" class="pl-0 mb-1">
<a t-if="can_access" class="o_wslides_fs_slide_link" t-att-href="link.link" target="_blank">
@@ -159,11 +159,11 @@
<t t-call="website_slides.join_course_link"/>
</li>
</div>
<li class="o_wslides_fs_sidebar_list_item pl-0 mb-1" t-if="slide.question_ids and not slide.slide_type == 'quiz'"
<li class="o_wslides_fs_sidebar_list_item pl-0 mb-1" t-if="slide.question_ids and not slide.slide_category == 'quiz'"
t-att-data-id="slide.id"
t-att-data-can-access="can_access"
t-att-data-name="slide.name"
t-att-data-type="slide.slide_type"
t-att-data-category="slide.slide_category"
t-att-data-slug="slug(slide)"
t-att-data-has-question="1 if slide.question_ids else 0"
t-att-data-is-quiz="1"
@@ -43,7 +43,7 @@ class WebsiteSlidesSurvey(WebsiteSlides):
@http.route(['/slides/add_slide'], type='json', auth='user', methods=['POST'], website=True)
def create_slide(self, *args, **post):
create_new_survey = post['slide_type'] == "certification" and post.get('survey') and not post['survey']['id']
create_new_survey = post['slide_category'] == "certification" and post.get('survey') and not post['survey']['id']
linked_survey_id = int(post.get('survey', {}).get('id') or 0)
if create_new_survey:
@@ -87,7 +87,7 @@ class WebsiteSlidesSurvey(WebsiteSlides):
# Utils
# ---------------------------------------------------
def _set_completed_slide(self, slide):
if slide.slide_type == 'certification':
if slide.slide_category == 'certification':
raise werkzeug.exceptions.Forbidden(_("Certification slides are completed when the survey is succeeded."))
return super(WebsiteSlidesSurvey, self)._set_completed_slide(slide)
@@ -6,7 +6,7 @@
<record id="website_slides.badge_data_certification_goal" model="gamification.goal.definition">
<field name="domain">[
('survey_scoring_success', '=', True),
('slide_id.slide_type', '=', 'certification')
('slide_id.slide_category', '=', 'certification')
]</field>
</record>
</data></odoo>
@@ -7,7 +7,7 @@
<field name="name">Furniture Creation Certification</field>
<field name="sequence">7</field>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_furniture.jpg"/>
<field name="slide_type">certification</field>
<field name="slide_category">certification</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_5_furn2"/>
<field name="category_id" ref="website_slides.slide_category_demo_5_0"/>
<field name="survey_id" ref="website_slides_survey.furniture_certification"/>
@@ -23,7 +23,7 @@
<field name="name">DIY Furniture Certification</field>
<field name="sequence">1</field>
<field name="image_1920" type="base64" file="website_slides/static/src/img/slide_demo_furniture.jpg"/>
<field name="slide_type">certification</field>
<field name="slide_category">certification</field>
<field name="channel_id" ref="website_slides.slide_channel_demo_6_furn3"/>
<field name="category_id" eval="False"/>
<field name="survey_id" ref="website_slides_survey.furniture_certification"/>
@@ -30,27 +30,27 @@ class SlidePartnerRelation(models.Model):
class Slide(models.Model):
_inherit = 'slide.slide'
slide_type = fields.Selection(selection_add=[
slide_category = fields.Selection(selection_add=[
('certification', 'Certification')
], ondelete={'certification': 'set default'})
survey_id = fields.Many2one('survey.survey', 'Certification')
nbr_certification = fields.Integer("Number of Certifications", compute='_compute_slides_statistics', store=True)
_sql_constraints = [
('check_survey_id', "CHECK(slide_type != 'certification' OR survey_id IS NOT NULL)", "A slide of type 'certification' requires a certification."),
('check_certification_preview', "CHECK(slide_type != 'certification' OR is_preview = False)", "A slide of type certification cannot be previewed."),
('check_survey_id', "CHECK(slide_category != 'certification' OR survey_id IS NOT NULL)", "A slide of type 'certification' requires a certification."),
('check_certification_preview', "CHECK(slide_category != 'certification' OR is_preview = False)", "A slide of type certification cannot be previewed."),
]
@api.onchange('survey_id')
def _on_change_survey_id(self):
if self.survey_id:
self.slide_type = 'certification'
self.slide_category = 'certification'
@api.model
def create(self, values):
rec = super(Slide, self).create(values)
if rec.survey_id:
rec.slide_type = 'certification'
rec.slide_category = 'certification'
if 'survey_id' in values:
rec._ensure_challenge_category()
return rec
@@ -90,7 +90,7 @@ class Slide(models.Model):
course can be enrolled multiple times.
"""
certification_urls = {}
for slide in self.filtered(lambda slide: slide.slide_type == 'certification' and slide.survey_id):
for slide in self.filtered(lambda slide: slide.slide_category == 'certification' and slide.survey_id):
if slide.channel_id.is_member:
user_membership_id_sudo = slide.user_membership_id.sudo()
if user_membership_id_sudo.user_input_ids:
@@ -11,7 +11,7 @@ Fullscreen.include({
),
/**
* Extend the _renderSlide method so that slides of type "certification"
* Extend the _renderSlide method so that slides of category "certification"
* are also taken into account and rendered correctly
*
* @private
@@ -20,7 +20,7 @@ Fullscreen.include({
_renderSlide: function (){
var def = this._super.apply(this, arguments);
var $content = this.$('.o_wslides_fs_content');
if (this.get('slide').type === "certification"){
if (this.get('slide').category === "certification"){
$content.html(QWeb.render('website.slides.fullscreen.certification',{widget: this}));
}
return Promise.all([def]);
@@ -7,7 +7,7 @@ var sessionStorage = window.sessionStorage;
var SlidesUpload = require('@website_slides/js/slides_upload')[Symbol.for("default")];
/**
* Management of the new 'certification' slide_type
* Management of the new 'certification' slide_category
*/
SlidesUpload.SlideUploadDialog.include({
events: _.extend({}, SlidesUpload.SlideUploadDialog.prototype.events || {}, {
@@ -32,14 +32,14 @@ SlidesUpload.SlideUploadDialog.include({
//--------------------------------------------------------------------------
/**
* Overridden to add the "certification" slide type
* Overridden to add the "certification" slide category
*
* @override
* @private
*/
_setup: function () {
this._super.apply(this, arguments);
this.slide_type_data['certification'] = {
this.slide_category_data['certification'] = {
icon: 'fa-trophy',
label: _t('Certification'),
template: 'website.slide.upload.modal.certification',
@@ -3,14 +3,14 @@
<t t-name="website.slides.fullscreen.certification">
<div class="justify-content-center align-self-center">
<div t-if="widget.get('slide').type === 'certification' &amp;&amp; !widget.get('slide').completed" class="">
<div t-if="widget.get('slide').category === 'certification' &amp;&amp; !widget.get('slide').completed" class="">
<a class="btn btn-primary" t-att-href="'/slides_survey/slide/get_certification_url?slide_id=' + widget.get('slide').id" target="_blank">
<i class="fa fa-trophy"/>
<span t-if="widget.get('slide').isMember"> Pass Certification</span>
<span t-else="">Test Certification</span>
</a>
</div>
<div class="" t-if="widget.get('slide').type === 'certification' &amp;&amp; widget.get('slide').completed">
<div class="" t-if="widget.get('slide').category === 'certification' &amp;&amp; widget.get('slide').completed">
<a role="button" class="btn btn-primary" t-att-href="'/survey/' + widget.get('slide').certificationId + '/get_certification'">
<i class="fa fa-fw fa-trophy" role="img" aria-label="Download certification" title="Download certification"/> Download certification
</a>
@@ -20,7 +20,7 @@
<t t-extend="website.slides.fullscreen.title">
<t t-jquery=".o_wslides_fs_slide_title_span" t-operation="before">
<i t-if="widget.get('slide').type === 'certification'" class="fa fa-trophy mr-2 text"></i>
<i t-if="widget.get('slide').category === 'certification'" class="fa fa-trophy mr-2 text"></i>
</t>
</t>
</templates>
@@ -47,11 +47,11 @@ class TestCourseCertificationFailureFlow(TestSurveyCommon):
'is_published': True,
})
# Step 2: link the certification to a slide of type 'certification'
# Step 2: link the certification to a slide of category 'certification'
self.slide_certification = self.env['slide.slide'].sudo().create({
'name': 'Certification slide',
'channel_id': self.channel.id,
'slide_type': 'certification',
'slide_category': 'certification',
'survey_id': certification.id,
'is_published': True,
})
@@ -10,11 +10,11 @@
<span class="o_stat_text" attrs="{'invisible': [('nbr_certification', '>', 0)]}">Finished</span>
<span class="o_stat_text" attrs="{'invisible': [('nbr_certification', '=', 0)]}">Certified</span>
</xpath>
<xpath expr="//field[@name='slide_type']" position="after">
<xpath expr="//field[@name='slide_category']" position="after">
<field name="survey_id"/>
</xpath>
<xpath expr="//create[@name='add_slide_lesson']" position="after">
<create name="add_slide_certificate" string="Add Certification" groups="survey.group_survey_user" context="{'default_slide_type': 'certification'}"/>
<create name="add_slide_certificate" string="Add Certification" groups="survey.group_survey_user" context="{'default_slide_category': 'certification'}"/>
</xpath>
</field>
</record>
@@ -5,9 +5,9 @@
<field name="model">slide.slide</field>
<field name="inherit_id" ref="website_slides.view_slide_slide_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='slide_type']" position="after">
<xpath expr="//field[@name='slide_category']" position="after">
<field name="survey_id"
attrs="{'invisible': [('slide_type', '!=', 'certification')], 'required': [('slide_type', '=', 'certification')]}"
attrs="{'invisible': [('slide_category', '!=', 'certification')], 'required': [('slide_category', '=', 'certification')]}"
domain="[('certification', '=', True)]" context="{'default_certification': True, 'default_scoring_type': 'scoring_without_answers'}"/>
</xpath>
</field>
@@ -17,8 +17,8 @@
<field name="name">Certifications</field>
<field name="res_model">slide.slide</field>
<field name="view_mode">tree,form,graph</field>
<field name="domain">[('slide_type', '=', 'certification')]</field>
<field name="context">{'default_slide_type': 'certification'}</field>
<field name="domain">[('slide_category', '=', 'certification')]</field>
<field name="context">{'default_slide_category': 'certification'}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Add a new certification
@@ -45,7 +45,7 @@
<div class="text-muted d-inline-block">No certifications yet!</div>
</t>
<div class="text-right d-inline-block pull-right">
<a href="/slides/all?slide_type=certification" class="btn btn-link btn-sm"><i class="fa fa-arrow-right mr-1"/>All Certifications</a>
<a href="/slides/all?slide_category=certification" class="btn btn-link btn-sm"><i class="fa fa-arrow-right mr-1"/>All Certifications</a>
</div>
</template>
@@ -11,7 +11,7 @@
<div t-if="channel.nbr_certification > 0 and channel.is_member and channel.completion == 0" class="alert alert-success d-flex align-items-center justify-content-between flex-wrap">
<div>Begin your <b>certification</b> today!</div>
<a t-attf-href="#{'/slides_survey/slide/get_certification_url?slide_id=%s' %(first_slide.id) if first_slide.slide_type == 'certification' and channel.total_slides == 1 else '/slides/slide/%s?fullscreen=1' %(slug(first_slide))}" class="btn btn-success mt-2 mt-sm-0">
<a t-attf-href="#{'/slides_survey/slide/get_certification_url?slide_id=%s' %(first_slide.id) if first_slide.slide_category == 'certification' and channel.total_slides == 1 else '/slides/slide/%s?fullscreen=1' %(slug(first_slide))}" class="btn btn-success mt-2 mt-sm-0">
<span>Start Now</span><i class="fa fa-chevron-right ml-2 align-middle"/>
</a>
</div>
@@ -20,16 +20,16 @@
<template id="slide_icon_inherit_survey" inherit_id="website_slides.slide_icon">
<xpath expr="//i[last()]" position="after">
<i t-if="slide.slide_type == 'certification'" t-att-class="'fa fa-trophy %s' % icon_class"></i>
<i t-if="slide.slide_category == 'certification'" t-att-class="'fa fa-trophy %s' % icon_class"></i>
</xpath>
</template>
<template id="course_slides_list_slide_inherit_survey" inherit_id="website_slides.course_slides_list_slide">
<xpath expr="//a[hasclass('o_wslides_js_slides_list_slide_link')]" position="attributes">
<attribute name="t-attf-href">#{'/slides_survey/slide/get_certification_url?slide_id=%s' %(slide.id) if slide.slide_type == 'certification' and slide.channel_id.total_slides == 1 else '/slides/slide/%s' %(slug(slide))}</attribute>
<attribute name="t-attf-href">#{'/slides_survey/slide/get_certification_url?slide_id=%s' %(slide.id) if slide.slide_category == 'certification' and slide.channel_id.total_slides == 1 else '/slides/slide/%s' %(slug(slide))}</attribute>
</xpath>
<xpath expr="//a[@name='o_wslides_list_slide_add_quizz']" position="attributes">
<attribute name="t-if">channel.can_upload and not slide.question_ids and slide.slide_type != 'certification'</attribute>
<attribute name="t-if">channel.can_upload and not slide.question_ids and slide.slide_category != 'certification'</attribute>
</xpath>
<xpath expr="//a[@t-if='channel.can_upload']" position="attributes">
<attribute name="t-attf-class">#{'d-none' if slide.slide_type == 'certification' else ''}</attribute>
@@ -3,7 +3,7 @@
<data>
<template id="courses_home_inherit_survey" inherit_id="website_slides.courses_home">
<xpath expr="//a[hasclass('o_wslides_home_all_slides')]" position="after">
<a class="nav-link nav-link d-flex" href="/slides/all?slide_type=certification">
<a class="nav-link nav-link d-flex" href="/slides/all?slide_category=certification">
<t t-call="website_slides_survey.o_wss_certification_icon"/>
<span class="ml-1">Certifications</span>
</a>
@@ -3,7 +3,7 @@
<data>
<template id="slide_content_detailed" inherit_id="website_slides.slide_content_detailed">
<xpath expr="//div[hasclass('o_wslides_lesson_content_type')]" position="inside">
<div t-if="slide.slide_type == 'certification' and not channel_progress[slide.id].get('completed')" class="col mt32 mb8 d-flex justify-content-center">
<div t-if="slide.slide_category == 'certification' and not channel_progress[slide.id].get('completed')" class="col mt32 mb8 d-flex justify-content-center">
<a role="button"
class="btn btn-primary btn-lg"
t-att-href="'/slides_survey/slide/get_certification_url?slide_id=%s' %(slide.id)">
@@ -12,7 +12,7 @@
<t t-else="">Test Certification</t>
</a>
</div>
<div t-if="slide.slide_type == 'certification' and channel_progress[slide.id].get('completed')" class="col mt32 mb8 d-flex justify-content-center">
<div t-if="slide.slide_category == 'certification' and channel_progress[slide.id].get('completed')" class="col mt32 mb8 d-flex justify-content-center">
<a role="button" class="btn btn-primary btn-lg" t-att-href="'/survey/%s/get_certification' % slide.survey_id.id">
<i class="fa fa-fw fa-trophy" role="img" aria-label="Download certification" title="Download certification"/> Download certification
</a>