Commit 1bd6792a by Nimisha Asthagiri

Support for "View Live" on Pages page.

Settings for ENABLE_STUDENT_NOTES and ENABLE_TEXTBOOK in CMS.
Do not display empty collection tabs.
Updated Changelog.
parent 3b0f7148
...@@ -5,6 +5,8 @@ These are notable changes in edx-platform. This is a rolling list of changes, ...@@ -5,6 +5,8 @@ These are notable changes in edx-platform. This is a rolling list of changes,
in roughly chronological order, most recent first. Add your entries at or near in roughly chronological order, most recent first. Add your entries at or near
the top. Include a label indicating the component affected. the top. Include a label indicating the component affected.
Studio: Add ability to reorder Pages and hide the Wiki page. STUD-1375
Blades: Added template for iFrames. BLD-611. Blades: Added template for iFrames. BLD-611.
Studio: Support for viewing built-in tabs on the Pages page. STUD-1193 Studio: Support for viewing built-in tabs on the Pages page. STUD-1193
......
...@@ -27,26 +27,26 @@ Feature: CMS.Pages ...@@ -27,26 +27,26 @@ Feature: CMS.Pages
@skip_safari @skip_safari
Scenario: Users can reorder static pages Scenario: Users can reorder static pages
Given I have created two different static pages Given I have created two different static pages
When I reorder the static pages When I drag the first static page to the last
Then the static pages are in the reverse order Then the static pages are switched
And I reload the page And I reload the page
Then the static pages are in the reverse order Then the static pages are switched
Scenario: Users can reorder built-in pages Scenario: Users can reorder built-in pages
Given I have opened the pages page in a new course Given I have opened the pages page in a new course
Then the built-in pages are in the default order Then the built-in pages are in the default order
When I reorder the pages When I drag the first page to the last
Then the built-in pages are in the reverse order Then the built-in pages are switched
And I reload the page And I reload the page
Then the built-in pages are in the reverse order Then the built-in pages are switched
Scenario: Users can reorder built-in pages amongst static pages Scenario: Users can reorder built-in pages amongst static pages
Given I have created two different static pages Given I have created two different static pages
Then the pages are in the default order Then the pages are in the default order
When I reorder the pages When I drag the first page to the last
Then the pages are in the reverse order Then the pages are switched
And I reload the page And I reload the page
Then the pages are in the reverse order Then the pages are switched
Scenario: Users can toggle visibility on hideable pages Scenario: Users can toggle visibility on hideable pages
Given I have opened the pages page in a new course Given I have opened the pages page in a new course
......
...@@ -6,6 +6,9 @@ from lettuce import world, step ...@@ -6,6 +6,9 @@ from lettuce import world, step
from nose.tools import assert_equal, assert_in # pylint: disable=E0611 from nose.tools import assert_equal, assert_in # pylint: disable=E0611
CSS_FOR_TAB_ELEMENT = "li[data-tab-id='{0}'] input.toggle-checkbox"
@step(u'I go to the pages page$') @step(u'I go to the pages page$')
def go_to_static(step): def go_to_static(step):
menu_css = 'li.nav-course-courseware' menu_css = 'li.nav-course-courseware'
...@@ -51,24 +54,24 @@ def change_name(step, new_name): ...@@ -51,24 +54,24 @@ def change_name(step, new_name):
world.css_click(save_button) world.css_click(save_button)
@step(u'I reorder the static pages') @step(u'I drag the first static page to the last$')
def reorder_static_pages(_step): def drag_first_static_page_to_last(step):
reorder_pages_with_css_class('.component') drag_first_to_last_with_css('.component')
@step(u'I have created a static page') @step(u'I have created a static page$')
def create_static_page(step): def create_static_page(step):
step.given('I have opened the pages page in a new course') step.given('I have opened the pages page in a new course')
step.given('I add a new static page') step.given('I add a new static page')
@step(u'I have opened the pages page in a new course') @step(u'I have opened the pages page in a new course$')
def open_pages_page_in_new_course(step): def open_pages_page_in_new_course(step):
step.given('I have opened a new course in Studio') step.given('I have opened a new course in Studio')
step.given('I go to the pages page') step.given('I go to the pages page')
@step(u'I have created two different static pages') @step(u'I have created two different static pages$')
def create_two_pages(step): def create_two_pages(step):
step.given('I have created a static page') step.given('I have created a static page')
step.given('I "edit" the static page') step.given('I "edit" the static page')
...@@ -78,8 +81,8 @@ def create_two_pages(step): ...@@ -78,8 +81,8 @@ def create_two_pages(step):
_verify_page_names('First', 'Empty') _verify_page_names('First', 'Empty')
@step(u'the static pages are in the reverse order') @step(u'the static pages are switched$')
def static_pages_in_reverse_order(step): def static_pages_are_switched(step):
_verify_page_names('Empty', 'First') _verify_page_names('Empty', 'First')
...@@ -90,51 +93,51 @@ def _verify_page_names(first, second): ...@@ -90,51 +93,51 @@ def _verify_page_names(first, second):
timeout_msg="Timed out waiting for two pages to be present" timeout_msg="Timed out waiting for two pages to be present"
) )
pages = world.css_find('.xmodule_StaticTabModule') pages = world.css_find('.xmodule_StaticTabModule')
assert pages[0].text == first assert_equal(pages[0].text, first)
assert pages[1].text == second assert_equal(pages[1].text, second)
@step(u'the built-in pages are in the default order') @step(u'the built-in pages are in the default order$')
def built_in_pages_in_default_order(step): def built_in_pages_in_default_order(step):
expected_pages = ['Courseware', 'Course Info', 'Discussion', 'Wiki', 'Progress'] expected_pages = ['Courseware', 'Course Info', 'Discussion', 'Wiki', 'Progress']
see_pages_in_expected_order(expected_pages) see_pages_in_expected_order(expected_pages)
@step(u'the built-in pages are in the reverse order') @step(u'the built-in pages are switched$')
def built_in_pages_in_reverse_order(step): def built_in_pages_switched(step):
expected_pages = ['Courseware', 'Course Info', 'Wiki', 'Progress', 'Discussion'] expected_pages = ['Courseware', 'Course Info', 'Wiki', 'Progress', 'Discussion']
see_pages_in_expected_order(expected_pages) see_pages_in_expected_order(expected_pages)
@step(u'the pages are in the default order') @step(u'the pages are in the default order$')
def pages_in_default_order(step): def pages_in_default_order(step):
expected_pages = ['Courseware', 'Course Info', 'Discussion', 'Wiki', 'Progress', 'First', 'Empty'] expected_pages = ['Courseware', 'Course Info', 'Discussion', 'Wiki', 'Progress', 'First', 'Empty']
see_pages_in_expected_order(expected_pages) see_pages_in_expected_order(expected_pages)
@step(u'the pages are in the reverse order') @step(u'the pages are switched$$')
def pages_in_reverse_order(step): def pages_are_switched(step):
expected_pages = ['Courseware', 'Course Info', 'Wiki', 'Progress', 'First', 'Empty', 'Discussion'] expected_pages = ['Courseware', 'Course Info', 'Wiki', 'Progress', 'First', 'Empty', 'Discussion']
see_pages_in_expected_order(expected_pages) see_pages_in_expected_order(expected_pages)
@step(u'I reorder the pages') @step(u'I drag the first page to the last$')
def reorder_pages(step): def drag_first_page_to_last(step):
reorder_pages_with_css_class('.sortable-tab') drag_first_to_last_with_css('.is-movable')
@step(u'I should see the "([^"]*)" page as "(visible|hidden)"$') @step(u'I should see the "([^"]*)" page as "(visible|hidden)"$')
def page_is_visible_or_hidden(step, page_id, visible_or_hidden): def page_is_visible_or_hidden(step, page_id, visible_or_hidden):
hidden = visible_or_hidden == "hidden" hidden = visible_or_hidden == "hidden"
assert world.css_find("li[data-tab-id='{0}'] input.toggle-checkbox".format(page_id)).checked == hidden assert_equal(world.css_find(CSS_FOR_TAB_ELEMENT.format(page_id)).checked, hidden)
@step(u'I toggle the visibility of the "([^"]*)" page') @step(u'I toggle the visibility of the "([^"]*)" page$')
def page_toggle_visibility(step, page_id): def page_toggle_visibility(step, page_id):
world.css_find("li[data-tab-id='{0}'] input.toggle-checkbox".format(page_id))[0].click() world.css_find(CSS_FOR_TAB_ELEMENT.format(page_id))[0].click()
def reorder_pages_with_css_class(css_class): def drag_first_to_last_with_css(css_class):
# For some reason, the drag_and_drop method did not work in this case. # For some reason, the drag_and_drop method did not work in this case.
draggables = world.css_find(css_class + ' .drag-handle') draggables = world.css_find(css_class + ' .drag-handle')
source = draggables.first source = draggables.first
...@@ -149,4 +152,3 @@ def see_pages_in_expected_order(page_names_in_expected_order): ...@@ -149,4 +152,3 @@ def see_pages_in_expected_order(page_names_in_expected_order):
assert_equal(len(page_names_in_expected_order), len(pages)) assert_equal(len(page_names_in_expected_order), len(pages))
for i, page_name in enumerate(page_names_in_expected_order): for i, page_name in enumerate(page_names_in_expected_order):
assert_in(page_name, pages[i].text) assert_in(page_name, pages[i].text)
...@@ -150,7 +150,7 @@ class TestGitExport(CourseTestCase): ...@@ -150,7 +150,7 @@ class TestGitExport(CourseTestCase):
'--format=%an|%ae'], cwd=cwd) '--format=%an|%ae'], cwd=cwd)
self.assertEqual(expect_string, git_log) self.assertEqual(expect_string, git_log)
# Make changes to course so there is something commit # Make changes to course so there is something to commit
self.populate_course() self.populate_course()
git_export_utils.export_to_git( git_export_utils.export_to_git(
self.course.id, self.course.id,
......
...@@ -409,9 +409,19 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase): ...@@ -409,9 +409,19 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
else: else:
built_in_tabs.append(tab) built_in_tabs.append(tab)
tab_ids = [{'tab_id': tab.tab_id} for tab in (built_in_tabs + reverse_static_tabs)] # create the requested tab_id_locators list
tab_id_locators = [
self.client.ajax_post(new_location.url_reverse('tabs'), {'tabs': tab_ids}) {
'tab_id': tab.tab_id
} for tab in built_in_tabs
]
tab_id_locators.extend([
{
'tab_locator': unicode(self._get_tab_locator(course, tab))
} for tab in reverse_static_tabs
])
self.client.ajax_post(new_location.url_reverse('tabs'), {'tabs': tab_id_locators})
course = module_store.get_item(course_location) course = module_store.get_item(course_location)
......
...@@ -918,9 +918,9 @@ def textbooks_detail_handler(request, tid, tag=None, package_id=None, branch=Non ...@@ -918,9 +918,9 @@ def textbooks_detail_handler(request, tid, tag=None, package_id=None, branch=Non
if not textbook: if not textbook:
return JsonResponse(status=404) return JsonResponse(status=404)
i = course.pdf_textbooks.index(textbook) i = course.pdf_textbooks.index(textbook)
new_textbooks = course.pdf_textbooks[0:i] remaining_textbooks = course.pdf_textbooks[0:i]
new_textbooks.extend(course.pdf_textbooks[i + 1:]) remaining_textbooks.extend(course.pdf_textbooks[i + 1:])
course.pdf_textbooks = new_textbooks course.pdf_textbooks = remaining_textbooks
store.update_item(course, request.user.id) store.update_item(course, request.user.id)
return JsonResponse() return JsonResponse()
......
...@@ -16,7 +16,7 @@ from xmodule.modulestore.django import loc_mapper ...@@ -16,7 +16,7 @@ from xmodule.modulestore.django import loc_mapper
from xmodule.modulestore.locator import BlockUsageLocator from xmodule.modulestore.locator import BlockUsageLocator
from xmodule.tabs import CourseTabList, StaticTab, CourseTab, InvalidTabsException from xmodule.tabs import CourseTabList, StaticTab, CourseTab, InvalidTabsException
from ..utils import get_modulestore from ..utils import get_modulestore, get_lms_link_for_item
__all__ = ['tabs_handler'] __all__ = ['tabs_handler']
...@@ -69,16 +69,16 @@ def tabs_handler(request, tag=None, package_id=None, branch=None, version_guid=N ...@@ -69,16 +69,16 @@ def tabs_handler(request, tag=None, package_id=None, branch=None, version_guid=N
if isinstance(tab, StaticTab): if isinstance(tab, StaticTab):
# static tab needs its locator information to render itself as an xmodule # static tab needs its locator information to render itself as an xmodule
static_tab_loc = old_location.replace(category='static_tab', name=tab.url_slug) static_tab_loc = old_location.replace(category='static_tab', name=tab.url_slug)
static_tab = modulestore('direct').get_item(static_tab_loc)
tab.locator = loc_mapper().translate_location( tab.locator = loc_mapper().translate_location(
course_item.location.course_id, static_tab.location, False, True course_item.location.course_id, static_tab_loc, False, True
) )
tabs_to_render.append(tab) tabs_to_render.append(tab)
return render_to_response('edit-tabs.html', { return render_to_response('edit-tabs.html', {
'context_course': course_item, 'context_course': course_item,
'tabs_to_render': tabs_to_render, 'tabs_to_render': tabs_to_render,
'course_locator': locator 'course_locator': locator,
'lms_link': get_lms_link_for_item(course_item.location),
}) })
else: else:
return HttpResponseNotFound() return HttpResponseNotFound()
...@@ -93,14 +93,14 @@ def reorder_tabs_handler(course_item, request): ...@@ -93,14 +93,14 @@ def reorder_tabs_handler(course_item, request):
# The locators are used to identify static tabs since they are xmodules. # The locators are used to identify static tabs since they are xmodules.
# Although all tabs have tab_ids, newly created static tabs do not know # Although all tabs have tab_ids, newly created static tabs do not know
# their tab_ids since the xmodule editor uses only locators to identify new objects. # their tab_ids since the xmodule editor uses only locators to identify new objects.
ids_locators_of_new_tab_order = request.json['tabs'] requested_tab_id_locators = request.json['tabs']
# original tab list in original order # original tab list in original order
old_tab_list = course_item.tabs old_tab_list = course_item.tabs
# create a new list in the new order # create a new list in the new order
new_tab_list = [] new_tab_list = []
for tab_id_locator in ids_locators_of_new_tab_order: for tab_id_locator in requested_tab_id_locators:
tab = get_tab_by_tab_id_locator(old_tab_list, tab_id_locator) tab = get_tab_by_tab_id_locator(old_tab_list, tab_id_locator)
if tab is None: if tab is None:
return JsonResponse( return JsonResponse(
......
...@@ -48,14 +48,17 @@ class TabsPageTests(CourseTestCase): ...@@ -48,14 +48,17 @@ class TabsPageTests(CourseTestCase):
with self.assertRaises(NotImplementedError): with self.assertRaises(NotImplementedError):
self.client.ajax_post( self.client.ajax_post(
self.url, self.url,
data={'tab_id': WikiTab.type, 'unsupported_request': None} data=json.dumps({
'tab_id_locator': {'tab_id': WikiTab.type},
'unsupported_request': None,
}),
) )
# invalid JSON POST request # invalid JSON POST request
with self.assertRaises(NotImplementedError): with self.assertRaises(NotImplementedError):
self.client.ajax_post( self.client.ajax_post(
self.url, self.url,
data={'invalid_request': None} data={'invalid_request': None},
) )
def test_view_index(self): def test_view_index(self):
...@@ -63,7 +66,7 @@ class TabsPageTests(CourseTestCase): ...@@ -63,7 +66,7 @@ class TabsPageTests(CourseTestCase):
resp = self.client.get_html(self.url) resp = self.client.get_html(self.url)
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
self.assertIn('course-nav-tab-list', resp.content) self.assertIn('course-nav-list', resp.content)
def test_reorder_tabs(self): def test_reorder_tabs(self):
"""Test re-ordering of tabs""" """Test re-ordering of tabs"""
...@@ -87,7 +90,7 @@ class TabsPageTests(CourseTestCase): ...@@ -87,7 +90,7 @@ class TabsPageTests(CourseTestCase):
# post the request # post the request
resp = self.client.ajax_post( resp = self.client.ajax_post(
self.url, self.url,
data={'tabs': [{'tab_id': tab_id} for tab_id in tab_ids]} data={'tabs': [{'tab_id': tab_id} for tab_id in tab_ids]},
) )
self.assertEqual(resp.status_code, 204) self.assertEqual(resp.status_code, 204)
...@@ -109,7 +112,7 @@ class TabsPageTests(CourseTestCase): ...@@ -109,7 +112,7 @@ class TabsPageTests(CourseTestCase):
# post the request # post the request
resp = self.client.ajax_post( resp = self.client.ajax_post(
self.url, self.url,
data={'tabs': [{'tab_id': tab_id} for tab_id in tab_ids]} data={'tabs': [{'tab_id': tab_id} for tab_id in tab_ids]},
) )
self.assertEqual(resp.status_code, 400) self.assertEqual(resp.status_code, 400)
resp_content = json.loads(resp.content) resp_content = json.loads(resp.content)
...@@ -123,7 +126,7 @@ class TabsPageTests(CourseTestCase): ...@@ -123,7 +126,7 @@ class TabsPageTests(CourseTestCase):
# post the request # post the request
resp = self.client.ajax_post( resp = self.client.ajax_post(
self.url, self.url,
data={'tabs': [{'tab_id': tab_id} for tab_id in invalid_tab_ids]} data={'tabs': [{'tab_id': tab_id} for tab_id in invalid_tab_ids]},
) )
self.check_invalid_tab_id_response(resp) self.check_invalid_tab_id_response(resp)
...@@ -141,7 +144,7 @@ class TabsPageTests(CourseTestCase): ...@@ -141,7 +144,7 @@ class TabsPageTests(CourseTestCase):
self.url, self.url,
data=json.dumps({ data=json.dumps({
'tab_id_locator': {'tab_id': old_tab.tab_id}, 'tab_id_locator': {'tab_id': old_tab.tab_id},
'is_hidden': new_is_hidden_setting 'is_hidden': new_is_hidden_setting,
}), }),
) )
self.assertEqual(resp.status_code, 204) self.assertEqual(resp.status_code, 204)
......
...@@ -43,9 +43,11 @@ FEATURES = { ...@@ -43,9 +43,11 @@ FEATURES = {
'GITHUB_PUSH': False, 'GITHUB_PUSH': False,
# for consistency in user-experience, keep the value of this setting in sync with the # for consistency in user-experience, keep the value of the following 3 settings
# one in lms/envs/common.py # in sync with the ones in lms/envs/common.py
'ENABLE_DISCUSSION_SERVICE': True, 'ENABLE_DISCUSSION_SERVICE': True,
'ENABLE_TEXTBOOK': True,
'ENABLE_STUDENT_NOTES': True,
'AUTH_USE_CERTIFICATES': False, 'AUTH_USE_CERTIFICATES': False,
......
...@@ -19,7 +19,7 @@ define ["jquery", "jquery.ui", "backbone", "js/views/feedback_prompt", "js/views ...@@ -19,7 +19,7 @@ define ["jquery", "jquery.ui", "backbone", "js/views/feedback_prompt", "js/views
@options.mast.find('.new-tab').on('click', @addNewTab) @options.mast.find('.new-tab').on('click', @addNewTab)
$('.add-pages .new-tab').on('click', @addNewTab) $('.add-pages .new-tab').on('click', @addNewTab)
$('.toggle-checkbox').on('click', @toggleVisibilityOfTab) $('.toggle-checkbox').on('click', @toggleVisibilityOfTab)
@$('.course-nav-tab-list').sortable( @$('.course-nav-list').sortable(
handle: '.drag-handle' handle: '.drag-handle'
update: @tabMoved update: @tabMoved
helper: 'clone' helper: 'clone'
...@@ -27,7 +27,7 @@ define ["jquery", "jquery.ui", "backbone", "js/views/feedback_prompt", "js/views ...@@ -27,7 +27,7 @@ define ["jquery", "jquery.ui", "backbone", "js/views/feedback_prompt", "js/views
placeholder: 'component-placeholder' placeholder: 'component-placeholder'
forcePlaceholderSize: true forcePlaceholderSize: true
axis: 'y' axis: 'y'
items: '> .sortable-tab' items: '> .is-movable'
) )
toggleVisibilityOfTab: (event, ui) => toggleVisibilityOfTab: (event, ui) =>
...@@ -85,7 +85,7 @@ define ["jquery", "jquery.ui", "backbone", "js/views/feedback_prompt", "js/views ...@@ -85,7 +85,7 @@ define ["jquery", "jquery.ui", "backbone", "js/views/feedback_prompt", "js/views
) )
$('.new-component-item').before(editor.$el) $('.new-component-item').before(editor.$el)
editor.$el.addClass('course-tab sortable-tab') editor.$el.addClass('course-tab is-movable')
editor.$el.addClass('new') editor.$el.addClass('new')
setTimeout(=> setTimeout(=>
editor.$el.removeClass('new') editor.$el.removeClass('new')
......
...@@ -40,7 +40,10 @@ ...@@ -40,7 +40,10 @@
<h3 class="sr">${_("Page Actions")}</h3> <h3 class="sr">${_("Page Actions")}</h3>
<ul> <ul>
<li class="nav-item"> <li class="nav-item">
<a href="#" class="button new-button new-tab"><i class="icon-plus"></i> ${_("New Page")}</a> <a href="#" class="button new-button new-tab"><i class="icon-plus"></i> ${_("New Page")}</a>
</li>
<li class="nav-item">
<a href="${lms_link}" rel="external" class="button view-button view-live-button">${_("View Live")}</a>
</li> </li>
</ul> </ul>
</nav> </nav>
...@@ -58,39 +61,34 @@ ...@@ -58,39 +61,34 @@
<ol class="course-nav-list course components"> <ol class="course-nav-list course components">
% for tab in tabs_to_render: % for tab in tabs_to_render:
<%
css_class = "course-tab"
if tab.is_movable:
css_class = css_class + " is-movable"
elif (not tab.is_movable) and (not tab.is_hideable):
css_class = css_class + " is-fixed"
%>
% if isinstance(tab, StaticTab): % if isinstance(tab, StaticTab):
<li class="component course-tab sortable-tab" data-locator="${tab.locator}" data-tab-id="${tab.tab_id}"></li> <li class="component ${css_class}" data-locator="${tab.locator}" data-tab-id="${tab.tab_id}"></li>
% else:
<% % else:
tab_name = _(tab.name) <li class="course-nav-item ${css_class}" data-tab-id="${tab.tab_id}">
item_names_formatted = ""
item_names = []
num_items = 0
if tab.is_collection:
item_names = [_(item.name) for item in tab.items(context_course)]
num_items = sum(1 for item in tab.items(context_course))
css_class = "course-nav-item course-nav-tab course-tab"
if tab.is_movable:
css_class = css_class + " sortable-tab"
%>
% if tab.is_hideable or tab.is_movable:
<li class="${css_class}" data-tab-id="${tab.tab_id}">
<div class="course-nav-item-header"> <div class="course-nav-item-header">
% if tab.is_collection: % if tab.is_collection:
<h3 class="title-sub">${tab_name}</h3>
<ul class="course-nav-item-children"> <h3 class="title-sub">${_(tab.name)}</h3>
% for item_name in item_names: <ul class="course-nav-item-children">
<li class="course-nav-item-child title"> % for item in tab.items(context_course):
${item_name} <li class="course-nav-item-child title">
</li> ${_(item.name)}
% endfor </li>
</ul> % endfor
</ul>
% else: % else:
<h3 class="title">${tab_name}</h3> <h3 class="title">${_(tab.name)}</h3>
% endif % endif
</div> </div>
...@@ -99,13 +97,13 @@ ...@@ -99,13 +97,13 @@
% if tab.is_hideable: % if tab.is_hideable:
<li class="action-item action-visible"> <li class="action-item action-visible">
<label><span class="sr">${_("Show this page")}</span></label> <label><span class="sr">${_("Show this page")}</span></label>
% if tab.is_hidden: % if tab.is_hidden:
<input type="checkbox" class="toggle-checkbox" data-tooltip="${_('Show/hide page')}" checked /> <input type="checkbox" class="toggle-checkbox" data-tooltip="${_('Show/hide page')}" checked />
% else: % else:
<input type="checkbox" class="toggle-checkbox" data-tooltip="${_('Show/hide page')}" /> <input type="checkbox" class="toggle-checkbox" data-tooltip="${_('Show/hide page')}" />
% endif % endif
<div class="action-button"><i class="icon-eye-open"></i><i class="icon-eye-close"></i></div> <div class="action-button"><i class="icon-eye-open"></i><i class="icon-eye-close"></i></div>
</li> </li>
% endif % endif
...@@ -123,30 +121,7 @@ ...@@ -123,30 +121,7 @@
% endif % endif
</li> </li>
% else: % endif
<li class="course-nav-item course_tab is-fixed" data-tab-id="${tab.tab_id}">
<div class="course-nav-item-header">
<h3 class="title">${tab_name}</h3>
% if tab.is_collection:
<ul class="course-nav-item-children">
% for item_name in item_names:
<li class="course-nav-item-child">
${item_name}
</li>
% endfor
</ul>
% endif
</ul>
</div>
<div class="drag-handle is-fixed" data-tooltip="${_('This page cannot be reordered')}">
<span class="sr">${_("This page cannot be reordered")}</span>
</div>
</li>
% endif
% endif
% endfor % endfor
<li class="new-component-item"></li> <li class="new-component-item"></li>
......
...@@ -17,7 +17,7 @@ class TabTestCase(unittest.TestCase): ...@@ -17,7 +17,7 @@ class TabTestCase(unittest.TestCase):
self.books = None self.books = None
def set_up_books(self, num_books): def set_up_books(self, num_books):
"""initializes the textbooks in the course and adds the given number of books to each textbook""" """Initializes the textbooks in the course and adds the given number of books to each textbook"""
self.books = [MagicMock() for _ in range(num_books)] self.books = [MagicMock() for _ in range(num_books)]
for book_index, book in enumerate(self.books): for book_index, book in enumerate(self.books):
book.title = 'Book{0}'.format(book_index) book.title = 'Book{0}'.format(book_index)
...@@ -76,7 +76,7 @@ class TabTestCase(unittest.TestCase): ...@@ -76,7 +76,7 @@ class TabTestCase(unittest.TestCase):
return tab return tab
def check_tab_equality(self, tab, dict_tab): def check_tab_equality(self, tab, dict_tab):
"""tests the equality methods on the given tab""" """Tests the equality methods on the given tab"""
self.assertEquals(tab, dict_tab) # test __eq__ self.assertEquals(tab, dict_tab) # test __eq__
ne_dict_tab = dict_tab ne_dict_tab = dict_tab
ne_dict_tab['type'] = 'fake_type' ne_dict_tab['type'] = 'fake_type'
...@@ -84,13 +84,13 @@ class TabTestCase(unittest.TestCase): ...@@ -84,13 +84,13 @@ class TabTestCase(unittest.TestCase):
self.assertNotEquals(tab, {'fake_key': 'fake_value'}) # test __ne__: missing type self.assertNotEquals(tab, {'fake_key': 'fake_value'}) # test __ne__: missing type
def check_tab_json_methods(self, tab): def check_tab_json_methods(self, tab):
"""tests the json from and to methods on the given tab""" """Tests the json from and to methods on the given tab"""
serialized_tab = tab.to_json() serialized_tab = tab.to_json()
deserialized_tab = tab.from_json(serialized_tab) deserialized_tab = tab.from_json(serialized_tab)
self.assertEquals(serialized_tab, deserialized_tab) self.assertEquals(serialized_tab, deserialized_tab)
def check_can_display_results(self, tab, expected_value=True, for_authenticated_users_only=False, for_staff_only=False): def check_can_display_results(self, tab, expected_value=True, for_authenticated_users_only=False, for_staff_only=False):
"""checks can display results for various users""" """Checks can display results for various users"""
if for_staff_only: if for_staff_only:
self.assertEquals( self.assertEquals(
expected_value, expected_value,
...@@ -108,7 +108,7 @@ class TabTestCase(unittest.TestCase): ...@@ -108,7 +108,7 @@ class TabTestCase(unittest.TestCase):
) )
def check_get_and_set_methods(self, tab): def check_get_and_set_methods(self, tab):
"""test __getitem__ and __setitem__ calls""" """Test __getitem__ and __setitem__ calls"""
self.assertEquals(tab['type'], tab.type) self.assertEquals(tab['type'], tab.type)
self.assertEquals(tab['tab_id'], tab.tab_id) self.assertEquals(tab['tab_id'], tab.tab_id)
with self.assertRaises(KeyError): with self.assertRaises(KeyError):
...@@ -120,7 +120,7 @@ class TabTestCase(unittest.TestCase): ...@@ -120,7 +120,7 @@ class TabTestCase(unittest.TestCase):
tab['invalid_key'] = 'New Value' tab['invalid_key'] = 'New Value'
def check_get_and_set_method_for_key(self, tab, key): def check_get_and_set_method_for_key(self, tab, key):
"""test __getitem__ and __setitem__ for the given key""" """Test __getitem__ and __setitem__ for the given key"""
old_value = tab[key] old_value = tab[key]
new_value = 'New Value' new_value = 'New Value'
tab[key] = new_value tab[key] = new_value
...@@ -540,7 +540,7 @@ class CourseTabListTestCase(TabListTestCase): ...@@ -540,7 +540,7 @@ class CourseTabListTestCase(TabListTestCase):
)): )):
self.assertEquals(tab.type, self.course.tabs[i].type) self.assertEquals(tab.type, self.course.tabs[i].type)
# enumerate the tabs and verify textbooks and the instructor tab # enumerate the tabs and verify textbooks and the instructor tab
for i, tab in enumerate(tabs.CourseTabList.iterate_displayable( for i, tab in enumerate(tabs.CourseTabList.iterate_displayable(
self.course, self.course,
self.settings, self.settings,
...@@ -555,8 +555,21 @@ class CourseTabListTestCase(TabListTestCase): ...@@ -555,8 +555,21 @@ class CourseTabListTestCase(TabListTestCase):
# all other tabs must match the expected type # all other tabs must match the expected type
self.assertEquals(tab.type, self.course.tabs[i].type) self.assertEquals(tab.type, self.course.tabs[i].type)
# test including non-empty collections
self.assertIn(
tabs.HtmlTextbookTabs(),
list(tabs.CourseTabList.iterate_displayable_cms(self.course, self.settings)),
)
# test not including empty collections
self.course.html_textbooks = []
self.assertNotIn(
tabs.HtmlTextbookTabs(),
list(tabs.CourseTabList.iterate_displayable_cms(self.course, self.settings)),
)
def test_get_tab_by_methods(self): def test_get_tab_by_methods(self):
"""tests the get_tab methods in CourseTabList""" """Tests the get_tab methods in CourseTabList"""
self.course.tabs = self.all_valid_tab_list self.course.tabs = self.all_valid_tab_list
for tab in self.course.tabs: for tab in self.course.tabs:
...@@ -587,7 +600,7 @@ class DiscussionLinkTestCase(TabTestCase): ...@@ -587,7 +600,7 @@ class DiscussionLinkTestCase(TabTestCase):
@staticmethod @staticmethod
def _reverse(course): def _reverse(course):
"""custom reverse function""" """Custom reverse function"""
def reverse_discussion_link(viewname, args): def reverse_discussion_link(viewname, args):
"""reverse lookup for discussion link""" """reverse lookup for discussion link"""
if viewname == "django_comment_client.forum.views.forum_form_discussion" and args == [course.id]: if viewname == "django_comment_client.forum.views.forum_form_discussion" and args == [course.id]:
......
...@@ -76,10 +76,11 @@ FEATURES = { ...@@ -76,10 +76,11 @@ FEATURES = {
'FORCE_UNIVERSITY_DOMAIN': False, # set this to the university domain to use, as an override to HTTP_HOST 'FORCE_UNIVERSITY_DOMAIN': False, # set this to the university domain to use, as an override to HTTP_HOST
# set to None to do no university selection # set to None to do no university selection
'ENABLE_TEXTBOOK': True, # for consistency in user-experience, keep the value of the following 3 settings
# in sync with the corresponding ones in cms/envs/common.py
# for consistency in user-experience, keep the value of this setting in sync with the one in cms/envs/common.py
'ENABLE_DISCUSSION_SERVICE': True, 'ENABLE_DISCUSSION_SERVICE': True,
'ENABLE_TEXTBOOK': True,
'ENABLE_STUDENT_NOTES': True, # enables the student notes API and UI.
# discussion home panel, which includes a subscription on/off setting for discussion digest emails. # discussion home panel, which includes a subscription on/off setting for discussion digest emails.
# this should remain off in production until digest notifications are online. # this should remain off in production until digest notifications are online.
...@@ -146,9 +147,6 @@ FEATURES = { ...@@ -146,9 +147,6 @@ FEATURES = {
# segment.io for LMS--need to explicitly turn it on for production. # segment.io for LMS--need to explicitly turn it on for production.
'SEGMENT_IO_LMS': False, 'SEGMENT_IO_LMS': False,
# Enables the student notes API and UI.
'ENABLE_STUDENT_NOTES': True,
# Provide a UI to allow users to submit feedback from the LMS (left-hand help modal) # Provide a UI to allow users to submit feedback from the LMS (left-hand help modal)
'ENABLE_FEEDBACK_SUBMISSION': False, 'ENABLE_FEEDBACK_SUBMISSION': False,
......
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