diff --git a/openedx/core/djangoapps/discussions/apps.py b/openedx/core/djangoapps/discussions/apps.py index 7ecc1e0c1a06a2b1b084c1a80278d193b0cb8c21..107346423f6f41bc2c71d688e265bf9d16051bbe 100644 --- a/openedx/core/djangoapps/discussions/apps.py +++ b/openedx/core/djangoapps/discussions/apps.py @@ -5,6 +5,8 @@ from django.apps import AppConfig from edx_django_utils.plugins import PluginSettings from edx_django_utils.plugins import PluginURLs +from openedx.core.djangoapps.plugins.constants import ProjectType + class DiscussionsConfig(AppConfig): """ @@ -13,6 +15,11 @@ class DiscussionsConfig(AppConfig): name = 'openedx.core.djangoapps.discussions' plugin_app = { PluginURLs.CONFIG: { + ProjectType.LMS: { + PluginURLs.NAMESPACE: '', + PluginURLs.REGEX: r'^discussions/', + PluginURLs.RELATIVE_PATH: 'urls', + }, }, PluginSettings.CONFIG: { }, diff --git a/openedx/core/djangoapps/discussions/urls.py b/openedx/core/djangoapps/discussions/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..7bf93308935773f2e55291c13ef84543771edc8f --- /dev/null +++ b/openedx/core/djangoapps/discussions/urls.py @@ -0,0 +1,15 @@ +""" +Configure URL endpoints for the djangoapp +""" +from django.conf.urls import url + +from .views import DiscussionsConfigurationView + + +urlpatterns = [ + url( + r'^api/v0/(?P<course_key_string>.+)$', + DiscussionsConfigurationView.as_view(), + name='discussions', + ), +] diff --git a/openedx/core/djangoapps/discussions/views.py b/openedx/core/djangoapps/discussions/views.py new file mode 100644 index 0000000000000000000000000000000000000000..2eaebc6b12982b677e09599ffcecb99d5dc446a6 --- /dev/null +++ b/openedx/core/djangoapps/discussions/views.py @@ -0,0 +1,110 @@ +""" +Handle view-logic for the djangoapp +""" +from opaque_keys.edx.keys import CourseKey +from opaque_keys import InvalidKeyError +from rest_framework import serializers +from rest_framework.response import Response +from rest_framework.views import APIView + +from openedx.core.lib.api.permissions import IsStaff +from openedx.core.lib.api.view_utils import view_auth_classes + +from .models import DiscussionsConfiguration + + +PROVIDER_FEATURE_MAP = { + 'cs_comments_service': [ + 'discussion-page', + 'embedded-course-sections', + 'lti', + 'wcag-2.1', + ], + 'piazza': [ + 'discussion-page', + 'lti', + ], +} + + +@view_auth_classes() +class DiscussionsConfigurationView(APIView): + """ + Handle configuration-related view-logic + """ + permission_classes = (IsStaff,) + + class Serializer(serializers.BaseSerializer): + """ + Serialize configuration responses + """ + + def create(self, validated_data): + """ + Create and save a new instance + """ + raise NotImplementedError + + def to_internal_data(self, data): + """ + Transform the *incoming* primitive data into a native value. + """ + raise NotImplementedError + + def to_representation(self, instance) -> dict: + """ + Serialize data into a dictionary, to be used as a response + """ + payload = { + 'context_key': str(instance.context_key), + 'enabled': instance.enabled, + 'features': { + 'discussion-page', + 'embedded-course-sections', + 'lti', + 'wcag-2.1', + }, + 'plugin_configuration': instance.plugin_configuration, + 'providers': { + 'active': instance.provider_type or '', + 'available': { + provider: { + 'features': PROVIDER_FEATURE_MAP.get(provider) or [], + } + for provider in instance.available_providers + }, + }, + } + return payload + + def update(self, instance, validated_data): + """ + Update and save an existing instance + """ + raise NotImplementedError + + # pylint: disable=redefined-builtin + def get(self, request, course_key_string, **_kwargs) -> Response: + """ + Handle HTTP/GET requests + """ + course_key = self._validate_course_key(course_key_string) + configuration = DiscussionsConfiguration.get(course_key) + serializer = self.Serializer(configuration) + return Response(serializer.data) + + def _validate_course_key(self, course_key_string: str) -> CourseKey: + """ + Validate and parse a course_key string, if supported + """ + try: + course_key = CourseKey.from_string(course_key_string) + except InvalidKeyError as error: + raise serializers.ValidationError( + f"{course_key_string} is not a valid CourseKey" + ) from error + if course_key.deprecated: + raise serializers.ValidationError( + 'Deprecated CourseKeys (Org/Course/Run) are not supported.' + ) + return course_key