Commit e6c94400 by Harry Rein Committed by GitHub

Merge pull request #15406 from edx/HarryRein/LEARNER-1534-no-updates-bug

Only display updates course tool when updates exist.
parents 330c4daa a34081c2
...@@ -12,7 +12,7 @@ class CourseBookmarksTool(CourseTool): ...@@ -12,7 +12,7 @@ class CourseBookmarksTool(CourseTool):
The course bookmarks tool. The course bookmarks tool.
""" """
@classmethod @classmethod
def is_enabled(cls, course_key): def is_enabled(cls, request, course_key):
""" """
Always show the bookmarks tool. Always show the bookmarks tool.
""" """
......
...@@ -18,7 +18,7 @@ class CourseTool(object): ...@@ -18,7 +18,7 @@ class CourseTool(object):
""" """
@classmethod @classmethod
def is_enabled(cls, course_key): def is_enabled(cls, request, course_key):
""" """
Returns true if this tool is enabled for the specified course key. Returns true if this tool is enabled for the specified course key.
""" """
......
...@@ -3,13 +3,15 @@ Platform plugins to support the course experience. ...@@ -3,13 +3,15 @@ Platform plugins to support the course experience.
This includes any locally defined CourseTools. This includes any locally defined CourseTools.
""" """
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from . import UNIFIED_COURSE_TAB_FLAG, SHOW_REVIEWS_TOOL_FLAG
from views.course_reviews import CourseReviewsModuleFragmentView
from course_tools import CourseTool from course_tools import CourseTool
from courseware.courses import get_course_by_id
from views.course_reviews import CourseReviewsModuleFragmentView
from views.course_updates import CourseUpdatesFragmentView
from . import SHOW_REVIEWS_TOOL_FLAG, UNIFIED_COURSE_TAB_FLAG
class CourseUpdatesTool(CourseTool): class CourseUpdatesTool(CourseTool):
...@@ -31,11 +33,13 @@ class CourseUpdatesTool(CourseTool): ...@@ -31,11 +33,13 @@ class CourseUpdatesTool(CourseTool):
return 'fa fa-newspaper-o' return 'fa fa-newspaper-o'
@classmethod @classmethod
def is_enabled(cls, course_key): def is_enabled(cls, request, course_key):
""" """
Returns True if this tool is enabled for the specified course key. Returns True if this tool is enabled for the specified course key.
""" """
return UNIFIED_COURSE_TAB_FLAG.is_enabled(course_key) course = get_course_by_id(course_key)
has_updates = CourseUpdatesFragmentView.has_updates(request, course)
return UNIFIED_COURSE_TAB_FLAG.is_enabled(course_key) and has_updates
@classmethod @classmethod
def url(cls, course_key): def url(cls, course_key):
...@@ -64,7 +68,7 @@ class CourseReviewsTool(CourseTool): ...@@ -64,7 +68,7 @@ class CourseReviewsTool(CourseTool):
return 'fa fa-star' return 'fa fa-star'
@classmethod @classmethod
def is_enabled(cls, course_key): def is_enabled(cls, request, course_key):
""" """
Returns True if this tool is enabled for the specified course key. Returns True if this tool is enabled for the specified course key.
""" """
......
...@@ -75,7 +75,7 @@ from openedx.features.course_experience.course_tools import CourseToolsPluginMan ...@@ -75,7 +75,7 @@ from openedx.features.course_experience.course_tools import CourseToolsPluginMan
<h3 class="hd-6">${_("Course Tools")}</h3> <h3 class="hd-6">${_("Course Tools")}</h3>
<ul class="list-unstyled"> <ul class="list-unstyled">
% for course_tool in course_tools: % for course_tool in course_tools:
% if course_tool.is_enabled(course_key): % if course_tool.is_enabled(request, course_key):
<li> <li>
<a href="${course_tool.url(course_key)}"> <a href="${course_tool.url(course_key)}">
<span class="icon ${course_tool.icon_classes()}" aria-hidden="true"></span> <span class="icon ${course_tool.icon_classes()}" aria-hidden="true"></span>
......
...@@ -29,20 +29,26 @@ from openedx.features.course_experience import course_home_page_title ...@@ -29,20 +29,26 @@ from openedx.features.course_experience import course_home_page_title
</div> </div>
</header> </header>
<div class="page-content"> <div class="page-content">
% if len(plain_html_updates) > 0: % if plain_html_updates:
${HTML(plain_html_updates)} ${HTML(plain_html_updates)}
% else: % else:
<div class="all-updates"> <div class="all-updates">
% for index, update in enumerate(updates): % if updates:
<article class="updates-article"> % for index, update in enumerate(updates):
% if not update.get("is_error"): <article class="updates-article">
<div class="date">${update.get("date")}</div> % if not update.get("is_error"):
% endif <div class="date">${update.get("date")}</div>
<div class="article-content"> % endif
${HTML(update.get("content"))} <div class="article-content">
${HTML(update.get("content"))}
</div>
</article>
% endfor
% else:
<div class="well depth-0 message-area">
${_("This course does not have any updates.")}
</div> </div>
</article> % endif
% endfor
</div> </div>
% endif % endif
</div> </div>
......
...@@ -16,6 +16,8 @@ from .test_course_updates import create_course_update, remove_course_updates ...@@ -16,6 +16,8 @@ from .test_course_updates import create_course_update, remove_course_updates
TEST_PASSWORD = 'test' TEST_PASSWORD = 'test'
TEST_WELCOME_MESSAGE = '<h2>Welcome!</h2>' TEST_WELCOME_MESSAGE = '<h2>Welcome!</h2>'
TEST_UPDATE_MESSAGE = '<h2>Test Update!</h2>'
TEST_COURSE_UPDATES_TOOL = '/course/updates">'
def course_home_url(course): def course_home_url(course):
...@@ -55,9 +57,6 @@ class TestCourseHomePage(SharedModuleStoreTestCase): ...@@ -55,9 +57,6 @@ class TestCourseHomePage(SharedModuleStoreTestCase):
cls.user = UserFactory(password=TEST_PASSWORD) cls.user = UserFactory(password=TEST_PASSWORD)
CourseEnrollment.enroll(cls.user, cls.course.id) CourseEnrollment.enroll(cls.user, cls.course.id)
# Create a welcome message
create_course_update(cls.course, cls.user, TEST_WELCOME_MESSAGE)
def setUp(self): def setUp(self):
""" """
Set up for the tests. Set up for the tests.
...@@ -65,22 +64,39 @@ class TestCourseHomePage(SharedModuleStoreTestCase): ...@@ -65,22 +64,39 @@ class TestCourseHomePage(SharedModuleStoreTestCase):
super(TestCourseHomePage, self).setUp() super(TestCourseHomePage, self).setUp()
self.client.login(username=self.user.username, password=TEST_PASSWORD) self.client.login(username=self.user.username, password=TEST_PASSWORD)
def tearDown(self):
remove_course_updates(self.course)
super(TestCourseHomePage, self).tearDown()
@override_waffle_flag(UNIFIED_COURSE_TAB_FLAG, active=True) @override_waffle_flag(UNIFIED_COURSE_TAB_FLAG, active=True)
def test_welcome_message_when_unified(self): def test_welcome_message_when_unified(self):
# Create a welcome message
create_course_update(self.course, self.user, TEST_WELCOME_MESSAGE)
url = course_home_url(self.course) url = course_home_url(self.course)
response = self.client.get(url) response = self.client.get(url)
self.assertContains(response, TEST_WELCOME_MESSAGE, status_code=200) self.assertContains(response, TEST_WELCOME_MESSAGE, status_code=200)
@override_waffle_flag(UNIFIED_COURSE_TAB_FLAG, active=False) @override_waffle_flag(UNIFIED_COURSE_TAB_FLAG, active=False)
def test_welcome_message_when_not_unified(self): def test_welcome_message_when_not_unified(self):
# Create a welcome message
create_course_update(self.course, self.user, TEST_WELCOME_MESSAGE)
url = course_home_url(self.course) url = course_home_url(self.course)
response = self.client.get(url) response = self.client.get(url)
self.assertNotContains(response, TEST_WELCOME_MESSAGE, status_code=200) self.assertNotContains(response, TEST_WELCOME_MESSAGE, status_code=200)
@override_waffle_flag(UNIFIED_COURSE_TAB_FLAG, active=True)
def test_updates_tool_visibility(self):
"""
Verify that the updates course tool is visible only when the course
has one or more updates.
"""
url = course_home_url(self.course)
response = self.client.get(url)
self.assertNotContains(response, TEST_COURSE_UPDATES_TOOL, status_code=200)
create_course_update(self.course, self.user, TEST_UPDATE_MESSAGE)
url = course_home_url(self.course)
response = self.client.get(url)
self.assertContains(response, TEST_COURSE_UPDATES_TOOL, status_code=200)
def test_queries(self): def test_queries(self):
""" """
Verify that the view's query count doesn't regress. Verify that the view's query count doesn't regress.
...@@ -89,7 +105,7 @@ class TestCourseHomePage(SharedModuleStoreTestCase): ...@@ -89,7 +105,7 @@ class TestCourseHomePage(SharedModuleStoreTestCase):
course_home_url(self.course) course_home_url(self.course)
# Fetch the view and verify the query counts # Fetch the view and verify the query counts
with self.assertNumQueries(51): with self.assertNumQueries(46):
with check_mongo_calls(5): with check_mongo_calls(4):
url = course_home_url(self.course) url = course_home_url(self.course)
self.client.get(url) self.client.get(url)
...@@ -61,7 +61,7 @@ def create_course_updates_block(course, user): ...@@ -61,7 +61,7 @@ def create_course_updates_block(course, user):
return course_updates return course_updates
def remove_course_updates(course): def remove_course_updates(user, course):
""" """
Remove any course updates in the specified course. Remove any course updates in the specified course.
""" """
...@@ -69,6 +69,7 @@ def remove_course_updates(course): ...@@ -69,6 +69,7 @@ def remove_course_updates(course):
try: try:
course_updates = modulestore().get_item(updates_usage_key) course_updates = modulestore().get_item(updates_usage_key)
course_updates.items = [] course_updates.items = []
modulestore().update_item(course_updates, user.id)
except ItemNotFoundError: except ItemNotFoundError:
pass pass
...@@ -105,7 +106,7 @@ class TestCourseUpdatesPage(SharedModuleStoreTestCase): ...@@ -105,7 +106,7 @@ class TestCourseUpdatesPage(SharedModuleStoreTestCase):
self.client.login(username=self.user.username, password=TEST_PASSWORD) self.client.login(username=self.user.username, password=TEST_PASSWORD)
def tearDown(self): def tearDown(self):
remove_course_updates(self.course) remove_course_updates(self.user, self.course)
super(TestCourseUpdatesPage, self).tearDown() super(TestCourseUpdatesPage, self).tearDown()
def test_view(self): def test_view(self):
......
...@@ -58,7 +58,7 @@ class TestWelcomeMessageView(ModuleStoreTestCase): ...@@ -58,7 +58,7 @@ class TestWelcomeMessageView(ModuleStoreTestCase):
self.client.login(username=self.user.username, password=TEST_PASSWORD) self.client.login(username=self.user.username, password=TEST_PASSWORD)
def tearDown(self): def tearDown(self):
remove_course_updates(self.course) remove_course_updates(self.user, self.course)
super(TestWelcomeMessageView, self).tearDown() super(TestWelcomeMessageView, self).tearDown()
def test_welcome_message(self): def test_welcome_message(self):
......
...@@ -19,9 +19,8 @@ from util.views import ensure_valid_course_key ...@@ -19,9 +19,8 @@ from util.views import ensure_valid_course_key
from ..utils import get_course_outline_block_tree from ..utils import get_course_outline_block_tree
from .course_dates import CourseDatesFragmentView from .course_dates import CourseDatesFragmentView
from .course_outline import CourseOutlineFragmentView from .course_outline import CourseOutlineFragmentView
from .course_reviews import CourseReviewsModuleFragmentView
from .welcome_message import WelcomeMessageFragmentView
from .course_sock import CourseSockFragmentView from .course_sock import CourseSockFragmentView
from .welcome_message import WelcomeMessageFragmentView
class CourseHomeView(CourseTabView): class CourseHomeView(CourseTabView):
...@@ -113,11 +112,9 @@ class CourseHomeFragmentView(EdxFragmentView): ...@@ -113,11 +112,9 @@ class CourseHomeFragmentView(EdxFragmentView):
# Get the handouts # Get the handouts
handouts_html = get_course_info_section(request, request.user, course, 'handouts') handouts_html = get_course_info_section(request, request.user, course, 'handouts')
# Only show the reviews button if configured
show_reviews_link = CourseReviewsModuleFragmentView.is_configured()
# Render the course home fragment # Render the course home fragment
context = { context = {
'request': request,
'csrf': csrf(request)['csrf_token'], 'csrf': csrf(request)['csrf_token'],
'course': course, 'course': course,
'course_key': course_key, 'course_key': course_key,
...@@ -130,7 +127,6 @@ class CourseHomeFragmentView(EdxFragmentView): ...@@ -130,7 +127,6 @@ class CourseHomeFragmentView(EdxFragmentView):
'course_sock_fragment': course_sock_fragment, 'course_sock_fragment': course_sock_fragment,
'disable_courseware_js': True, 'disable_courseware_js': True,
'uses_pattern_library': True, 'uses_pattern_library': True,
'show_reviews_link': show_reviews_link,
} }
html = render_to_string('course_experience/course-home-fragment.html', context) html = render_to_string('course_experience/course-home-fragment.html', context)
return Fragment(html) return Fragment(html)
...@@ -53,12 +53,10 @@ class CourseUpdatesFragmentView(EdxFragmentView): ...@@ -53,12 +53,10 @@ class CourseUpdatesFragmentView(EdxFragmentView):
course_url_name = default_course_url_name(request) course_url_name = default_course_url_name(request)
course_url = reverse(course_url_name, kwargs={'course_id': unicode(course.id)}) course_url = reverse(course_url_name, kwargs={'course_id': unicode(course.id)})
# Fetch all of the updates individually ordered_updates = self.get_ordered_updates(request, course)
info_module = get_course_info_section_module(request, request.user, course, 'updates') plain_html_updates = ''
ordered_updates = self.order_updates(info_module.items) if ordered_updates:
plain_html_updates = self.get_plain_html_updates(request, course)
# Older implementations and a few tests store a single html object representing all the updates
plain_html_updates = info_module.data
# Render the course home fragment # Render the course home fragment
context = { context = {
...@@ -74,16 +72,33 @@ class CourseUpdatesFragmentView(EdxFragmentView): ...@@ -74,16 +72,33 @@ class CourseUpdatesFragmentView(EdxFragmentView):
return Fragment(html) return Fragment(html)
@classmethod @classmethod
def order_updates(self, updates): def get_ordered_updates(self, request, course):
""" """
Returns any course updates in reverse chronological order. Returns any course updates in reverse chronological order.
""" """
sorted_updates = [update for update in updates if update.get('status') == self.STATUS_VISIBLE] info_module = get_course_info_section_module(request, request.user, course, 'updates')
sorted_updates.sort(
updates = info_module.items if info_module else []
ordered_updates = [update for update in updates if update.get('status') == self.STATUS_VISIBLE]
ordered_updates.sort(
key=lambda item: (self.safe_parse_date(item['date']), item['id']), key=lambda item: (self.safe_parse_date(item['date']), item['id']),
reverse=True reverse=True
) )
return sorted_updates return ordered_updates
@classmethod
def has_updates(self, request, course):
return len(self.get_ordered_updates(request, course)) > 0
@classmethod
def get_plain_html_updates(self, request, course):
"""
Returns any course updates in an html chunk. Used
for older implementations and a few tests that store
a single html object representing all the updates.
"""
info_module = get_course_info_section_module(request, request.user, course, 'updates')
return info_module.data if info_module else ''
@staticmethod @staticmethod
def safe_parse_date(date): def safe_parse_date(date):
......
...@@ -53,14 +53,11 @@ class WelcomeMessageFragmentView(EdxFragmentView): ...@@ -53,14 +53,11 @@ class WelcomeMessageFragmentView(EdxFragmentView):
""" """
Returns the course's welcome message or None if it doesn't have one. 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 # Return the course update with the most recent publish date
ordered_updates = CourseUpdatesFragmentView.order_updates(info_module.items) ordered_updates = CourseUpdatesFragmentView.get_ordered_updates(request, course)
content = None content = None
if ordered_updates: if ordered_updates:
info_module = get_course_info_section_module(request, request.user, course, 'updates')
info_block = getattr(info_module, '_xmodule', info_module) info_block = getattr(info_module, '_xmodule', info_module)
content = info_block.system.replace_urls(ordered_updates[0]['content']) content = info_block.system.replace_urls(ordered_updates[0]['content'])
......
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