From 1894dc54148bd008ffd8ab29ce5cf6cb9c4afaea Mon Sep 17 00:00:00 2001 From: Patrick Hoste <pko@odoo.com> Date: Mon, 9 Sep 2019 12:18:50 +0000 Subject: [PATCH] [IMP] website_slides: allow to download slide content and additional resources PURPOSE Currently eLearning lacks feature of having downloadable resources on lessons. Indeed for example teachers would like to attach zip files or other resources linked to a given lesson. SPECIFICATIONS You can now allow the user to download the slide content for pdf and image slides. A new icon will be visible next to the fullscreen button on the pdf viewer. Course maintainers can also add additional resources to a slide which will be available to download for the user. Task ID 2066741 PR #39890 Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com> --- .../views/website_slides_templates.xml | 35 +++++++++++++++++++ .../website_slides/data/slide_slide_demo.xml | 18 ++++++++++ addons/website_slides/models/slide_slide.py | 21 ++++++++++- .../security/ir.model.access.csv | 5 ++- .../security/website_slides_security.xml | 12 +++++++ .../src/js/slides_course_fullscreen_player.js | 1 + .../views/slide_slide_views.xml | 16 ++++++--- .../views/website_slides_templates_lesson.xml | 28 ++++++++++++++- .../website_slides_templates_lesson_embed.xml | 9 ++++- ...ite_slides_templates_lesson_fullscreen.xml | 15 +++++++- .../views/website_slides_templates_utils.xml | 7 ++++ 11 files changed, 158 insertions(+), 9 deletions(-) diff --git a/addons/website_sale_slides/views/website_slides_templates.xml b/addons/website_sale_slides/views/website_slides_templates.xml index ec51520fd3df..abeb0ec227d7 100644 --- a/addons/website_sale_slides/views/website_slides_templates.xml +++ b/addons/website_sale_slides/views/website_slides_templates.xml @@ -79,4 +79,39 @@ </div> </xpath> </template> + +<template name="Buy Course To Download Resource" id="slide_aside_training_category_buy_course" inherit_id="website_slides.slide_aside_training_category"> + <xpath expr="//div[hasclass('o_wslides_js_course_join')]" position="inside"> + <li t-elif="aside_slide.channel_id.enroll == 'payment'" class="text-decoration-none small"> + <i class="fa fa-download mr-1"/> + <t t-call="website_sale_slides.buy_course_link"> + <t t-set="slide" t-value="aside_slide"/> + </t> + </li> + </xpath> +</template> + +<template name="Buy Course To Download Resource Fullscreen" id="slide_fullscreen_sidebar_category_buy_course" inherit_id="website_slides.slide_fullscreen_sidebar_category"> + <xpath expr="//div[hasclass('o_wslides_js_course_join')]" position="inside"> + <li t-elif="slide.channel_id.enroll == 'payment'" class="o_wslides_fs_slide_link mb-1"> + <i class="fa fa-download mr-1"/> + <t t-call="website_sale_slides.buy_course_link"/> + </li> + </xpath> +</template> + +<template name="Buy Course To Download Resource Slide Detail" id="slide_content_detailed_buy_course" inherit_id="website_slides.slide_content_detailed"> + <xpath expr="//div[hasclass('o_wslides_js_course_join')]" position="inside"> + <span t-elif="slide.channel_id.enroll == 'payment'" class="text-muted mr-auto border-left pl-3"> + <t t-call="website_sale_slides.buy_course_link"/> + </span> + </xpath> +</template> + +<template name="Buy Course Link" id="buy_course_link"> + <a t-att-href="'/shop/cart/update?product_id=%s' % slide.channel_id.product_id.id"> + Buy Course + </a> + to download resources +</template> </data></odoo> diff --git a/addons/website_slides/data/slide_slide_demo.xml b/addons/website_slides/data/slide_slide_demo.xml index d5925fe69f9c..5c8d20cf1f7d 100644 --- a/addons/website_slides/data/slide_slide_demo.xml +++ b/addons/website_slides/data/slide_slide_demo.xml @@ -18,6 +18,12 @@ <field name="tag_ids" eval="[(4, ref('website_slides.slide_tag_demo_tools')), (4, ref('website_slides.slide_tag_demo_howto'))]"/> <field name="description">A summary of know-how: how and what. All the basics for this course about gardening.</field> </record> + <!--RESOURCE--> + <record id="slide_slide_demo_0_0_resource_0" model="slide.slide.resource"> + <field name="name">Document</field> + <field name="data" type="base64" file="website_slides/static/src/img/document.png"/> + <field name="slide_id" ref="slide_slide_demo_0_0"/> + </record> <record id="slide_slide_demo_0_1" model="slide.slide"> <field name="name">Home Gardening</field> <field name="sequence">2</field> @@ -191,6 +197,12 @@ <field name="link">http://www.example.com</field> <field name="slide_id" ref="slide_slide_demo_1_2"/> </record> + <!--RESOURCE--> + <record id="slide_slide_demo_1_2_resource_0" model="slide.slide.resource"> + <field name="name">Presentation</field> + <field name="data" type="base64" file="website_slides/static/src/img/presentation.pdf"/> + <field name="slide_id" ref="slide_slide_demo_1_2"/> + </record> <record id="slide_slide_demo_1_3" model="slide.slide"> <field name="name">How to plant a potted tree</field> <field name="sequence">5</field> @@ -392,6 +404,12 @@ <field name="link">http://www.example.com</field> <field name="slide_id" ref="slide_slide_demo_2_0"/> </record> + <!--RESOURCE--> + <record id="slide_slide_demo_2_0_resource_0" model="slide.slide.resource"> + <field name="name">Tree image</field> + <field name="data" type="base64" file="website_slides/static/src/img/slide_demo_tree_img_2.jpg"/> + <field name="slide_id" ref="slide_slide_demo_2_0"/> + </record> <!-- QUIZZ --> <record id="slide_slide_demo_2_0_question_0" model="slide.question"> <field name="question">Do you make beams out of lemon trees ?</field> diff --git a/addons/website_slides/models/slide_slide.py b/addons/website_slides/models/slide_slide.py index e64a7ecf3635..917371b0b298 100644 --- a/addons/website_slides/models/slide_slide.py +++ b/addons/website_slides/models/slide_slide.py @@ -63,6 +63,15 @@ class SlideLink(models.Model): link = fields.Char('Link', required=True) +class SlideResource(models.Model): + _name = 'slide.slide.resource' + _description = "Additional resource for a particular slide" + + slide_id = fields.Many2one('slide.slide', required=True, ondelete='cascade') + name = fields.Char('Name', required=True) + data = fields.Binary('Resource') + + class EmbeddedSlide(models.Model): """ Embedding in third party websites. Track view count, generate statistics. """ _name = 'slide.embed' @@ -160,6 +169,8 @@ class Slide(models.Model): url = fields.Char('Document URL', help="Youtube or Google Document URL") document_id = fields.Char('Document ID', help="Youtube or Google Document ID") link_ids = fields.One2many('slide.slide.link', 'slide_id', string="External URL for this slide") + slide_resource_ids = fields.One2many('slide.slide.resource', 'slide_id', string="Additional Resource for this slide") + slide_resource_downloadable = fields.Boolean('Allow Download', default=False, 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) # website @@ -347,12 +358,20 @@ class Slide(models.Model): @api.onchange('datas') def _on_change_datas(self): - """ For PDFs, we assume that it takes 5 minutes to read a page. """ + """ 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 + image field. (It avoids the infinite loading in PDF viewer)""" if self.datas: data = base64.b64decode(self.datas) if data.startswith(b'%PDF-'): pdf = PyPDF2.PdfFileReader(io.BytesIO(data), overwriteWarnings=False) self.completion_time = (5 * len(pdf.pages)) / 60 + else: + self.slide_type = 'infographic' + self.image_1920 = self.datas + self.datas = None @api.depends('name', 'channel_id.website_id.domain') def _compute_website_url(self): diff --git a/addons/website_slides/security/ir.model.access.csv b/addons/website_slides/security/ir.model.access.csv index b6d1a76ce441..43f14a23bbf1 100644 --- a/addons/website_slides/security/ir.model.access.csv +++ b/addons/website_slides/security/ir.model.access.csv @@ -22,4 +22,7 @@ access_slide_channel_partners_system,slide.channel.users.system,model_slide_chan access_slide_embed_all,slide.embed.all,model_slide_embed,,1,0,0,0 access_slide_embed_user,slide.embed.user,model_slide_embed,base.group_user,1,1,1,1 access_slide_slide_link_all,slide.slide.link.all,model_slide_slide_link,,1,0,0,0 -access_slide_slide_link_officer,slide.slide.link.officer,model_slide_slide_link,website_slides.group_website_slides_officer,1,1,1,1 \ No newline at end of file +access_slide_slide_link_officer,slide.slide.link.officer,model_slide_slide_link,website_slides.group_website_slides_officer,1,1,1,1 +access_slide_slide_resource_all,slide.slide.resource.all,model_slide_slide_resource,,1,0,0,0 +access_slide_slide_resource_public,slide.slide.resource.public,model_slide_slide_resource,base.group_public,0,0,0,0 +access_slide_slide_resource_publisher,slide.slide.resource.publisher,model_slide_slide_resource,website_slides.group_website_slides_officer,1,1,1,1 diff --git a/addons/website_slides/security/website_slides_security.xml b/addons/website_slides/security/website_slides_security.xml index a6a347dd85f2..bcfd70f0682f 100644 --- a/addons/website_slides/security/website_slides_security.xml +++ b/addons/website_slides/security/website_slides_security.xml @@ -155,5 +155,17 @@ <field name="perm_read" eval="0"/> <field name="perm_create" eval="1"/> </record> + + <!--SLIDE RESOURCE--> + <record id="rule_slide_slide_resource_downloadable" model="ir.rule"> + <field name="name">Resource: restricted to channel members and channel responsible</field> + <field name="model_id" ref="model_slide_slide_resource"/> + <field name="domain_force">[('slide_id.channel_id.partner_ids', '=', user.partner_id.id)]</field> + <field name="groups" eval="[(4, ref('base.group_portal')), (4, ref('base.group_user'))]"/> + <field name="perm_read" eval="True"/> + <field name="perm_write" eval="False"/> + <field name="perm_create" eval="False"/> + <field name="perm_unlink" eval="False"/> + </record> </data> </odoo> \ No newline at end of file diff --git a/addons/website_slides/static/src/js/slides_course_fullscreen_player.js b/addons/website_slides/static/src/js/slides_course_fullscreen_player.js index 00490ed9a0aa..ef317465bbc2 100644 --- a/addons/website_slides/static/src/js/slides_course_fullscreen_player.js +++ b/addons/website_slides/static/src/js/slides_course_fullscreen_player.js @@ -15,6 +15,7 @@ odoo.define('website_slides.fullscreen', function (require) { var Dialog = require('web.Dialog'); + require('website_slides.course.join.widget'); /** * Helper: Get the slide dict matching the given criteria diff --git a/addons/website_slides/views/slide_slide_views.xml b/addons/website_slides/views/slide_slide_views.xml index 10fd9f7d3ad3..0301710e345f 100644 --- a/addons/website_slides/views/slide_slide_views.xml +++ b/addons/website_slides/views/slide_slide_views.xml @@ -75,13 +75,13 @@ </group> <group name="related_details"> <field name="user_id"/> - <field name="website_id" groups="website.group_multi_website"/> - <field name="is_preview"/> <label for="completion_time"/> <div> <field name="completion_time" widget="float_time" class="oe_inline"/> <span> hours</span> </div> + <field name="is_preview"/> + <field name="slide_resource_downloadable" attrs="{'invisible': [('slide_type', 'not in', ['presentation', 'document'])]}"/> <field name="date_published" string="Published Date" attrs="{'invisible': [('date_published', '=', False)]}" groups="base.group_no_one"/> </group> </group> @@ -89,8 +89,8 @@ <page name="description" string="Description"> <field name="description" placeholder="e.g. In this video, we'll give you the keys on how Odoo can help you to grow your business. At the end, we'll propose you a quiz to test your knowledge."/> </page> - <page string="External Links" name="external_links" > - <group> + <page string="Additional Resources" name="external_links" > + <group string="External Links"> <field name="link_ids" widget="one2many" nolabel="1"> <tree editable="top"> <field name="name"/> @@ -98,6 +98,14 @@ </tree> </field> </group> + <group string="Resources"> + <field name="slide_resource_ids" widget="one2many" nolabel="1"> + <tree editable="top"> + <field name="name"/> + <field name="data" string="Size" required="1"/> + </tree> + </field> + </group> </page> <page name="quiz" string="Quiz"> <group name="quiz_details"> diff --git a/addons/website_slides/views/website_slides_templates_lesson.xml b/addons/website_slides/views/website_slides_templates_lesson.xml index 71457ea0bb18..35d104c15895 100644 --- a/addons/website_slides/views/website_slides_templates_lesson.xml +++ b/addons/website_slides/views/website_slides_templates_lesson.xml @@ -162,7 +162,7 @@ </span> </div> </a> - <ul t-if="aside_slide.link_ids or aside_slide.question_ids" class="list-group px-2 mb-1 list-unstyled"> + <ul t-if="aside_slide.link_ids or aside_slide.slide_resource_ids or aside_slide.question_ids" class="list-group px-2 mb-1 list-unstyled"> <t t-foreach="aside_slide.link_ids" t-as="resource"> <li class="pl-4"> <a t-if="can_access" t-att-href="resource.link" target="new" class="text-decoration-none small"> @@ -173,6 +173,19 @@ </span> </li> </t> + <div class="o_wslides_js_course_join pl-4" t-if="aside_slide.slide_resource_ids"> + <t t-if="is_member or aside_slide.channel_id.can_publish"> + <li t-foreach="aside_slide.slide_resource_ids" t-as="resource"> + <a t-attf-href="/web/content/slide.slide.resource/#{resource.id}/data?download=true" class="text-decoration-none small"> + <i class="fa fa-download mr-1"/><span t-field="resource.name"/> + </a> + </li> + </t> + <li t-elif="aside_slide.channel_id.enroll == 'public'" class="text-decoration-none small"> + <i class="fa fa-download mr-1"/> + <t t-call="website_slides.join_course_link"/> + </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"> <i class="fa fa-flag text-warning"/> Quiz @@ -390,6 +403,19 @@ </t> </div> </div> + <div class="col-12 col-md d-flex align-items-start mb-4 mb-md-0 o_wslides_js_course_join" t-if="len(slide.slide_resource_ids)"> + <span t-if="slide.channel_id.is_member or slide.channel_id.can_publish or slide.is_preview or slide.channel_id.enroll in ['private', 'payment']" class="text-muted font-weight-bold mr-3"> + Additional Resources + </span> + <div t-if="slide.channel_id.is_member or slide.channel_id.can_publish" class="text-muted mr-auto border-left pl-3"> + <t t-foreach="slide.slide_resource_ids" t-as="resource"> + <a t-attf-href="/web/content/slide.slide.resource/#{resource.id}/data?download=true" t-esc="resource.name"/><br /> + </t> + </div> + <div t-elif="slide.channel_id.enroll == 'public'" class="text-muted mr-auto border-left pl-3"> + <t t-call="website_slides.join_course_link"/> + </div> + </div> <div t-if="slide.channel_id.allow_comment and slide.channel_id.channel_type == 'documentation'" class="col-12 col-md d-flex align-items-start justify-content-md-end mb-2 mb-md-0"> <span class="text-muted font-weight-bold mr-3">Rating</span> diff --git a/addons/website_slides/views/website_slides_templates_lesson_embed.xml b/addons/website_slides/views/website_slides_templates_lesson_embed.xml index 2103edd081c9..b0897d506c25 100644 --- a/addons/website_slides/views/website_slides_templates_lesson_embed.xml +++ b/addons/website_slides/views/website_slides_templates_lesson_embed.xml @@ -112,7 +112,14 @@ <a id="last" href="#" class="text-decoration-none ml-1 ml-sm-2" title="Last slide" aria-label="Last slide" role="button"> <i class="fa fa-step-forward"/> </a> </div> <div class="col-2 flex-grow-0 text-right"> - <a id="fullscreen" href="#" title="View fullscreen" role="img" aria-label="Fullscreen"> <i class="fa fa-arrows-alt"/> </a> + <a t-if="slide.slide_resource_downloadable" id="download" t-attf-href="/web/content/slide.slide/#{slide.id}/datas?download=true" + class="text-decoration-none mr-1 mr-sm-2" title="Download Content" role="img" aria-label="Download"> + <i class="fa fa-download" /> + </a> + <a id="fullscreen" href="#" class="text-decoration-none ml-1 ml-sm-2" + title="View fullscreen" role="img" aria-label="Fullscreen"> + <i class="fas fa-expand-arrows-alt"/> + </a> </div> </div> </div> diff --git a/addons/website_slides/views/website_slides_templates_lesson_fullscreen.xml b/addons/website_slides/views/website_slides_templates_lesson_fullscreen.xml index 17a835c1c587..090d4b879a03 100644 --- a/addons/website_slides/views/website_slides_templates_lesson_fullscreen.xml +++ b/addons/website_slides/views/website_slides_templates_lesson_fullscreen.xml @@ -116,7 +116,7 @@ <div class="o_wslides_fs_slide_name text-muted" t-esc="slide.name"/> </div> </span> - <ul class="list-unstyled w-100 pt-2 small" t-if="slide.link_ids or (slide.question_ids and not slide.slide_type =='quiz')" > + <ul class="list-unstyled w-100 pt-2 small" t-if="slide.link_ids or slide.slide_resource_ids or (slide.question_ids and not slide.slide_type =='quiz')" > <li t-if="slide.link_ids" t-foreach="slide.link_ids" 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"> <i class="fa fa-link mr-2"/><span t-esc="link.name"/> @@ -125,6 +125,19 @@ <i class="fa fa-link mr-2"/><span t-esc="link.name"/> </span> </li> + <div class="o_wslides_js_course_join pl-0" t-if="slide.slide_resource_ids"> + <t t-if="is_member or slide.channel_id.can_publish"> + <li t-foreach="slide.slide_resource_ids" t-as="resource" class="mb-1"> + <a class="o_wslides_fs_slide_link" t-attf-href="/web/content/slide.slide.resource/#{resource.id}/data?download=true"> + <i class="fa fa-download mr-2"/><span t-esc="resource.name"/> + </a> + </li> + </t> + <li t-elif="slide.channel_id.enroll == 'public'" class="o_wslides_fs_slide_link mb-1"> + <i class="fa fa-download mr-1"/> + <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'" t-att-data-id="slide.id" t-att-data-can-access="can_access" diff --git a/addons/website_slides/views/website_slides_templates_utils.xml b/addons/website_slides/views/website_slides_templates_utils.xml index ad3b75ae2318..dca05280b3b2 100644 --- a/addons/website_slides/views/website_slides_templates_utils.xml +++ b/addons/website_slides/views/website_slides_templates_utils.xml @@ -74,4 +74,11 @@ </xpath> </template> +<template id="join_course_link" name="Join Course Link"> + <a class="o_wslides_js_course_join_link" href="#" + t-att-data-channel-id="slide.channel_id.id"> + Join Course + </a> to download resources +</template> + </data></odoo> -- GitLab