Skip to content

Commit 361c7e6

Browse files
committed
🚩(backend) add feature flags to enable/disable core resources
Currently, it is not possible to disable the core resources for the whole site. Introducing three settings flags (DOCUMENT_ENABLED, VIDEO_ENABLED and WEBINAR_ENABLED) selectively disabling these resources. These flags apply to both the standalone site and the LTI site.
1 parent 33db6d5 commit 361c7e6

File tree

8 files changed

+196
-30
lines changed

8 files changed

+196
-30
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Versioning](https://semver.org/spec/v2.0.0.html).
1010

1111
### Added
1212

13+
- Add feature flags to enable/disable core resources for the whole site
1314
- Add support for storing markdown images on Scaleway S3
1415
- Add support for storing deposited files on Scaleway S3
1516
- Add support for storing classroom documents on Scaleway S3

env.d/development.dist

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ DJANGO_LTI_CONFIG_DESCRIPTION=marsha_32x32_blue.png
3131
DJANGO_LTI_CONFIG_URL=https://github.com/openfun/marsha
3232
3333

34+
# Core applications
35+
DJANGO_DOCUMENT_ENABLED=True
36+
DJANGO_VIDEO_ENABLED=True
37+
DJANGO_WEBINAR_ENABLED=True
38+
3439
# BBB server credentials
3540
DJANGO_BBB_ENABLED=False
3641
DJANGO_BBB_API_ENDPOINT=https://example.com/bbb/api

env.d/test

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ DJANGO_CLOUDFRONT_DOMAIN=abc.cloudfront.net
1818
DJANGO_SCW_EDGE_SERVICE_DOMAIN=abc.svc.edge.scw.cloud
1919
DJANGO_UPDATE_STATE_SHARED_SECRETS=dummy,secret
2020

21+
# Core applications
22+
DJANGO_DOCUMENT_ENABLED=True
23+
DJANGO_VIDEO_ENABLED=True
24+
DJANGO_WEBINAR_ENABLED=True
25+
2126
# BBB
2227
DJANGO_BBB_ENABLED=False
2328
DJANGO_BBB_API_SECRET=ThisIsAnExampleKeyForDevPurposeOnly

src/backend/marsha/core/api/base.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,15 @@
1010
from waffle import switch_is_active
1111

1212
from marsha.core import serializers
13-
from marsha.core.defaults import READY, SENTRY, TRANSCRIPTION, VOD_CONVERT
13+
from marsha.core.defaults import (
14+
DOCUMENT,
15+
READY,
16+
SENTRY,
17+
TRANSCRIPTION,
18+
VIDEO,
19+
VOD_CONVERT,
20+
WEBINAR,
21+
)
1422
from marsha.core.models import SiteConfig, Video
1523
from marsha.core.signals import signal_object_uploaded
1624
from marsha.core.simple_jwt.tokens import PlaylistAccessToken
@@ -219,6 +227,14 @@ def get_frontend_configuration(request):
219227

220228
domain = request.get_host()
221229
inactive_resources = []
230+
231+
if not settings.DOCUMENT_ENABLED:
232+
inactive_resources.append(DOCUMENT)
233+
if not settings.VIDEO_ENABLED:
234+
inactive_resources.append(VIDEO)
235+
if not settings.WEBINAR_ENABLED:
236+
inactive_resources.append(WEBINAR)
237+
222238
vod_conversion_enabled = True
223239

224240
is_default_site = domain in settings.FRONTEND_HOME_URL
Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Module dedicated to lti select feature for core."""
22

3+
from django.conf import settings
34
from django.db.models import Q
45

56
from marsha.core.defaults import ENDED, LTI_DOCUMENT_ROUTE, LTI_VIDEO_ROUTE
@@ -12,29 +13,40 @@
1213

1314
def get_lti_select_config():
1415
"""return config for lti select."""
15-
return [
16-
{
17-
"name": "document",
18-
"serializer": DocumentSelectLTISerializer,
19-
"model": Document,
20-
"route": LTI_DOCUMENT_ROUTE,
21-
},
22-
{
23-
"name": "video",
24-
"serializer": VideoSelectLTISerializer,
25-
"model": Video,
26-
"route": LTI_VIDEO_ROUTE,
27-
"extra_filter": lambda queryset: queryset.filter(
28-
Q(live_type__isnull=True) | Q(live_state=ENDED)
29-
),
30-
},
31-
{
32-
"name": "webinar",
33-
"serializer": VideoSelectLTISerializer,
34-
"model": Video,
35-
"route": LTI_VIDEO_ROUTE,
36-
"extra_filter": lambda queryset: queryset.filter(
37-
live_type__isnull=False
38-
).exclude(live_state=ENDED),
39-
},
40-
]
16+
lti_config = []
17+
18+
if settings.DOCUMENT_ENABLED:
19+
lti_config.append(
20+
{
21+
"name": "document",
22+
"serializer": DocumentSelectLTISerializer,
23+
"model": Document,
24+
"route": LTI_DOCUMENT_ROUTE,
25+
}
26+
)
27+
if settings.VIDEO_ENABLED:
28+
lti_config.append(
29+
{
30+
"name": "video",
31+
"serializer": VideoSelectLTISerializer,
32+
"model": Video,
33+
"route": LTI_VIDEO_ROUTE,
34+
"extra_filter": lambda queryset: queryset.filter(
35+
Q(live_type__isnull=True) | Q(live_state=ENDED)
36+
),
37+
}
38+
)
39+
if settings.WEBINAR_ENABLED:
40+
lti_config.append(
41+
{
42+
"name": "webinar",
43+
"serializer": VideoSelectLTISerializer,
44+
"model": Video,
45+
"route": LTI_VIDEO_ROUTE,
46+
"extra_filter": lambda queryset: queryset.filter(
47+
live_type__isnull=False
48+
).exclude(live_state=ENDED),
49+
}
50+
)
51+
52+
return lti_config

src/backend/marsha/core/tests/views/test_lti_select.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,74 @@ def test_views_lti_select(self):
149149
{"can_access_dashboard": False, "can_update": True},
150150
)
151151

152+
@override_settings(
153+
DOCUMENT_ENABLED=False, VIDEO_ENABLED=False, WEBINAR_ENABLED=False
154+
)
155+
def test_views_lti_select_disabled(self):
156+
"""Frontend context flags should be disabled when flags are disabled."""
157+
lti_consumer_parameters = {
158+
"roles": random.choice(["instructor", "administrator"]),
159+
"content_item_return_url": "https://lti-consumer.site/lti",
160+
"context_id": "sent_lti_context_id",
161+
"title": "Sent LMS activity title",
162+
"text": "Sent LMS activity text",
163+
}
164+
lti_parameters, passport = generate_passport_and_signed_lti_parameters(
165+
url="http://testserver/lti/select/",
166+
lti_parameters=lti_consumer_parameters,
167+
)
168+
169+
resolutions = [144]
170+
playlist = PlaylistFactory(
171+
lti_id=lti_parameters.get("context_id"),
172+
consumer_site=passport.consumer_site,
173+
)
174+
VideoFactory(
175+
playlist=playlist,
176+
uploaded_on=timezone.now(),
177+
resolutions=resolutions,
178+
position=1,
179+
)
180+
DocumentFactory(
181+
playlist=playlist,
182+
uploaded_on=timezone.now(),
183+
)
184+
VideoFactory(
185+
playlist=playlist,
186+
live_state=IDLE,
187+
live_type=JITSI,
188+
position=2,
189+
)
190+
VideoFactory(
191+
playlist=playlist,
192+
live_state=ENDED,
193+
live_type=JITSI,
194+
position=3,
195+
)
196+
197+
response = self.client.post(
198+
"/lti/select/",
199+
lti_parameters,
200+
HTTP_REFERER="http://testserver",
201+
)
202+
self.assertEqual(response.status_code, 200)
203+
self.assertContains(response, "<html>")
204+
205+
match = re.search(
206+
'<div id="marsha-frontend-data" data-context="(.*)">',
207+
response.content.decode("utf-8"),
208+
)
209+
context = json.loads(unescape(match.group(1)))
210+
211+
self.assertFalse(context.get("flags").get("document"))
212+
self.assertIsNone(context.get("document"))
213+
214+
self.assertFalse(context.get("flags").get("video"))
215+
self.assertIsNone(context.get("video"))
216+
217+
self.assertFalse(context.get("flags").get("webinar"))
218+
self.assertIsNone(context.get("webinar"))
219+
152220
def test_views_lti_select_video(self):
153221
"""
154222
Validate the context passed to the frontend app for an LTI Content selection targeting
@@ -242,6 +310,60 @@ def test_views_lti_select_video(self):
242310
{"can_access_dashboard": False, "can_update": True},
243311
)
244312

313+
@override_settings(VIDEO_ENABLED=False)
314+
def test_views_lti_select_video_disabled(self):
315+
"""
316+
Frontend context flag should be disabled when flag is disabled."""
317+
lti_consumer_parameters = {
318+
"roles": random.choice(["instructor", "administrator"]),
319+
"content_item_return_url": "https://lti-consumer.site/lti",
320+
"context_id": "sent_lti_context_id",
321+
"title": "Sent LMS activity title",
322+
"text": "Sent LMS activity text",
323+
}
324+
lti_parameters, passport = generate_passport_and_signed_lti_parameters(
325+
url="http://testserver/lti/select/video/",
326+
lti_parameters=lti_consumer_parameters,
327+
)
328+
329+
playlist = PlaylistFactory(
330+
lti_id=lti_parameters.get("context_id"),
331+
consumer_site=passport.consumer_site,
332+
)
333+
VideoFactory(
334+
playlist=playlist,
335+
uploaded_on=timezone.now(),
336+
resolutions=[144],
337+
position=1,
338+
)
339+
DocumentFactory(
340+
playlist=playlist,
341+
uploaded_on=timezone.now(),
342+
)
343+
VideoFactory(
344+
playlist=playlist,
345+
live_state=IDLE,
346+
live_type=JITSI,
347+
position=2,
348+
)
349+
350+
response = self.client.post(
351+
"/lti/select/video/",
352+
lti_parameters,
353+
HTTP_REFERER="http://testserver",
354+
)
355+
self.assertEqual(response.status_code, 200)
356+
self.assertContains(response, "<html>")
357+
358+
match = re.search(
359+
'<div id="marsha-frontend-data" data-context="(.*)">',
360+
response.content.decode("utf-8"),
361+
)
362+
context = json.loads(unescape(match.group(1)))
363+
364+
self.assertFalse(context.get("flags").get("video"))
365+
self.assertIsNone(context.get("video"))
366+
245367
@override_settings(LTI_CONFIG_TITLE="Marsha")
246368
def test_views_lti_select_default_title(self):
247369
"""Validate the context passed to the frontend app for an LTI Content selection."""

src/backend/marsha/core/views.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,9 @@ def _get_base_app_data(self):
133133
DEPOSIT: settings.DEPOSIT_ENABLED,
134134
LIVE_RAW: settings.LIVE_RAW_ENABLED,
135135
MARKDOWN: settings.MARKDOWN_ENABLED,
136-
VIDEO: True,
137-
WEBINAR: True,
138-
DOCUMENT: True,
136+
VIDEO: settings.VIDEO_ENABLED,
137+
WEBINAR: settings.WEBINAR_ENABLED,
138+
DOCUMENT: settings.DOCUMENT_ENABLED,
139139
TRANSCRIPTION: switch_is_active(TRANSCRIPTION),
140140
},
141141
"release": settings.RELEASE,

src/backend/marsha/settings.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,11 @@ class Base(Configuration):
416416
LTI_REPLAY_PROTECTION_CACHE_DURATION = values.PositiveIntegerValue(3600) # 1 hour
417417
LTI_REPLAY_PROTECTION_ENABLED = values.BooleanValue(True)
418418

419+
# Core applications
420+
DOCUMENT_ENABLED = values.BooleanValue(True)
421+
VIDEO_ENABLED = values.BooleanValue(True)
422+
WEBINAR_ENABLED = values.BooleanValue(True)
423+
419424
# BBB
420425
BBB_ENABLED = values.BooleanValue(False)
421426
BBB_API_ENDPOINT = values.Value()

0 commit comments

Comments
 (0)