Skip to content

Commit

Permalink
feat!: configure zoom live conference calls settings if exists at org…
Browse files Browse the repository at this point in the history
…anization level (#415)

* feat!: configure zoom globally

* fix!: fix ENV_TOKENS error

---------

Co-authored-by: Muhammad Faraz  Maqsood <[email protected]>
  • Loading branch information
Faraz32123 and Muhammad Faraz Maqsood authored Sep 11, 2023
1 parent 1699d14 commit de14328
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 6 deletions.
10 changes: 10 additions & 0 deletions openedx/core/djangoapps/course_live/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,15 @@ class CourseLiveConfig(AppConfig):
PluginURLs.REGEX: r'^api/course_live/',
PluginURLs.RELATIVE_PATH: 'urls',
},
'settings_config': {
'lms.djangoapp': {
'common': {'relative_path': 'settings.common'},
'production': {'relative_path': 'settings.production'},
},
'cms.djangoapp': {
'common': {'relative_path': 'settings.common'},
'production': {'relative_path': 'settings.production'},
},
},
}
}
38 changes: 36 additions & 2 deletions openedx/core/djangoapps/course_live/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def has_valid_global_keys(self) -> bool:
raise NotImplementedError()


class Zoom(LiveProvider):
class Zoom(LiveProvider, HasGlobalCredentials):
"""
Zoom LTI PRO live provider
"""
Expand All @@ -92,9 +92,43 @@ class Zoom(LiveProvider):
]

@property
def is_enabled(self):
def has_free_tier(self) -> bool:
"""
Check if free tier is enabled by checking for valid keys
"""
return self.has_valid_global_keys()

@property
def is_enabled(self) -> bool:
return True

@staticmethod
def get_global_keys() -> Dict:
"""
Get keys from settings
"""
try:
COURSE_LIVE_GLOBAL_CREDENTIALS = {
"KEY": settings.ZOOM_BUTTON_GLOBAL_KEY,
"SECRET": settings.ZOOM_BUTTON_GLOBAL_SECRET,
"URL": settings.ZOOM_BUTTON_GLOBAL_URL,
}
return COURSE_LIVE_GLOBAL_CREDENTIALS
except AttributeError:
return {}

def has_valid_global_keys(self) -> bool:
"""
Check if keys are valid and not None
"""
credentials = self.get_global_keys()
if credentials:
self.key = credentials.get('KEY')
self.secret = credentials.get('SECRET')
self.url = credentials.get('URL')
return bool(self.key and self.secret and self.url)
return False


class BigBlueButton(LiveProvider, HasGlobalCredentials):
"""
Expand Down
Empty file.
19 changes: 19 additions & 0 deletions openedx/core/djangoapps/course_live/settings/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""
Common settings for Live Video Conferencing Tools.
"""


def plugin_settings(settings):
"""
Common settings for Live Video Conferencing Tools
Set these variables in the Tutor Config or lms.yml for local testing
ZOOM_BUTTON_GLOBAL_KEY: <ZOOM_BUTTON_GLOBAL_KEY>
ZOOM_BUTTON_GLOBAL_SECRET: <ZOOM_BUTTON_GLOBAL_SECRET>
ZOOM_BUTTON_GLOBAL_URL: <ZOOM_BUTTON_GLOBAL_URL>
ZOOM_INSTRUCTOR_EMAIL: <ZOOM_INSTRUCTOR_EMAIL>
"""
# zoom settings
settings.ZOOM_BUTTON_GLOBAL_KEY = ""
settings.ZOOM_BUTTON_GLOBAL_SECRET = ""
settings.ZOOM_BUTTON_GLOBAL_URL = ""
settings.ZOOM_INSTRUCTOR_EMAIL = ""
23 changes: 23 additions & 0 deletions openedx/core/djangoapps/course_live/settings/production.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""
Production settings for Live Video Conferencing Tools.
"""


def plugin_settings(settings):
"""
Production settings for Live Video Conferencing Tools
"""

# zoom settings
settings.ZOOM_BUTTON_GLOBAL_KEY = settings.ENV_TOKENS.get(
"ZOOM_BUTTON_GLOBAL_KEY", settings.ZOOM_BUTTON_GLOBAL_KEY
)
settings.ZOOM_BUTTON_GLOBAL_SECRET = settings.ENV_TOKENS.get(
"ZOOM_BUTTON_GLOBAL_SECRET", settings.ZOOM_BUTTON_GLOBAL_SECRET
)
settings.ZOOM_BUTTON_GLOBAL_URL = settings.ENV_TOKENS.get(
"ZOOM_BUTTON_GLOBAL_URL", settings.ZOOM_BUTTON_GLOBAL_URL
)
settings.ZOOM_INSTRUCTOR_EMAIL = settings.ENV_TOKENS.get(
"ZOOM_INSTRUCTOR_EMAIL", settings.ZOOM_INSTRUCTOR_EMAIL
)
5 changes: 4 additions & 1 deletion openedx/core/djangoapps/course_live/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
from openedx.core.djangoapps.course_live.views import (
CourseLiveConfigurationView,
CourseLiveIframeView,
CourseLiveProvidersView
CourseLiveProvidersView,
CourseLiveZoomView,
)

urlpatterns = [
Expand All @@ -19,4 +20,6 @@
CourseLiveProvidersView.as_view(), name='live_providers'),
re_path(fr'^iframe/{settings.COURSE_ID_PATTERN}/?$',
CourseLiveIframeView.as_view(), name='live_iframe'),
re_path(rf"^configure_zoom/{settings.COURSE_ID_PATTERN}/?$",
CourseLiveZoomView.as_view(), name="zoom"),
]
108 changes: 105 additions & 3 deletions openedx/core/djangoapps/course_live/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
"""
from typing import Dict

from django.conf import settings
import edx_api_doc_tools as apidocs
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
from edx_rest_framework_extensions.auth.session.authentication import SessionAuthenticationAllowInactiveUser
from edx_rest_framework_extensions.auth.session.authentication import (
SessionAuthenticationAllowInactiveUser,
)
from lti_consumer.api import get_lti_pii_sharing_state_for_course
from opaque_keys.edx.keys import CourseKey
from rest_framework import permissions, status
Expand All @@ -25,6 +28,27 @@
from .serializers import CourseLiveConfigurationSerializer


def is_zoom_creds_global(serialized_data):
"""
returns True or False if zoom creds are global or not respoectively
"""
if serialized_data.get("provider_type", "") == "zoom":
key = serialized_data.get("lti_configuration", {}).get("lti_1p1_client_key", "")
url = serialized_data.get("lti_configuration", {}).get("lti_1p1_launch_url", "")
email = (
serialized_data.get("lti_configuration", {})
.get("lti_config", "")
.get("additional_parameters", {})
.get("custom_instructor_email", "")
)
global_key = settings.ZOOM_BUTTON_GLOBAL_KEY
global_url = settings.ZOOM_BUTTON_GLOBAL_URL
global_email = settings.ZOOM_INSTRUCTOR_EMAIL
if key == global_key and url == global_url and email == global_email:
return True
return False


class CourseLiveConfigurationView(APIView):
"""
View for configuring CourseLive settings.
Expand Down Expand Up @@ -62,8 +86,12 @@ def get(self, request: Request, course_id: str) -> Response:
"pii_sharing_allowed": get_lti_pii_sharing_state_for_course(course_id),
"course_id": course_id
})
serialized_data = serializer.data
serialized_data["global_zoom_creds_enabled"] = is_zoom_creds_global(
serialized_data
)

return Response(serializer.data)
return Response(serialized_data)

@apidocs.schema(
parameters=[
Expand Down Expand Up @@ -136,7 +164,12 @@ def post(self, request, course_id: str) -> Response:
if not serializer.is_valid():
raise ValidationError(serializer.errors)
serializer.save()
return Response(serializer.data)
serialized_data = serializer.data
serialized_data["global_zoom_creds_enabled"] = is_zoom_creds_global(
serialized_data
)

return Response(serialized_data)


class CourseLiveProvidersView(APIView):
Expand Down Expand Up @@ -277,3 +310,72 @@ def get(self, request, course_id: str, **_kwargs) -> Response:
"iframe": iframe.content
}
return Response(data, status=status.HTTP_200_OK)


class CourseLiveZoomView(APIView):
"""
View for configuring Zoom Global Credentials settings.
"""
@ensure_valid_course_key
@verify_course_exists()
def post(self, request, course_id: str) -> Response:
"""
Handle HTTP/POST requests
"""
request.data["enabled"] = True
request.data["lti_configuration"] = {
"lti_1p1_client_key": settings.ZOOM_BUTTON_GLOBAL_KEY,
"lti_1p1_client_secret": settings.ZOOM_BUTTON_GLOBAL_SECRET,
"lti_1p1_launch_url": settings.ZOOM_BUTTON_GLOBAL_URL,
"lti_config": {
"additional_parameters": {
"custom_instructor_email": settings.ZOOM_INSTRUCTOR_EMAIL,
}
},
"version": "lti_1p1",
}
request.data["provider_type"] = "zoom"
request.data["pii_sharing_allowed"] = True
request.data["free_tier"] = False

pii_sharing_allowed = get_lti_pii_sharing_state_for_course(course_id)
provider = (
ProviderManager()
.get_enabled_providers()
.get(request.data.get("provider_type"))
)

if not pii_sharing_allowed and provider.requires_pii_sharing():
return Response(
{
"pii_sharing_allowed": pii_sharing_allowed,
"message": "PII sharing is not allowed on this course",
}
)
if (
provider
and not provider.additional_parameters
and request.data.get("lti_configuration", False)
):
# Add empty lti config if none is provided in case additional params are not required
request.data["lti_configuration"]["lti_config"] = {
"additional_parameters": {}
}
configuration = CourseLiveConfiguration.objects.filter(
course_key=course_id
).last()
serializer = CourseLiveConfigurationSerializer(
configuration,
data=request.data,
context={
"pii_sharing_allowed": pii_sharing_allowed,
"course_id": course_id,
"provider": provider,
},
)
if not serializer.is_valid():
raise ValidationError(serializer.errors)
serializer.save()
serialized_data = serializer.data
serialized_data["global_zoom_creds_enabled"] = True
return Response(serialized_data)

0 comments on commit de14328

Please sign in to comment.