Skip to content

Commit c88cb5e

Browse files
adasatorresrrebollo
authored andcommitted
[ADD] website_slides_require_completion
1 parent 787961f commit c88cb5e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1710
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../../website_slides_require_completion
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import setuptools
2+
3+
setuptools.setup(
4+
setup_requires=['setuptools-odoo'],
5+
odoo_addon=True,
6+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../../website_slides_require_slide
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import setuptools
2+
3+
setuptools.setup(
4+
setup_requires=['setuptools-odoo'],
5+
odoo_addon=True,
6+
)
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
===================================================
2+
Courses's Slides Require Previous Slides Completion
3+
===================================================
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:ee4378b92b13f8ef9d5e45aaa9ac42feeeba2a7ac88e5d58c1b60bea420e0853
11+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
12+
13+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
14+
:target: https://odoo-community.org/page/development-status
15+
:alt: Beta
16+
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
17+
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
18+
:alt: License: AGPL-3
19+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fe--learning-lightgray.png?logo=github
20+
:target: https://github.com/OCA/e-learning/tree/16.0/website_slides_require_completion
21+
:alt: OCA/e-learning
22+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
23+
:target: https://translation.odoo-community.org/projects/e-learning-16-0/e-learning-16-0-website_slides_require_completion
24+
:alt: Translate me on Weblate
25+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
26+
:target: https://runboat.odoo-community.org/builds?repo=OCA/e-learning&target_branch=16.0
27+
:alt: Try me on Runboat
28+
29+
|badge1| |badge2| |badge3| |badge4| |badge5|
30+
31+
This module allows you to enforce the completion of previous slides before accessing the next one in an e-learning course.
32+
33+
**Table of contents**
34+
35+
.. contents::
36+
:local:
37+
38+
Usage
39+
=====
40+
41+
To use this module, you need to:
42+
43+
#. Go to an e-learning course form view "Options" tab
44+
#. Under the "Display" section, check the "Require previous slides completion" option
45+
46+
Bug Tracker
47+
===========
48+
49+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/e-learning/issues>`_.
50+
In case of trouble, please check there if your issue has already been reported.
51+
If you spotted it first, help us to smash it by providing a detailed and welcomed
52+
`feedback <https://github.com/OCA/e-learning/issues/new?body=module:%20website_slides_require_completion%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
53+
54+
Do not contact contributors directly about support or help with technical issues.
55+
56+
Credits
57+
=======
58+
59+
Authors
60+
~~~~~~~
61+
62+
* Binhex
63+
64+
Contributors
65+
~~~~~~~~~~~~
66+
67+
* Adasat Torres de León <[email protected]>
68+
* Rolando Pérez Rebollo <[email protected]>
69+
70+
Maintainers
71+
~~~~~~~~~~~
72+
73+
This module is maintained by the OCA.
74+
75+
.. image:: https://odoo-community.org/logo.png
76+
:alt: Odoo Community Association
77+
:target: https://odoo-community.org
78+
79+
OCA, or the Odoo Community Association, is a nonprofit organization whose
80+
mission is to support the collaborative development of Odoo features and
81+
promote its widespread use.
82+
83+
.. |maintainer-adasatorres| image:: https://github.com/adasatorres.png?size=40px
84+
:target: https://github.com/adasatorres
85+
:alt: adasatorres
86+
87+
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
88+
89+
|maintainer-adasatorres|
90+
91+
This module is part of the `OCA/e-learning <https://github.com/OCA/e-learning/tree/16.0/website_slides_require_completion>`_ project on GitHub.
92+
93+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Copyright 2025 Binhex - Adasat Torres de León
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
4+
from . import models
5+
6+
from . import controllers
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Copyright 2025 Binhex - Adasat Torres de León
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
4+
{
5+
"name": "Courses's Slides Require Previous Slides Completion",
6+
"summary": """If checked, the user has to complete previous slides before """
7+
"""moving to the next one.""",
8+
"version": "16.0.1.0.0",
9+
"category": "Website/eLearning",
10+
"author": "Binhex, Odoo Community Association (OCA)",
11+
"website": "https://github.com/OCA/e-learning",
12+
"depends": ["web", "website_slides"],
13+
"data": [
14+
"views/slide_channel_views.xml",
15+
"views/website_slides_templates_course.xml",
16+
"views/website_slides_templates_lesson_fullscreen.xml",
17+
"views/website_slides_templates_lesson_embed.xml",
18+
],
19+
"assets": {
20+
"web.assets_frontend": [
21+
"website_slides_require_completion/static/src/js/*.js",
22+
"website_slides_require_completion/static/src/xml/*.xml",
23+
],
24+
},
25+
"license": "AGPL-3",
26+
"maintainers": ["adasatorres"],
27+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright 2025 Binhex - Adasat Torres de León
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
from . import main
4+
from . import binary
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import io
2+
3+
from PIL import Image, ImageDraw, ImageFont
4+
5+
from odoo import _
6+
from odoo.exceptions import UserError
7+
from odoo.http import Stream, request
8+
9+
from odoo.addons.web.controllers.binary import Binary
10+
11+
12+
class WebsiteSlidesBinary(Binary):
13+
def content_image(
14+
self,
15+
xmlid=None,
16+
model="ir.attachment",
17+
id=None, # pylint: disable=redefined-builtin
18+
field="raw",
19+
filename_field="name",
20+
filename=None,
21+
mimetype=None,
22+
unique=False,
23+
download=False,
24+
width=0,
25+
height=0,
26+
crop=False,
27+
access_token=None,
28+
nocache=False,
29+
):
30+
if model == "slide.slide" and id:
31+
try:
32+
slide = request.env["ir.binary"]._find_record(
33+
xmlid, model, id and int(id), access_token
34+
)
35+
except UserError:
36+
slide = False
37+
38+
if slide and not slide.can_self_view_slide():
39+
msg = _(
40+
"Slide not found or you must complete the previous slide first."
41+
)
42+
raw = self.text_image(msg).getvalue()
43+
44+
# Manual stream instantiation
45+
stream = Stream()
46+
stream.type = "data"
47+
stream.data = raw
48+
stream.mimetype = "image/png"
49+
stream.download_name = f"slide-{slide.id}-error.png"
50+
stream.size = len(raw)
51+
52+
if request.httprequest.args.get("access_token"):
53+
stream.public = True
54+
55+
return stream.get_response()
56+
57+
return super().content_image(
58+
xmlid,
59+
model,
60+
id,
61+
field,
62+
filename_field,
63+
filename,
64+
mimetype,
65+
unique,
66+
download,
67+
width,
68+
height,
69+
crop,
70+
access_token,
71+
nocache,
72+
)
73+
74+
def text_image(self, text, color="black", bg="white"):
75+
try:
76+
font = ImageFont.truetype("DejaVuSans.ttf", 20)
77+
except OSError:
78+
font = ImageFont.load_default()
79+
80+
# Calculate size
81+
dummy_img = Image.new("RGB", (1, 1))
82+
draw = ImageDraw.Draw(dummy_img)
83+
text_width, text_height = draw.textsize(text, font=font)
84+
85+
# Create image
86+
img = Image.new("RGB", (text_width + 10, text_height + 10), color=bg)
87+
draw = ImageDraw.Draw(img)
88+
draw.text((5, 5), text, font=font, fill=color)
89+
90+
# Save to buffer
91+
buffer = io.BytesIO()
92+
img.save(buffer, format="PNG")
93+
buffer.seek(0)
94+
return buffer
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Copyright 2025 Binhex - Adasat Torres de León
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
from odoo import _, http
4+
from odoo.exceptions import AccessError
5+
from odoo.http import request
6+
7+
from odoo.addons.website_slides.controllers.main import WebsiteSlides
8+
9+
10+
class WebsiteSlideRequired(WebsiteSlides):
11+
"""Override WebsiteSlides to add the required question functionality."""
12+
13+
@http.route()
14+
def slide_view(self, slide, **kwargs):
15+
"""Override slide_view to check if the slide is required."""
16+
if slide.channel_id.require_slides_completion:
17+
slide = slide.with_context(skip_mark_completed=True)
18+
return super().slide_view(slide, **kwargs)
19+
20+
def _slide_mark_completed(self, slide):
21+
if not slide.env.context.get("skip_mark_completed"):
22+
return super()._slide_mark_completed(slide)
23+
24+
def _slide_embed(self, slide_id, page="1", is_external_embed=False, **kw):
25+
try:
26+
slide = request.env["slide.slide"].browse(slide_id)
27+
except AccessError:
28+
return request.render("website_slides.embed_slide_forbidden", {})
29+
30+
if not slide.can_self_view_slide():
31+
return request.render(
32+
"website_slides_require_completion.embed_require_previous_slide_completion",
33+
{},
34+
)
35+
return super()._slide_embed(slide_id, page, is_external_embed, **kw)
36+
37+
def _get_slide_quiz_data(self, slide):
38+
if not slide.can_self_view_slide():
39+
values = {
40+
"slide_description": _(
41+
"You must complete the previous slides before accessing this slide."
42+
)
43+
if slide.channel_id.is_member
44+
else "",
45+
"slide_questions": [],
46+
"slide_resource_ids": [],
47+
}
48+
return values
49+
else:
50+
return super()._get_slide_quiz_data(slide)

0 commit comments

Comments
 (0)