Show a welcome message on the course home page
LEARNER-879
This commit is contained in:
@@ -446,18 +446,24 @@ class CourseInfoModule(CourseInfoFields, HtmlModuleMixin):
|
||||
return self.data.replace("%%USER_ID%%", self.system.anonymous_student_id)
|
||||
return self.data
|
||||
else:
|
||||
course_updates = [item for item in self.items if item.get('status') == self.STATUS_VISIBLE]
|
||||
course_updates.sort(
|
||||
key=lambda item: (CourseInfoModule.safe_parse_date(item['date']), item['id']),
|
||||
reverse=True
|
||||
)
|
||||
course_updates = self.ordered_updates()
|
||||
context = {
|
||||
'visible_updates': course_updates[:3],
|
||||
'hidden_updates': course_updates[3:],
|
||||
}
|
||||
|
||||
return self.system.render_template("{0}/course_updates.html".format(self.TEMPLATE_DIR), context)
|
||||
|
||||
def ordered_updates(self):
|
||||
"""
|
||||
Returns any course updates in reverse chronological order.
|
||||
"""
|
||||
course_updates = [item for item in self.items if item.get('status') == self.STATUS_VISIBLE]
|
||||
course_updates.sort(
|
||||
key=lambda item: (CourseInfoModule.safe_parse_date(item['date']), item['id']),
|
||||
reverse=True
|
||||
)
|
||||
return course_updates
|
||||
|
||||
@staticmethod
|
||||
def safe_parse_date(date):
|
||||
"""
|
||||
|
||||
@@ -235,6 +235,13 @@ def get_course_about_section(request, course, section_key):
|
||||
raise KeyError("Invalid about key " + str(section_key))
|
||||
|
||||
|
||||
def get_course_info_usage_key(course, section_key):
|
||||
"""
|
||||
Returns the usage key for the specified section's course info module.
|
||||
"""
|
||||
return course.id.make_usage_key('course_info', section_key)
|
||||
|
||||
|
||||
def get_course_info_section_module(request, user, course, section_key):
|
||||
"""
|
||||
This returns the course info module for a given section_key.
|
||||
@@ -245,7 +252,7 @@ def get_course_info_section_module(request, user, course, section_key):
|
||||
- updates
|
||||
- guest_updates
|
||||
"""
|
||||
usage_key = course.id.make_usage_key('course_info', section_key)
|
||||
usage_key = get_course_info_usage_key(course, section_key)
|
||||
|
||||
# Use an empty cache
|
||||
field_data_cache = FieldDataCache([], course.id, user)
|
||||
|
||||
@@ -10,7 +10,7 @@ from django.utils.translation import ugettext as _, ugettext_noop
|
||||
from courseware.access import has_access
|
||||
from courseware.entrance_exams import user_can_skip_entrance_exam
|
||||
from openedx.core.lib.course_tabs import CourseTabPluginManager
|
||||
from openedx.features.course_experience import defaut_course_url_name, UNIFIED_COURSE_EXPERIENCE_FLAG
|
||||
from openedx.features.course_experience import default_course_url_name, UNIFIED_COURSE_EXPERIENCE_FLAG
|
||||
from request_cache.middleware import RequestCache
|
||||
from student.models import CourseEnrollment
|
||||
from xmodule.tabs import CourseTab, CourseTabList, key_checker, link_reverse_func
|
||||
@@ -45,7 +45,7 @@ class CoursewareTab(EnrolledTab):
|
||||
Returns a function that computes the URL for this tab.
|
||||
"""
|
||||
request = RequestCache.get_current_request()
|
||||
url_name = defaut_course_url_name(request)
|
||||
url_name = default_course_url_name(request)
|
||||
return link_reverse_func(url_name)
|
||||
|
||||
|
||||
|
||||
@@ -428,7 +428,7 @@ class StaticCourseTabView(EdxFragmentView):
|
||||
"""
|
||||
return get_static_tab_fragment(request, course, tab)
|
||||
|
||||
def render_to_standalone_html(self, request, fragment, course=None, tab=None, **kwargs):
|
||||
def render_standalone_response(self, request, fragment, course=None, tab=None, **kwargs):
|
||||
"""
|
||||
Renders this static tab's fragment to HTML for a standalone page.
|
||||
"""
|
||||
@@ -531,14 +531,14 @@ class CourseTabView(EdxFragmentView):
|
||||
tab = page_context['tab']
|
||||
return tab.render_to_fragment(request, course, **kwargs)
|
||||
|
||||
def render_to_standalone_html(self, request, fragment, course=None, tab=None, page_context=None, **kwargs):
|
||||
def render_standalone_response(self, request, fragment, course=None, tab=None, page_context=None, **kwargs):
|
||||
"""
|
||||
Renders this course tab's fragment to HTML for a standalone page.
|
||||
"""
|
||||
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)
|
||||
return render_to_response('courseware/tab-view.html', page_context)
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Welcome message
|
||||
.welcome-message {
|
||||
border: solid 1px $lms-border-color;
|
||||
@include border-left(solid 4px $black);
|
||||
margin-bottom: $baseline;
|
||||
padding: $baseline;
|
||||
|
||||
h1, h2, h3 {
|
||||
font-size: font-size(large);
|
||||
font-weight: bold;
|
||||
color: $black;
|
||||
}
|
||||
}
|
||||
|
||||
// Course sidebar
|
||||
.course-sidebar {
|
||||
@include margin-left(0);
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
Views for building plugins.
|
||||
"""
|
||||
|
||||
from abc import abstractmethod
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.staticfiles.storage import staticfiles_storage
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import render_to_response
|
||||
from web_fragments.views import FragmentView
|
||||
|
||||
@@ -78,10 +78,14 @@ class EdxFragmentView(FragmentView):
|
||||
for js_file in self.js_dependencies():
|
||||
fragment.add_javascript_url(staticfiles_storage.url(js_file))
|
||||
|
||||
def render_to_standalone_html(self, request, fragment, **kwargs):
|
||||
def render_standalone_response(self, request, fragment, **kwargs):
|
||||
"""
|
||||
Renders this fragment to HTML for a standalone page.
|
||||
Renders a standalone page for the specified fragment.
|
||||
|
||||
Note: if fragment is None, a 204 response will be returned (no content).
|
||||
"""
|
||||
if fragment is None:
|
||||
return HttpResponse(status=204)
|
||||
context = {
|
||||
'uses-pattern-library': self.USES_PATTERN_LIBRARY,
|
||||
'settings': settings,
|
||||
|
||||
@@ -15,7 +15,7 @@ from django.views.generic import View
|
||||
from courseware.courses import get_course_with_access
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
|
||||
from openedx.features.course_experience import defaut_course_url_name
|
||||
from openedx.features.course_experience import default_course_url_name
|
||||
from util.views import ensure_valid_course_key
|
||||
from web_fragments.fragment import Fragment
|
||||
|
||||
@@ -38,7 +38,7 @@ class CourseBookmarksView(View):
|
||||
"""
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
|
||||
course_url_name = defaut_course_url_name(request)
|
||||
course_url_name = default_course_url_name(request)
|
||||
course_url = reverse(course_url_name, kwargs={'course_id': unicode(course.id)})
|
||||
|
||||
# Render the bookmarks list as a fragment
|
||||
|
||||
@@ -14,7 +14,7 @@ UNIFIED_COURSE_EXPERIENCE_FLAG = 'unified_course_experience'
|
||||
UNIFIED_COURSE_VIEW_FLAG = 'unified_course_view'
|
||||
|
||||
|
||||
def defaut_course_url_name(request=None):
|
||||
def default_course_url_name(request=None):
|
||||
"""
|
||||
Returns the default course URL name for the current user.
|
||||
"""
|
||||
|
||||
@@ -58,6 +58,12 @@ from openedx.features.course_experience import UNIFIED_COURSE_EXPERIENCE_FLAG
|
||||
<div class="page-content">
|
||||
<div class="layout layout-1q3q">
|
||||
<main class="layout-col layout-col-b">
|
||||
% if welcome_message_fragment and waffle.flag_is_active(request, UNIFIED_COURSE_EXPERIENCE_FLAG):
|
||||
<div class="section section-dates">
|
||||
${HTML(welcome_message_fragment.body_html())}
|
||||
</div>
|
||||
% endif
|
||||
|
||||
${HTML(outline_fragment.body_html())}
|
||||
</main>
|
||||
<aside class="course-sidebar layout-col layout-col-a">
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
## mako
|
||||
|
||||
<%page expression_filter="h"/>
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
|
||||
<%!
|
||||
from openedx.core.djangolib.markup import HTML
|
||||
%>
|
||||
|
||||
<%block name="content">
|
||||
<div class="welcome-message">
|
||||
${HTML(welcome_message_html)}
|
||||
</div>
|
||||
</%block>
|
||||
@@ -6,7 +6,6 @@ from waffle.testutils import override_flag
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from openedx.core.djangoapps.content.block_structure.api import get_course_in_cache
|
||||
from student.models import CourseEnrollment
|
||||
from student.tests.factories import UserFactory
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
@@ -15,7 +14,10 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, chec
|
||||
|
||||
from openedx.features.course_experience import UNIFIED_COURSE_EXPERIENCE_FLAG
|
||||
|
||||
from .test_course_updates import create_course_update, remove_course_updates
|
||||
|
||||
TEST_PASSWORD = 'test'
|
||||
TEST_WELCOME_MESSAGE = '<h2>Welcome!</h2>'
|
||||
|
||||
|
||||
def course_home_url(course):
|
||||
@@ -55,6 +57,9 @@ class TestCourseHomePage(SharedModuleStoreTestCase):
|
||||
cls.user = UserFactory(password=TEST_PASSWORD)
|
||||
CourseEnrollment.enroll(cls.user, cls.course.id)
|
||||
|
||||
# Create a welcome message
|
||||
create_course_update(cls.course, cls.user, TEST_WELCOME_MESSAGE)
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up for the tests.
|
||||
@@ -62,6 +67,10 @@ class TestCourseHomePage(SharedModuleStoreTestCase):
|
||||
super(TestCourseHomePage, self).setUp()
|
||||
self.client.login(username=self.user.username, password=TEST_PASSWORD)
|
||||
|
||||
def tearDown(self):
|
||||
remove_course_updates(self.course)
|
||||
super(TestCourseHomePage, self).tearDown()
|
||||
|
||||
@override_flag(UNIFIED_COURSE_EXPERIENCE_FLAG, active=True)
|
||||
def test_unified_page(self):
|
||||
"""
|
||||
@@ -71,15 +80,27 @@ class TestCourseHomePage(SharedModuleStoreTestCase):
|
||||
response = self.client.get(url)
|
||||
self.assertContains(response, '<h2 class="hd hd-3 page-title">Test Course</h2>')
|
||||
|
||||
@override_flag(UNIFIED_COURSE_EXPERIENCE_FLAG, active=True)
|
||||
def test_welcome_message_when_unified(self):
|
||||
url = course_home_url(self.course)
|
||||
response = self.client.get(url)
|
||||
self.assertContains(response, TEST_WELCOME_MESSAGE, status_code=200)
|
||||
|
||||
@override_flag(UNIFIED_COURSE_EXPERIENCE_FLAG, active=False)
|
||||
def test_welcome_message_when_not_unified(self):
|
||||
url = course_home_url(self.course)
|
||||
response = self.client.get(url)
|
||||
self.assertNotContains(response, TEST_WELCOME_MESSAGE, status_code=200)
|
||||
|
||||
def test_queries(self):
|
||||
"""
|
||||
Verify that the view's query count doesn't regress.
|
||||
"""
|
||||
# Pre-fill the course blocks cache
|
||||
get_course_in_cache(self.course.id)
|
||||
# Pre-fetch the view to populate any caches
|
||||
course_home_url(self.course)
|
||||
|
||||
# Fetch the view and verify the query counts
|
||||
with self.assertNumQueries(35):
|
||||
with check_mongo_calls(3):
|
||||
with self.assertNumQueries(43):
|
||||
with check_mongo_calls(5):
|
||||
url = course_home_url(self.course)
|
||||
self.client.get(url)
|
||||
|
||||
@@ -3,11 +3,13 @@ Tests for the course updates page.
|
||||
"""
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from courseware.courses import get_course_info_section_module, get_course_info_usage_key
|
||||
from student.models import CourseEnrollment
|
||||
from student.tests.factories import UserFactory
|
||||
from xmodule.html_module import CourseInfoModule
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.exceptions import ItemNotFoundError
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, check_mongo_calls
|
||||
|
||||
@@ -26,6 +28,51 @@ def course_updates_url(course):
|
||||
)
|
||||
|
||||
|
||||
def create_course_update(course, user, content, date='December 31, 1999'):
|
||||
"""
|
||||
Creates a test welcome message for the specified course.
|
||||
"""
|
||||
updates_usage_key = get_course_info_usage_key(course, 'updates')
|
||||
try:
|
||||
course_updates = modulestore().get_item(updates_usage_key)
|
||||
except ItemNotFoundError:
|
||||
course_updates = create_course_updates_block(course, user)
|
||||
course_updates.items.append({
|
||||
"id": len(course_updates.items) + 1,
|
||||
"date": date,
|
||||
"content": content,
|
||||
"status": CourseInfoModule.STATUS_VISIBLE
|
||||
})
|
||||
modulestore().update_item(course_updates, user.id)
|
||||
|
||||
|
||||
def create_course_updates_block(course, user):
|
||||
"""
|
||||
Create a course updates block.
|
||||
"""
|
||||
updates_usage_key = get_course_info_usage_key(course, 'updates')
|
||||
course_updates = modulestore().create_item(
|
||||
user.id,
|
||||
updates_usage_key.course_key,
|
||||
updates_usage_key.block_type,
|
||||
block_id=updates_usage_key.block_id
|
||||
)
|
||||
course_updates.data = ''
|
||||
return course_updates
|
||||
|
||||
|
||||
def remove_course_updates(course):
|
||||
"""
|
||||
Remove any course updates in the specified course.
|
||||
"""
|
||||
updates_usage_key = get_course_info_usage_key(course, 'updates')
|
||||
try:
|
||||
course_updates = modulestore().get_item(updates_usage_key)
|
||||
course_updates.items = []
|
||||
except ItemNotFoundError:
|
||||
pass
|
||||
|
||||
|
||||
class TestCourseUpdatesPage(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Test the course updates page.
|
||||
@@ -50,24 +97,6 @@ class TestCourseUpdatesPage(SharedModuleStoreTestCase):
|
||||
cls.user = UserFactory(password=TEST_PASSWORD)
|
||||
CourseEnrollment.enroll(cls.user, cls.course.id)
|
||||
|
||||
# Create course updates
|
||||
cls.create_course_updates(cls.course, cls.user)
|
||||
|
||||
@classmethod
|
||||
def create_course_updates(cls, course, user, count=5):
|
||||
"""
|
||||
Create some test course updates.
|
||||
"""
|
||||
updates_usage_key = course.id.make_usage_key('course_info', 'updates')
|
||||
course_updates = modulestore().create_item(
|
||||
user.id,
|
||||
updates_usage_key.course_key,
|
||||
updates_usage_key.block_type,
|
||||
block_id=updates_usage_key.block_id
|
||||
)
|
||||
course_updates.data = u'<ol><li><a href="test">Test Update</a></li></ol>'
|
||||
modulestore().update_item(course_updates, user.id)
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up for the tests.
|
||||
@@ -75,14 +104,25 @@ class TestCourseUpdatesPage(SharedModuleStoreTestCase):
|
||||
super(TestCourseUpdatesPage, self).setUp()
|
||||
self.client.login(username=self.user.username, password=TEST_PASSWORD)
|
||||
|
||||
def tearDown(self):
|
||||
remove_course_updates(self.course)
|
||||
super(TestCourseUpdatesPage, self).tearDown()
|
||||
|
||||
def test_view(self):
|
||||
create_course_update(self.course, self.user, 'First Message')
|
||||
create_course_update(self.course, self.user, 'Second Message')
|
||||
url = course_updates_url(self.course)
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response_content = response.content.decode("utf-8")
|
||||
self.assertIn('<a href="test">Test Update</a>', response_content)
|
||||
self.assertContains(response, 'First Message')
|
||||
self.assertContains(response, 'Second Message')
|
||||
|
||||
def test_queries(self):
|
||||
create_course_update(self.course, self.user, 'First Message')
|
||||
|
||||
# Pre-fetch the view to populate any caches
|
||||
course_updates_url(self.course)
|
||||
|
||||
# Fetch the view and verify that the query counts haven't changed
|
||||
with self.assertNumQueries(32):
|
||||
with check_mongo_calls(4):
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
"""
|
||||
Tests for course welcome messages.
|
||||
"""
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from student.models import CourseEnrollment
|
||||
from student.tests.factories import UserFactory
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
|
||||
from .test_course_updates import create_course_update, remove_course_updates
|
||||
|
||||
TEST_PASSWORD = 'test'
|
||||
TEST_WELCOME_MESSAGE = '<h2>Welcome!</h2>'
|
||||
|
||||
|
||||
def welcome_message_url(course):
|
||||
"""
|
||||
Returns the URL for the welcome message view.
|
||||
"""
|
||||
return reverse(
|
||||
'openedx.course_experience.welcome_message_fragment_view',
|
||||
kwargs={
|
||||
'course_id': unicode(course.id),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class TestWelcomeMessageView(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Tests for the course welcome message fragment view.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Set up the simplest course possible."""
|
||||
# setUpClassAndTestData() already calls setUpClass on SharedModuleStoreTestCase
|
||||
# pylint: disable=super-method-not-called
|
||||
with super(TestWelcomeMessageView, cls).setUpClassAndTestData():
|
||||
with cls.store.default_store(ModuleStoreEnum.Type.split):
|
||||
cls.course = CourseFactory.create()
|
||||
with cls.store.bulk_operations(cls.course.id):
|
||||
# Create a basic course structure
|
||||
chapter = ItemFactory.create(category='chapter', parent_location=cls.course.location)
|
||||
section = ItemFactory.create(category='sequential', parent_location=chapter.location)
|
||||
ItemFactory.create(category='vertical', parent_location=section.location)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
"""Set up and enroll our fake user in the course."""
|
||||
cls.user = UserFactory(password=TEST_PASSWORD)
|
||||
CourseEnrollment.enroll(cls.user, cls.course.id)
|
||||
|
||||
def setUp(self):
|
||||
super(TestWelcomeMessageView, self).setUp()
|
||||
self.client.login(username=self.user.username, password=TEST_PASSWORD)
|
||||
|
||||
def tearDown(self):
|
||||
remove_course_updates(self.course)
|
||||
super(TestWelcomeMessageView, self).tearDown()
|
||||
|
||||
def test_welcome_message(self):
|
||||
create_course_update(self.course, self.user, 'First Update', date='January 1, 2000')
|
||||
create_course_update(self.course, self.user, 'Second Update', date='January 1, 2017')
|
||||
create_course_update(self.course, self.user, 'Retroactive Update', date='January 1, 2010')
|
||||
response = self.client.get(welcome_message_url(self.course))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, 'Second Update')
|
||||
|
||||
def test_empty_welcome_message(self):
|
||||
response = self.client.get(welcome_message_url(self.course))
|
||||
self.assertEqual(response.status_code, 204)
|
||||
@@ -7,6 +7,7 @@ from django.conf.urls import url
|
||||
from views.course_home import CourseHomeView, CourseHomeFragmentView
|
||||
from views.course_outline import CourseOutlineFragmentView
|
||||
from views.course_updates import CourseUpdatesFragmentView, CourseUpdatesView
|
||||
from views.welcome_message import WelcomeMessageFragmentView
|
||||
|
||||
urlpatterns = [
|
||||
url(
|
||||
@@ -34,4 +35,9 @@ urlpatterns = [
|
||||
CourseUpdatesFragmentView.as_view(),
|
||||
name='openedx.course_experience.course_updates_fragment_view',
|
||||
),
|
||||
url(
|
||||
r'^welcome_message_fragment$',
|
||||
WelcomeMessageFragmentView.as_view(),
|
||||
name='openedx.course_experience.welcome_message_fragment_view',
|
||||
),
|
||||
]
|
||||
|
||||
@@ -18,6 +18,7 @@ from web_fragments.fragment import Fragment
|
||||
|
||||
from .course_outline import CourseOutlineFragmentView
|
||||
from .course_dates import CourseDatesFragmentView
|
||||
from .welcome_message import WelcomeMessageFragmentView
|
||||
from ..utils import get_course_outline_block_tree
|
||||
|
||||
|
||||
@@ -93,6 +94,11 @@ class CourseHomeFragmentView(EdxFragmentView):
|
||||
# Get resume course information
|
||||
has_visited_course, resume_course_url = self._get_resume_course_info(request, course_id)
|
||||
|
||||
# Render the welcome message as a fragment
|
||||
welcome_message_fragment = WelcomeMessageFragmentView().render_to_fragment(
|
||||
request, course_id=course_id, **kwargs
|
||||
)
|
||||
|
||||
# Render the course dates as a fragment
|
||||
dates_fragment = CourseDatesFragmentView().render_to_fragment(request, course_id=course_id, **kwargs)
|
||||
|
||||
@@ -113,6 +119,7 @@ class CourseHomeFragmentView(EdxFragmentView):
|
||||
'has_visited_course': has_visited_course,
|
||||
'resume_course_url': resume_course_url,
|
||||
'dates_fragment': dates_fragment,
|
||||
'welcome_message_fragment': welcome_message_fragment,
|
||||
'disable_courseware_js': True,
|
||||
'uses_pattern_library': True,
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ from courseware.courses import get_course_info_section, 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 openedx.features.course_experience import defaut_course_url_name
|
||||
from openedx.features.course_experience import default_course_url_name
|
||||
from web_fragments.fragment import Fragment
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class CourseUpdatesFragmentView(EdxFragmentView):
|
||||
"""
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
|
||||
course_url_name = defaut_course_url_name(request)
|
||||
course_url_name = default_course_url_name(request)
|
||||
course_url = reverse(course_url_name, kwargs={'course_id': unicode(course.id)})
|
||||
|
||||
# Fetch the updates as HTML
|
||||
|
||||
48
openedx/features/course_experience/views/welcome_message.py
Normal file
48
openedx/features/course_experience/views/welcome_message.py
Normal file
@@ -0,0 +1,48 @@
|
||||
"""
|
||||
View logic for handling course welcome messages.
|
||||
"""
|
||||
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
from courseware.courses import get_course_info_section_module, get_course_with_access
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
|
||||
from web_fragments.fragment import Fragment
|
||||
|
||||
|
||||
class WelcomeMessageFragmentView(EdxFragmentView):
|
||||
"""
|
||||
A fragment that displays a course's welcome message.
|
||||
"""
|
||||
def render_to_fragment(self, request, course_id=None, **kwargs):
|
||||
"""
|
||||
Renders the welcome message fragment for the specified course.
|
||||
|
||||
Returns: A fragment, or None if there is no welcome message.
|
||||
"""
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True)
|
||||
welcome_message_html = self.welcome_message_html(request, course)
|
||||
if not welcome_message_html:
|
||||
return None
|
||||
|
||||
context = {
|
||||
'welcome_message_html': welcome_message_html,
|
||||
}
|
||||
|
||||
html = render_to_string('course_experience/welcome-message-fragment.html', context)
|
||||
return Fragment(html)
|
||||
|
||||
@classmethod
|
||||
def welcome_message_html(cls, request, course):
|
||||
"""
|
||||
Returns the course's welcome message or None if it doesn't have one.
|
||||
"""
|
||||
info_module = get_course_info_section_module(request, request.user, course, 'updates')
|
||||
if not info_module:
|
||||
return None
|
||||
|
||||
# Return the course update with the most recent publish date
|
||||
info_block = getattr(info_module, '_xmodule', info_module)
|
||||
ordered_updates = info_block.ordered_updates()
|
||||
return ordered_updates[0]['content'] if ordered_updates else None
|
||||
@@ -210,7 +210,7 @@ py2neo==3.1.2
|
||||
-r coverage.txt
|
||||
|
||||
# Support for plugins
|
||||
web-fragments==0.2.1
|
||||
web-fragments==0.2.2
|
||||
xblock==0.5.0
|
||||
|
||||
# Third Party XBlocks
|
||||
|
||||
Reference in New Issue
Block a user