From b953841893acb03721835bd861cdefe91adc9f64 Mon Sep 17 00:00:00 2001 From: Alan Zarembok Date: Sun, 25 Aug 2019 14:06:28 -0400 Subject: [PATCH] PROD-202: Add per-course flag to block access to youtube videos. Can be set for courses that have lost access to their youtube channels. --- .../xmodule/video_module/video_module.py | 15 ++++++-- openedx/core/djangoapps/video_config/admin.py | 12 ++++++- openedx/core/djangoapps/video_config/forms.py | 11 ++++++ .../0008_courseyoutubeblockedflag.py | 33 +++++++++++++++++ .../core/djangoapps/video_config/models.py | 35 +++++++++++++++++++ 5 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 openedx/core/djangoapps/video_config/migrations/0008_courseyoutubeblockedflag.py diff --git a/common/lib/xmodule/xmodule/video_module/video_module.py b/common/lib/xmodule/xmodule/video_module/video_module.py index be50ca0944..cb6a3d03cc 100644 --- a/common/lib/xmodule/xmodule/video_module/video_module.py +++ b/common/lib/xmodule/xmodule/video_module/video_module.py @@ -30,7 +30,7 @@ from xblock.core import XBlock from xblock.fields import ScopeIds from xblock.runtime import KvsFieldData -from openedx.core.djangoapps.video_config.models import HLSPlaybackEnabledFlag +from openedx.core.djangoapps.video_config.models import HLSPlaybackEnabledFlag, CourseYoutubeBlockedFlag from openedx.core.djangoapps.video_pipeline.config.waffle import DEPRECATE_YOUTUBE, waffle_flags from openedx.core.lib.cache_utils import request_cached from openedx.core.lib.license import LicenseMixin @@ -199,6 +199,12 @@ class VideoBlock( # is enabled for this course return waffle_flags()[DEPRECATE_YOUTUBE].is_enabled(self.location.course_key) + def youtube_disabled_for_course(self): + if CourseYoutubeBlockedFlag.feature_enabled(self.location.course_key): + return True + else: + return False + def prioritize_hls(self, youtube_streams, html5_sources): """ Decide whether hls can be prioritized as primary playback or not. @@ -206,7 +212,7 @@ class VideoBlock( If both the youtube and hls sources are present then make decision on flag If only either youtube or hls is present then play whichever is present """ - yt_present = bool(youtube_streams.strip()) + yt_present = bool(youtube_streams.strip()) if youtube_streams else False hls_present = any(source for source in html5_sources if source.strip().endswith('.m3u8')) if yt_present and hls_present: @@ -340,7 +346,10 @@ class VideoBlock( cdn_eval = False cdn_exp_group = None - self.youtube_streams = youtube_streams or create_youtube_string(self) # pylint: disable=W0201 + if self.youtube_disabled_for_course(): + self.youtube_streams = '' + else: + self.youtube_streams = youtube_streams or create_youtube_string(self) # pylint: disable=W0201 settings_service = self.runtime.service(self, 'settings') diff --git a/openedx/core/djangoapps/video_config/admin.py b/openedx/core/djangoapps/video_config/admin.py index 6f1416ad45..e34439048c 100644 --- a/openedx/core/djangoapps/video_config/admin.py +++ b/openedx/core/djangoapps/video_config/admin.py @@ -8,10 +8,12 @@ from django.contrib import admin from openedx.core.djangoapps.video_config.forms import ( CourseHLSPlaybackFlagAdminForm, + CourseYoutubeBlockedFlagAdminForm, CourseVideoTranscriptFlagAdminForm, ) from openedx.core.djangoapps.video_config.models import ( CourseHLSPlaybackEnabledFlag, HLSPlaybackEnabledFlag, + CourseYoutubeBlockedFlag, CourseVideoTranscriptEnabledFlag, VideoTranscriptEnabledFlag, TranscriptMigrationSetting, MigrationEnqueuedCourse, VideoThumbnailSetting, UpdatedCourseVideos, @@ -44,6 +46,14 @@ class CourseHLSPlaybackEnabledFlagAdmin(CourseSpecificEnabledFlagBaseAdmin): form = CourseHLSPlaybackFlagAdminForm +class CourseYoutubeBlockedFlagAdmin(CourseSpecificEnabledFlagBaseAdmin): + """ + Admin of youtube blocking feature on course-by-course basis. + Allows searching by course id. + """ + form = CourseYoutubeBlockedFlagAdminForm + + class CourseVideoTranscriptEnabledFlagAdmin(CourseSpecificEnabledFlagBaseAdmin): """ Admin of Video Transcript feature on course-by-course basis. @@ -79,7 +89,7 @@ class UpdatedCourseVideosAdmin(admin.ModelAdmin): admin.site.register(HLSPlaybackEnabledFlag, ConfigurationModelAdmin) admin.site.register(CourseHLSPlaybackEnabledFlag, CourseHLSPlaybackEnabledFlagAdmin) - +admin.site.register(CourseYoutubeBlockedFlag, CourseYoutubeBlockedFlagAdmin) admin.site.register(VideoTranscriptEnabledFlag, ConfigurationModelAdmin) admin.site.register(CourseVideoTranscriptEnabledFlag, CourseVideoTranscriptEnabledFlagAdmin) admin.site.register(TranscriptMigrationSetting, ConfigurationModelAdmin) diff --git a/openedx/core/djangoapps/video_config/forms.py b/openedx/core/djangoapps/video_config/forms.py index 4b0064286c..a5d634f566 100644 --- a/openedx/core/djangoapps/video_config/forms.py +++ b/openedx/core/djangoapps/video_config/forms.py @@ -8,6 +8,7 @@ from django import forms from openedx.core.djangoapps.video_config.models import ( CourseHLSPlaybackEnabledFlag, + CourseYoutubeBlockedFlag, CourseVideoTranscriptEnabledFlag, ) from openedx.core.lib.courses import clean_course_id @@ -41,6 +42,16 @@ class CourseHLSPlaybackFlagAdminForm(CourseSpecificFlagAdminBaseForm): fields = '__all__' +class CourseYoutubeBlockedFlagAdminForm(CourseSpecificFlagAdminBaseForm): + """ + Form for course-specific youtube blocking configuration. + """ + + class Meta(object): + model = CourseYoutubeBlockedFlag + fields = '__all__' + + class CourseVideoTranscriptFlagAdminForm(CourseSpecificFlagAdminBaseForm): """ Form for course-specific Video Transcript configuration. diff --git a/openedx/core/djangoapps/video_config/migrations/0008_courseyoutubeblockedflag.py b/openedx/core/djangoapps/video_config/migrations/0008_courseyoutubeblockedflag.py new file mode 100644 index 0000000000..6047011c02 --- /dev/null +++ b/openedx/core/djangoapps/video_config/migrations/0008_courseyoutubeblockedflag.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.23 on 2019-08-25 16:11 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import opaque_keys.edx.django.models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('video_config', '0007_videothumbnailsetting_offset'), + ] + + operations = [ + migrations.CreateModel( + name='CourseYoutubeBlockedFlag', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')), + ('enabled', models.BooleanField(default=False, verbose_name='Enabled')), + ('course_id', opaque_keys.edx.django.models.CourseKeyField(db_index=True, max_length=255)), + ('changed_by', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='Changed by')), + ], + options={ + 'ordering': ('-change_date',), + 'abstract': False, + }, + ), + ] diff --git a/openedx/core/djangoapps/video_config/models.py b/openedx/core/djangoapps/video_config/models.py index 8178357e8e..f04696c256 100644 --- a/openedx/core/djangoapps/video_config/models.py +++ b/openedx/core/djangoapps/video_config/models.py @@ -78,6 +78,41 @@ class CourseHLSPlaybackEnabledFlag(ConfigurationModel): ) +class CourseYoutubeBlockedFlag(ConfigurationModel): + """ + Disables the playback of youtube videos for a given course. + If the flag is present for the course, and set to "enabled", + then youtube is disabled for that course. + .. no_pii + """ + KEY_FIELDS = ('course_id',) + + course_id = CourseKeyField(max_length=255, db_index=True) + + @classmethod + def feature_enabled(cls, course_id): + """ + Determine if the youtube blocking feature is enabled for the specified course id. + Argument: + course_id (CourseKey): course id for whom feature will be checked + """ + feature = (CourseYoutubeBlockedFlag.objects + .filter(course_id=course_id) + .order_by('-change_date') + .first()) + return feature.enabled if feature else False + + def __unicode__(self): + not_en = "Not " + if self.enabled: + not_en = "" + + return u"Course '{course_key}': Youtube Block {not_enabled}Enabled".format( + course_key=six.text_type(self.course_id), + not_enabled=not_en + ) + + class VideoTranscriptEnabledFlag(ConfigurationModel): """ Enables Video Transcript across the platform.