AA-36: Link to toggle calendar sync
This commit is contained in:
@@ -3,6 +3,9 @@
|
||||
|
||||
from .models import UserCalendarSyncConfig
|
||||
|
||||
SUBSCRIBE = 'subscribe'
|
||||
UNSUBSCRIBE = 'unsubscribe'
|
||||
|
||||
|
||||
def subscribe_user_to_calendar(user, course_key):
|
||||
"""
|
||||
|
||||
76
openedx/features/calendar_sync/plugins.py
Normal file
76
openedx/features/calendar_sync/plugins.py
Normal file
@@ -0,0 +1,76 @@
|
||||
"""
|
||||
Platform plugins to support Calendar Sync toggle.
|
||||
"""
|
||||
|
||||
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from openedx.features.calendar_sync.api import SUBSCRIBE, UNSUBSCRIBE
|
||||
from openedx.features.calendar_sync.models import UserCalendarSyncConfig
|
||||
from openedx.features.course_experience import CALENDAR_SYNC_FLAG, RELATIVE_DATES_FLAG
|
||||
from openedx.features.course_experience.course_tools import CourseTool, HttpMethod
|
||||
from student.models import CourseEnrollment
|
||||
|
||||
|
||||
class CalendarSyncToggleTool(CourseTool):
|
||||
"""
|
||||
The Calendar Sync toggle tool.
|
||||
"""
|
||||
http_method = HttpMethod.POST
|
||||
link_title = _('Calendar Sync')
|
||||
toggle_data = {'toggle_data': ''}
|
||||
|
||||
@classmethod
|
||||
def analytics_id(cls):
|
||||
"""
|
||||
Returns an id to uniquely identify this tool in analytics events.
|
||||
"""
|
||||
return 'edx.calendar-sync'
|
||||
|
||||
@classmethod
|
||||
def is_enabled(cls, request, course_key):
|
||||
"""
|
||||
The Calendar Sync toggle tool is limited to user enabled through a waffle flag.
|
||||
Staff always has access.
|
||||
"""
|
||||
if not (CALENDAR_SYNC_FLAG.is_enabled(course_key) and RELATIVE_DATES_FLAG.is_enabled(course_key)):
|
||||
return False
|
||||
|
||||
if CourseEnrollment.is_enrolled(request.user, course_key):
|
||||
if UserCalendarSyncConfig.is_enabled_for_course(request.user, course_key):
|
||||
cls.link_title = _('Unsubscribe from calendar updates')
|
||||
cls.toggle_data['toggle_data'] = UNSUBSCRIBE
|
||||
else:
|
||||
cls.link_title = _('Subscribe to calendar updates')
|
||||
cls.toggle_data['toggle_data'] = SUBSCRIBE
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def title(cls): # pylint: disable=arguments-differ
|
||||
"""
|
||||
Returns the title of this tool.
|
||||
"""
|
||||
return cls.link_title
|
||||
|
||||
@classmethod
|
||||
def icon_classes(cls): # pylint: disable=arguments-differ
|
||||
"""
|
||||
Returns the icon classes needed to represent this tool.
|
||||
"""
|
||||
return 'fa fa-calendar'
|
||||
|
||||
@classmethod
|
||||
def url(cls, course_key):
|
||||
"""
|
||||
Returns the URL for this tool for the specified course key.
|
||||
"""
|
||||
return reverse('openedx.calendar_sync', args=[course_key])
|
||||
|
||||
@classmethod
|
||||
def data(cls):
|
||||
"""
|
||||
Additional data to send with a form submission
|
||||
"""
|
||||
return cls.toggle_data
|
||||
44
openedx/features/calendar_sync/tests/test_plugins.py
Normal file
44
openedx/features/calendar_sync/tests/test_plugins.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""
|
||||
Unit tests for the calendar sync plugins.
|
||||
"""
|
||||
|
||||
|
||||
import crum
|
||||
import ddt
|
||||
from django.test import RequestFactory
|
||||
|
||||
from xmodule.modulestore.tests.django_utils import CourseUserType, SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag
|
||||
from openedx.features.calendar_sync.plugins import CalendarSyncToggleTool
|
||||
from openedx.features.course_experience import CALENDAR_SYNC_FLAG, RELATIVE_DATES_FLAG
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class TestCalendarSyncToggleTool(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Test the calendar sync toggle tool.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
""" Set up any course data """
|
||||
super(TestCalendarSyncToggleTool, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
cls.course_key = cls.course.id
|
||||
|
||||
@ddt.data(
|
||||
[CourseUserType.ANONYMOUS, False],
|
||||
[CourseUserType.ENROLLED, True],
|
||||
[CourseUserType.UNENROLLED, False],
|
||||
[CourseUserType.UNENROLLED_STAFF, False],
|
||||
)
|
||||
@ddt.unpack
|
||||
@override_waffle_flag(CALENDAR_SYNC_FLAG, active=True)
|
||||
@RELATIVE_DATES_FLAG.override(active=True)
|
||||
def test_calendar_sync_toggle_tool_is_enabled(self, user_type, should_be_enabled):
|
||||
request = RequestFactory().request()
|
||||
request.user = self.create_user_for_course(self.course, user_type)
|
||||
self.addCleanup(crum.set_current_request, None)
|
||||
crum.set_current_request(request)
|
||||
self.assertEqual(CalendarSyncToggleTool.is_enabled(request, self.course.id), should_be_enabled)
|
||||
51
openedx/features/calendar_sync/tests/test_views.py
Normal file
51
openedx/features/calendar_sync/tests/test_views.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""
|
||||
Tests for Calendar Sync views.
|
||||
"""
|
||||
|
||||
|
||||
import ddt
|
||||
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from openedx.features.calendar_sync.api import SUBSCRIBE, UNSUBSCRIBE
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
TEST_PASSWORD = 'test'
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class TestCalendarSyncView(SharedModuleStoreTestCase, TestCase):
|
||||
"""Tests for the calendar sync view."""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
""" Set up any course data """
|
||||
super(TestCalendarSyncView, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
|
||||
def setUp(self):
|
||||
super(TestCalendarSyncView, self).setUp()
|
||||
self.user = self.create_user_for_course(self.course)
|
||||
self.client.login(username=self.user.username, password=TEST_PASSWORD)
|
||||
self.calendar_sync_url = reverse('openedx.calendar_sync', args=[self.course.id])
|
||||
|
||||
@ddt.data(
|
||||
# Redirect on successful subscribe
|
||||
[{'tool_data': "{{'toggle_data': '{}'}}".format(SUBSCRIBE)}, 302, ''],
|
||||
# Redirect on successful unsubscribe
|
||||
[{'tool_data': "{{'toggle_data': '{}'}}".format(UNSUBSCRIBE)}, 302, ''],
|
||||
# 422 on unknown toggle_data
|
||||
[{'tool_data': "{{'toggle_data': '{}'}}".format('gibberish')}, 422,
|
||||
'Toggle data was not provided or had unknown value.'],
|
||||
# 422 on no toggle_data
|
||||
[{'tool_data': "{{'random_data': '{}'}}".format('gibberish')}, 422,
|
||||
'Toggle data was not provided or had unknown value.'],
|
||||
# 422 on no tool_data
|
||||
[{'nonsense': "{{'random_data': '{}'}}".format('gibberish')}, 422, 'Tool data was not provided.'],
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_course_dates_fragment(self, data, expected_status_code, contained_text):
|
||||
response = self.client.post(self.calendar_sync_url, data)
|
||||
self.assertEqual(response.status_code, expected_status_code)
|
||||
self.assertIn(contained_text, str(response.content))
|
||||
16
openedx/features/calendar_sync/urls.py
Normal file
16
openedx/features/calendar_sync/urls.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
Defines URLs for Calendar Sync.
|
||||
"""
|
||||
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from .views.calendar_sync import CalendarSyncView
|
||||
|
||||
urlpatterns = [
|
||||
url(
|
||||
r'^calendar_sync$',
|
||||
CalendarSyncView.as_view(),
|
||||
name='openedx.calendar_sync',
|
||||
),
|
||||
]
|
||||
0
openedx/features/calendar_sync/views/__init__.py
Normal file
0
openedx/features/calendar_sync/views/__init__.py
Normal file
54
openedx/features/calendar_sync/views/calendar_sync.py
Normal file
54
openedx/features/calendar_sync/views/calendar_sync.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""
|
||||
Views to toggle Calendar Sync settings for a user on a course
|
||||
"""
|
||||
|
||||
|
||||
import json
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
from django.views.generic import View
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from rest_framework import status
|
||||
|
||||
from openedx.features.calendar_sync.api import (
|
||||
SUBSCRIBE, UNSUBSCRIBE, subscribe_user_to_calendar, unsubscribe_user_to_calendar
|
||||
)
|
||||
from util.views import ensure_valid_course_key
|
||||
|
||||
|
||||
class CalendarSyncView(View):
|
||||
"""
|
||||
View for Calendar Sync
|
||||
"""
|
||||
@method_decorator(login_required)
|
||||
@method_decorator(ensure_csrf_cookie)
|
||||
@method_decorator(ensure_valid_course_key)
|
||||
def post(self, request, course_id):
|
||||
"""
|
||||
Updates the request user's calendar sync subscription status
|
||||
|
||||
Arguments:
|
||||
request: HTTP request
|
||||
course_id (str): string of a course key
|
||||
"""
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
tool_data = request.POST.get('tool_data')
|
||||
if not tool_data:
|
||||
return HttpResponse('Tool data was not provided.', status=status.HTTP_422_UNPROCESSABLE_ENTITY)
|
||||
|
||||
json_acceptable_string = tool_data.replace("'", "\"")
|
||||
data = json.loads(json_acceptable_string)
|
||||
toggle_data = data.get('toggle_data')
|
||||
if toggle_data == SUBSCRIBE:
|
||||
subscribe_user_to_calendar(request.user, course_key)
|
||||
elif toggle_data == UNSUBSCRIBE:
|
||||
unsubscribe_user_to_calendar(request.user, course_key)
|
||||
else:
|
||||
return HttpResponse('Toggle data was not provided or had unknown value.',
|
||||
status=status.HTTP_422_UNPROCESSABLE_ENTITY)
|
||||
return redirect(reverse('openedx.course_experience.course_home', args=[course_id]))
|
||||
Reference in New Issue
Block a user