Commit ec241774 by Andy Armstrong

Add the staff preview bar to the new course home page

LEARNER-75
parent b1b950c6
......@@ -72,6 +72,9 @@ class CourseTab(object):
# True if this tab should be displayed only for instructors
course_staff_only = False
# True if this tab supports showing staff users a preview menu
supports_preview_menu = False
def __init__(self, tab_dict):
"""
Initializes class members with values passed in by subclasses.
......
......@@ -36,6 +36,7 @@ class CoursewareTab(EnrolledTab):
view_name = 'courseware'
is_movable = False
is_default = False
supports_preview_menu = True
@staticmethod
def main_course_url_name(request):
......
......@@ -395,7 +395,7 @@ class TestStaffMasqueradeAsSpecificStudent(StaffMasqueradeTestCase, ProblemSubmi
def test_masquerade_as_specific_student_progress(self):
"""
Test masquesrading as a specific user for progress page.
Test masquerading as a specific user for progress page.
"""
# Give the student some correct answers, check their progress page
self.login_student()
......
......@@ -412,9 +412,9 @@ class CoursewareIndex(View):
'init': '',
'fragment': Fragment(),
'staff_access': self.is_staff,
'studio_url': get_studio_url(self.course, 'course'),
'masquerade': self.masquerade,
'real_user': self.real_user,
'supports_preview_menu': True,
'studio_url': get_studio_url(self.course, 'course'),
'xqa_server': settings.FEATURES.get('XQA_SERVER', "http://your_xqa_server.com"),
'bookmarks_api_url': reverse('bookmarks'),
'language_preference': self._get_language_preference(),
......
......@@ -51,6 +51,7 @@ import survey.views
from certificates import api as certs_api
from certificates.models import CertificateStatuses
from openedx.core.djangoapps.models.course_details import CourseDetails
from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
from commerce.utils import EcommerceService
from enrollment.api import add_enrollment
from course_modes.models import CourseMode
......@@ -383,10 +384,11 @@ def course_info(request, course_id):
'course': course,
'staff_access': staff_access,
'masquerade': masquerade,
'supports_preview_menu': True,
'studio_url': studio_url,
'show_enroll_banner': show_enroll_banner,
'url_to_enroll': url_to_enroll,
'upgrade_link': upgrade_link
'upgrade_link': upgrade_link,
}
# Get the URL of the user's last position in order to display the 'where you were last' message
......@@ -449,7 +451,7 @@ def get_last_accessed_courseware(course, request, user):
return (None, None)
class StaticCourseTabView(FragmentView):
class StaticCourseTabView(EdxFragmentView):
"""
View that displays a static course tab with a given name.
"""
......@@ -486,7 +488,7 @@ class StaticCourseTabView(FragmentView):
})
class CourseTabView(FragmentView):
class CourseTabView(EdxFragmentView):
"""
View that displays a course tab page.
"""
......@@ -499,29 +501,46 @@ class CourseTabView(FragmentView):
course_key = CourseKey.from_string(course_id)
course = get_course_with_access(request.user, 'load', course_key)
tab = CourseTabList.get_tab_by_type(course.tabs, tab_type)
return super(CourseTabView, self).get(request, course=course, tab=tab, **kwargs)
page_context = self.create_page_context(request, course=course, tab=tab, **kwargs)
return super(CourseTabView, self).get(request, course=course, page_context=page_context, **kwargs)
def render_to_fragment(self, request, course=None, tab=None, **kwargs):
def create_page_context(self, request, course=None, tab=None, **kwargs):
"""
Creates the context for the fragment's template.
"""
staff_access = has_access(request.user, 'staff', course)
supports_preview_menu = tab.get('supports_preview_menu', False)
if supports_preview_menu:
masquerade, masquerade_user = setup_masquerade(request, course.id, staff_access, reset_masquerade_data=True)
request.user = masquerade_user
else:
masquerade = None
return {
'course': course,
'tab': tab,
'active_page': tab.get('type', None),
'staff_access': staff_access,
'masquerade': masquerade,
'supports_preview_menu': supports_preview_menu,
'uses_pattern_library': True,
'disable_courseware_js': True,
}
def render_to_fragment(self, request, course=None, page_context=None, **kwargs):
"""
Renders the course tab to a fragment.
"""
tab = page_context['tab']
return tab.render_to_fragment(request, course, **kwargs)
def render_to_standalone_html(self, request, fragment, course=None, tab=None, **kwargs):
def render_to_standalone_html(self, request, fragment, course=None, tab=None, page_context=None, **kwargs):
"""
Renders this course tab's fragment to HTML for a standalone page.
"""
return render_to_string(
'courseware/tab-view.html',
{
'course': course,
'active_page': tab['type'],
'tab': tab,
'fragment': fragment,
'uses_pattern_library': True,
'disable_courseware_js': True,
},
)
if not page_context:
page_context = self.create_page_context(request, course=course, tab=tab, **kwargs)
page_context['fragment'] = fragment
return render_to_string('courseware/tab-view.html', page_context)
@ensure_csrf_cookie
......@@ -871,11 +890,12 @@ def _progress(request, course_key, student_id):
'studio_url': studio_url,
'grade_summary': grade_summary,
'staff_access': staff_access,
'masquerade': masquerade,
'supports_preview_menu': True,
'student': student,
'passed': is_course_passed(course, grade_summary),
'credit_course_requirements': _credit_course_requirements(course_key, student),
'certificate_data': _get_cert_data(student, course, course_key, is_active, enrollment_mode),
'masquerade': masquerade
}
with outer_atomic():
......@@ -1397,7 +1417,6 @@ def render_xblock(request, usage_key_string, check_if_enrolled=True):
'disable_header': True,
'disable_footer': True,
'disable_window_wrap': True,
'disable_preview_menu': True,
'staff_access': bool(has_access(request.user, 'staff', course)),
'xqa_server': settings.FEATURES.get('XQA_SERVER', 'http://your_xqa_server.com'),
}
......
......@@ -21,14 +21,14 @@
display: inline-block;
.action-preview-label {
@include margin-right($baseline/2);
display: inline-block;
margin-right: ($baseline/2);
margin-bottom: 0;
vertical-align: middle;
}
.action-preview-select {
margin-right: $baseline;
@include margin-right($baseline);
}
.action-preview-username-container {
......
......@@ -92,3 +92,60 @@
}
}
}
.wrapper-preview-menu {
@include clearfix();
@include box-sizing(border-box);
margin: 0 auto 0;
padding: ($baseline*0.75);
background-color: $lms-preview-menu-color;
@media print {
display: none;
}
.preview-menu {
max-width: $lms-max-width;
width: auto;
margin: 0 auto;
}
.preview-actions {
@include margin-left(0);
display: inline-block;
margin-bottom: 0;
.action-preview {
display: inline-block;
.action-preview-label {
@include margin-right($baseline/2);
display: inline-block;
margin-bottom: 0;
vertical-align: middle;
}
.action-preview-select {
@include margin-right($baseline);
}
.action-preview-username-container {
display: none;
.action-preview-username {
vertical-align: middle;
height: 25px;
}
}
}
}
.preview-specific-student-notice {
margin-top: ($baseline/2);
font-size: 90%;
> p {
margin-bottom: 0;
}
}
}
......@@ -19,7 +19,7 @@
margin: 0 auto;
padding: 10px 10px 0;
width: 100%;
max-width: 1180px;
max-width: $lms-max-width;
.left {
@include float(left);
......
// LMS layouts
.content-wrapper {
max-width: 1180px;
padding: {
top: $baseline/2;
bottom: $baseline*2;
}
max-width: $lms-max-width;
padding-bottom: $baseline*2;
.container {
@include clearfix();
......
// LMS variables
$lms-max-width: 1180px;
$lms-gray: palette(grayscale, base);
$lms-background-color: palette(grayscale, x-back);
$lms-container-background-color: $white;
$lms-border-color: palette(grayscale, back);
$lms-label-color: palette(grayscale, black);
$lms-active-color: palette(primary, base);
$lms-preview-menu-color: #c8c8c8;
$white-transparent: rgba(255, 255, 255, 0);
$white-opacity-40: rgba(255, 255, 255, 0.4);
......
## mako
<%page args="active_page=None" expression_filter="h" />
<%namespace name='static' file='/static_content.html'/>
<%!
from django.utils.translation import ugettext as _
from courseware.tabs import get_course_tab_list
from django.core.urlresolvers import reverse
from django.conf import settings
from openedx.core.djangoapps.course_groups.partition_scheme import get_cohorted_user_partition
from openedx.core.djangolib.js_utils import dump_js_escaped_json
from openedx.core.djangolib.markup import HTML, Text
from student.models import CourseEnrollment
%>
<%
if active_page is None and active_page_context is not UNDEFINED:
# If active_page is not passed in as an argument, it may be in the context as active_page_context
active_page = active_page_context
def selected(is_selected):
return "selected" if is_selected else ""
# If active_page is not passed in as an argument, it may be in the context as active_page_context
active_page = active_page_context
show_preview_menu = not disable_preview_menu and staff_access and active_page in ["courseware", "info", "progress"]
cohorted_user_partition = get_cohorted_user_partition(course)
masquerade_user_name = masquerade.user_name if masquerade else None
masquerade_group_id = masquerade.group_id if masquerade else None
staff_selected = selected(not masquerade or masquerade.role != "student")
specific_student_selected = selected(not staff_selected and masquerade.user_name)
student_selected = selected(not staff_selected and not specific_student_selected and not masquerade_group_id)
include_special_exams = settings.FEATURES.get('ENABLE_SPECIAL_EXAMS', False) and (course.enable_proctored_exams or course.enable_timed_exams)
%>
......@@ -39,47 +26,6 @@ include_special_exams = settings.FEATURES.get('ENABLE_SPECIAL_EXAMS', False) and
% endfor
<div class="proctored_exam_status"></div>
% endif
% if show_preview_menu:
<nav class="wrapper-preview-menu" aria-label="${_('Course View')}">
<div class="preview-menu">
<ol class="preview-actions">
<li class="action-preview">
<form action="#" class="action-preview-form" method="post">
<label for="action-preview-select" class="action-preview-label">${_("View this course as:")}</label>
<select class="action-preview-select" id="action-preview-select" name="select">
<option value="staff" ${staff_selected}>${_("Staff")}</option>
<option value="student" ${student_selected}>${_("Student")}</option>
<option value="specific student" ${specific_student_selected}>${_("Specific student")}</option>
% if cohorted_user_partition:
% for group in sorted(cohorted_user_partition.groups, key=lambda group: group.name):
<option value="group.id" data-group-id="${group.id}" ${selected(masquerade_group_id == group.id)}>
${_("Student in {content_group}").format(content_group=group.name)}
</option>
% endfor
% endif
</select>
<div class="action-preview-username-container">
<label for="action-preview-username" class="action-preview-label">${_("Username or email:")}</label>
<input type="text" class="action-preview-username" id="action-preview-username">
</div>
<button type="submit" class="sr" name="submit" value="submit">${_("Set preview mode")}</button>
</form>
</li>
</ol>
% if specific_student_selected:
<div class="preview-specific-student-notice">
<p>
${Text(_("You are now viewing the course as {i_start}{user_name}{i_end}.")).format(
user_name=masquerade_user_name,
i_start=HTML(u'<i>'),
i_end=HTML(u'</i>'),
)}
</p>
</div>
% endif
</div>
</nav>
% endif
% if disable_tabs is UNDEFINED or not disable_tabs:
<nav class="${active_page} wrapper-course-material" aria-label="${_('Course Material')}">
......@@ -95,18 +41,3 @@ include_special_exams = settings.FEATURES.get('ENABLE_SPECIAL_EXAMS', False) and
</div>
</nav>
%endif
% if show_preview_menu:
<%
preview_options = {
"courseId": course.id,
"disableStudentAccess": disable_student_access if disable_student_access is not UNDEFINED else False,
"specificStudentSelected": specific_student_selected,
"cohortedUserPartitionId": cohorted_user_partition.id if cohorted_user_partition else None,
"masqueradeUsername" : masquerade_user_name if masquerade_user_name is not UNDEFINED else None,
}
%>
<%static:require_module_async module_name="lms/js/preview/preview_factory" class_name="PreviewFactory">
PreviewFactory(${preview_options | n, dump_js_escaped_json});
</%static:require_module_async>
% endif
......@@ -134,6 +134,7 @@ from pipeline_mako import render_require_js_path_overrides
% if not disable_header:
<%include file="${static.get_template_path('header.html')}" args="online_help_token=online_help_token" />
<%include file="/preview_menu.html" />
% endif
<div class="content-wrapper" id="content">
......
## mako
<%page args="active_page=None" expression_filter="h" />
<%namespace name='static' file='/static_content.html'/>
<%!
from django.utils.translation import ugettext as _
from django.conf import settings
from openedx.core.djangoapps.course_groups.partition_scheme import get_cohorted_user_partition
from openedx.core.djangolib.js_utils import dump_js_escaped_json
from openedx.core.djangolib.markup import HTML, Text
%>
<%
show_preview_menu = course and staff_access and supports_preview_menu
%>
% if show_preview_menu:
<%
def selected(is_selected):
return "selected" if is_selected else ""
cohorted_user_partition = get_cohorted_user_partition(course)
masquerade_user_name = masquerade.user_name if masquerade else None
masquerade_group_id = masquerade.group_id if masquerade else None
staff_selected = selected(not masquerade or masquerade.role != "student")
specific_student_selected = selected(not staff_selected and masquerade.user_name)
student_selected = selected(not staff_selected and not specific_student_selected and not masquerade_group_id)
%>
<nav class="wrapper-preview-menu" aria-label="${_('Course View')}">
<div class="preview-menu">
<ol class="preview-actions">
<li class="action-preview">
<form action="#" class="action-preview-form" method="post">
<label for="action-preview-select" class="action-preview-label">${_("View this course as:")}</label>
<select class="action-preview-select" id="action-preview-select" name="select">
<option value="staff" ${staff_selected}>${_("Staff")}</option>
<option value="student" ${student_selected}>${_("Student")}</option>
<option value="specific student" ${specific_student_selected}>${_("Specific student")}</option>
% if cohorted_user_partition:
% for group in sorted(cohorted_user_partition.groups, key=lambda group: group.name):
<option value="group.id" data-group-id="${group.id}" ${selected(masquerade_group_id == group.id)}>
${_("Student in {content_group}").format(content_group=group.name)}
</option>
% endfor
% endif
</select>
<div class="action-preview-username-container">
<label for="action-preview-username" class="action-preview-label">${_("Username or email:")}</label>
<input type="text" class="action-preview-username" id="action-preview-username">
</div>
<button type="submit" class="sr" name="submit" value="submit">${_("Set preview mode")}</button>
</form>
</li>
</ol>
% if specific_student_selected:
<div class="preview-specific-student-notice">
<p>
${Text(_("You are now viewing the course as {i_start}{user_name}{i_end}.")).format(
user_name=masquerade_user_name,
i_start=HTML(u'<i>'),
i_end=HTML(u'</i>'),
)}
</p>
</div>
% endif
</div>
</nav>
<%
preview_options = {
"courseId": course.id,
"disableStudentAccess": disable_student_access if disable_student_access is not UNDEFINED else False,
"specificStudentSelected": specific_student_selected,
"cohortedUserPartitionId": cohorted_user_partition.id if cohorted_user_partition else None,
"masqueradeUsername" : masquerade_user_name if masquerade_user_name is not UNDEFINED else None,
}
%>
<%static:require_module_async module_name="lms/js/preview/preview_factory" class_name="PreviewFactory">
PreviewFactory(${preview_options | n, dump_js_escaped_json});
</%static:require_module_async>
% endif
......@@ -4,7 +4,7 @@
## This template should not use the target student's details when masquerading, see TNL-4895
<%
self.real_user = real_user if real_user != UNDEFINED else user
self.real_user = getattr(user, 'real_user', user)
%>
<%!
......
......@@ -91,6 +91,5 @@ class EdxFragmentView(FragmentView):
'disable_header': True,
'disable_footer': True,
'disable_window_wrap': True,
'disable_preview_menu': True,
}
return render_to_response(settings.STANDALONE_FRAGMENT_VIEW_TEMPLATE, context)
......@@ -49,6 +49,7 @@ class CourseBookmarksView(View):
context = {
'csrf': csrf(request)['csrf_token'],
'course': course,
'supports_preview_menu': True,
'course_url': course_url,
'bookmarks_fragment': bookmarks_fragment,
'disable_courseware_js': True,
......
## mako
<%! main_css = "style-main-v2" %>
<%page expression_filter="h"/>
<%inherit file="../main.html" />
<%namespace name='static' file='../static_content.html'/>
<%def name="online_help_token()"><% return "courseware" %></%def>
<%def name="course_name()">
<% return _("{course_number} Courseware").format(course_number=course.display_number_with_default) %>
</%def>
<%!
import json
......@@ -21,20 +14,6 @@ from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_str
from openedx.core.djangolib.markup import HTML
%>
<%block name="bodyclass">course</%block>
<%block name="pagetitle">${course_name()}</%block>
<%include file="../courseware/course_navigation.html" args="active_page='courseware'" />
<%block name="headextra">
${HTML(outline_fragment.head_html())}
</%block>
<%block name="js_extra">
${HTML(outline_fragment.foot_html())}
</%block>
<%block name="content">
<div class="course-view container" id="course-container">
<header class="page-header has-secondary">
......
"""
Tests for the Course Outline view and supporting views.
"""
import datetime
from mock import patch
import json
from django.core.urlresolvers import reverse
from courseware.tests.factories import StaffFactory
from student.models import CourseEnrollment
from student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
TEST_PASSWORD = 'test'
def course_home_url(course):
"""
Returns the URL for the course's home page
"""
return reverse(
'edx.course_experience.course_home',
kwargs={
'course_id': unicode(course.id),
}
)
class TestCourseOutlinePage(SharedModuleStoreTestCase):
"""
......@@ -43,8 +60,7 @@ class TestCourseOutlinePage(SharedModuleStoreTestCase):
@classmethod
def setUpTestData(cls):
"""Set up and enroll our fake user in the course."""
cls.password = 'test'
cls.user = UserFactory(password=cls.password)
cls.user = UserFactory(password=TEST_PASSWORD)
for course in cls.courses:
CourseEnrollment.enroll(cls.user, course.id)
......@@ -53,18 +69,13 @@ class TestCourseOutlinePage(SharedModuleStoreTestCase):
Set up for the tests.
"""
super(TestCourseOutlinePage, self).setUp()
self.client.login(username=self.user.username, password=self.password)
self.client.login(username=self.user.username, password=TEST_PASSWORD)
@patch('openedx.features.course_experience.views.course_outline.get_last_accessed_courseware')
def test_render(self, patched_get_last_accessed):
for course in self.courses:
patched_get_last_accessed.return_value = (None, course.last_accessed)
url = reverse(
'edx.course_experience.course_home',
kwargs={
'course_id': unicode(course.id),
}
)
url = course_home_url(course)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
response_content = response.content.decode("utf-8")
......@@ -79,3 +90,67 @@ class TestCourseOutlinePage(SharedModuleStoreTestCase):
self.assertIn(section.display_name, response_content)
for vertical in section.children:
self.assertNotIn(vertical.display_name, response_content)
class TestCourseOutlinePreview(SharedModuleStoreTestCase):
"""
Unit tests for staff preview of the course outline.
"""
def update_masquerade(self, course, role, group_id=None, user_name=None):
"""
Toggle masquerade state.
"""
masquerade_url = reverse(
'masquerade_update',
kwargs={
'course_key_string': unicode(course.id),
}
)
response = self.client.post(
masquerade_url,
json.dumps({'role': role, 'group_id': group_id, 'user_name': user_name}),
'application/json'
)
self.assertEqual(response.status_code, 200)
return response
def test_preview(self):
"""
Verify the behavior of preview for the course outline.
"""
course = CourseFactory.create(
start=datetime.datetime.now() - datetime.timedelta(days=30)
)
staff_user = StaffFactory(course_key=course.id, password=TEST_PASSWORD)
CourseEnrollment.enroll(staff_user, course.id)
future_date = datetime.datetime.now() + datetime.timedelta(days=30)
with self.store.bulk_operations(course.id):
chapter = ItemFactory.create(
category='chapter',
parent_location=course.location,
display_name='First Chapter',
)
section = ItemFactory.create(category='sequential', parent_location=chapter.location)
ItemFactory.create(category='vertical', parent_location=section.location)
chapter = ItemFactory.create(
category='chapter',
parent_location=course.location,
display_name='Future Chapter',
due=future_date,
)
section = ItemFactory.create(category='sequential', parent_location=chapter.location)
ItemFactory.create(category='vertical', parent_location=section.location)
# Verify that a staff user sees a chapter with a due date in the future
self.client.login(username=staff_user.username, password='test')
url = course_home_url(course)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'Future Chapter')
# Verify that staff masquerading as a learner does not see the future chapter.
self.update_masquerade(course, role='student')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, 'Future Chapter')
......@@ -4,7 +4,7 @@ Defines URLs for the course experience.
from django.conf.urls import url
from views.course_home import CourseHomeView
from views.course_home import CourseHomeView, CourseHomeFragmentView
from views.course_outline import CourseOutlineFragmentView
urlpatterns = [
......@@ -14,6 +14,11 @@ urlpatterns = [
name='edx.course_experience.course_home',
),
url(
r'^home_fragment$',
CourseHomeFragmentView.as_view(),
name='edx.course_experience.course_home_fragment_view',
),
url(
r'^outline_fragment$',
CourseOutlineFragmentView.as_view(),
name='edx.course_experience.course_outline_fragment_view',
......
......@@ -4,20 +4,22 @@ Views for the course home page.
from django.contrib.auth.decorators import login_required
from django.core.context_processors import csrf
from django.shortcuts import render_to_response
from django.template.loader import render_to_string
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_control
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.generic import View
from courseware.courses import get_course_with_access
from lms.djangoapps.courseware.views.views import CourseTabView
from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
from util.views import ensure_valid_course_key
from web_fragments.fragment import Fragment
from course_outline import CourseOutlineFragmentView
class CourseHomeView(View):
class CourseHomeView(CourseTabView):
"""
The home page for a course.
"""
......@@ -25,21 +27,33 @@ class CourseHomeView(View):
@method_decorator(ensure_csrf_cookie)
@method_decorator(cache_control(no_cache=True, no_store=True, must_revalidate=True))
@method_decorator(ensure_valid_course_key)
def get(self, request, course_id):
def get(self, request, course_id, **kwargs):
"""
Displays the home page for the specified course.
"""
return super(CourseHomeView, self).get(request, course_id, 'courseware', **kwargs)
def render_to_fragment(self, request, course=None, tab=None, **kwargs):
course_id = unicode(course.id)
home_fragment_view = CourseHomeFragmentView()
return home_fragment_view.render_to_fragment(request, course_id=course_id, **kwargs)
Arguments:
request: HTTP request
course_id (unicode): course id
class CourseHomeFragmentView(EdxFragmentView):
"""
A fragment to render the home page for a course.
"""
def render_to_fragment(self, request, course_id=None, **kwargs):
"""
Renders the course's home page as a fragment.
"""
course_key = CourseKey.from_string(course_id)
course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
# Render the outline as a fragment
outline_fragment = CourseOutlineFragmentView().render_to_fragment(request, course_id=course_id)
outline_fragment = CourseOutlineFragmentView().render_to_fragment(request, course_id=course_id, **kwargs)
# Render the entire unified course view
# Render the course home fragment
context = {
'csrf': csrf(request)['csrf_token'],
'course': course,
......@@ -47,4 +61,5 @@ class CourseHomeView(View):
'disable_courseware_js': True,
'uses_pattern_library': True,
}
return render_to_response('course_experience/course-home.html', context)
html = render_to_string('course_experience/course-home-fragment.html', context)
return Fragment(html)
......@@ -9,12 +9,12 @@ from courseware.courses import get_course_with_access
from lms.djangoapps.courseware.views.views import get_last_accessed_courseware
from lms.djangoapps.course_api.blocks.api import get_blocks
from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
from web_fragments.fragment import Fragment
from web_fragments.views import FragmentView
from xmodule.modulestore.django import modulestore
class CourseOutlineFragmentView(FragmentView):
class CourseOutlineFragmentView(EdxFragmentView):
"""
Course outline fragment to be shown in the unified course view.
"""
......@@ -35,7 +35,7 @@ class CourseOutlineFragmentView(FragmentView):
return block
def render_to_fragment(self, request, course_id=None, **kwargs):
def render_to_fragment(self, request, course_id=None, page_context=None, **kwargs):
"""
Renders the course outline as a fragment.
"""
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment