Commit 6d82cf09 by cahrens

Correct publish titles, code review feedback.

parent ef581e11
......@@ -1209,7 +1209,7 @@ class ContentStoreTest(ContentStoreTestCase):
resp = self._show_course_overview(course.id)
self.assertContains(
resp,
'<article class="outline" data-locator="{locator}" data-course-key="{course_key}">'.format(
'<article class="outline outline-course" data-locator="{locator}" data-course-key="{course_key}">'.format(
locator='i4x://MITx/999/course/Robot_Super_Course',
course_key='MITx/999/Robot_Super_Course',
),
......
......@@ -149,7 +149,7 @@ def course_image_url(course):
path = loc.to_deprecated_string()
return path
# pylint: disable=invalid-name
def is_currently_visible_to_students(xblock):
"""
Returns true if there is a published version of the xblock that is currently visible to students.
......
......@@ -178,10 +178,6 @@ def container_handler(request, usage_key_string):
# about the block's ancestors and siblings for use by the Unit Outline.
xblock_info = create_xblock_info(xblock, include_ancestor_info=is_unit_page)
# On the unit page only, add 'has_changes' to indicate when there are changes that can be discarded.
if is_unit_page:
xblock_info['has_changes'] = modulestore().has_changes(xblock.location)
# Create the link for preview.
preview_lms_base = settings.FEATURES.get('PREVIEW_LMS_BASE')
# need to figure out where this item is in the list of children as the
......
......@@ -632,6 +632,9 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
published = modulestore().has_item(xblock.location, revision=ModuleStoreEnum.RevisionOption.published_only)
currently_visible_to_students = is_currently_visible_to_students(xblock)
is_xblock_unit = is_unit(xblock)
is_unit_with_changes = is_xblock_unit and modulestore().has_changes(xblock.location)
xblock_info = {
"id": unicode(xblock.location),
"display_name": xblock.display_name_with_default,
......@@ -646,7 +649,7 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
"release_date": release_date,
"release_date_from": _get_release_date_from(xblock) if release_date else None,
"currently_visible_to_students": currently_visible_to_students,
"visibility_state": _compute_visibility_state(xblock, child_info) if not xblock.category == 'course' else None
"visibility_state": _compute_visibility_state(xblock, child_info, is_unit_with_changes) if not xblock.category == 'course' else None
}
if data is not None:
xblock_info["data"] = data
......@@ -656,6 +659,11 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
xblock_info['ancestor_info'] = _create_xblock_ancestor_info(xblock)
if child_info:
xblock_info['child_info'] = child_info
# On the unit page only, add 'has_changes' to indicate when there are changes that can be discarded.
# We don't add it in general because it is an expensive operation.
if is_xblock_unit:
xblock_info['has_changes'] = is_unit_with_changes
return xblock_info
......@@ -680,18 +688,20 @@ class VisibilityState(object):
"""
live = 'live'
ready = 'ready'
unscheduled = 'unscheduled' # unscheduled
unscheduled = 'unscheduled'
needs_attention = 'needs_attention'
staff_only = 'staff_only'
def _compute_visibility_state(xblock, child_info):
def _compute_visibility_state(xblock, child_info, is_unit_with_changes):
"""
Returns the current publish state for the specified xblock and its children
"""
if xblock.visible_to_staff_only:
return VisibilityState.staff_only
elif is_unit(xblock) and modulestore().has_changes(xblock.location):
elif is_unit_with_changes:
# Note that a unit that has never been published will fall into this category,
# as well as previously published units with draft content.
return VisibilityState.needs_attention
is_unscheduled = xblock.start == DEFAULT_START_DATE
is_live = datetime.now(UTC) > xblock.start
......
......@@ -231,6 +231,7 @@ class TestCourseOutline(CourseTestCase):
self.assertEqual(json_response['category'], 'course')
self.assertEqual(json_response['id'], 'i4x://MITx/999/course/Robot_Super_Course')
self.assertEqual(json_response['display_name'], 'Robot Super Course')
self.assertTrue(json_response['published'])
self.assertIsNone(json_response['visibility_state'])
# Now verify the first child
......@@ -240,6 +241,7 @@ class TestCourseOutline(CourseTestCase):
self.assertEqual(first_child_response['category'], 'chapter')
self.assertEqual(first_child_response['id'], 'i4x://MITx/999/chapter/Week_1')
self.assertEqual(first_child_response['display_name'], 'Week 1')
self.assertTrue(json_response['published'])
self.assertEqual(first_child_response['visibility_state'], VisibilityState.unscheduled)
self.assertTrue(len(first_child_response['child_info']['children']) > 0)
......@@ -253,6 +255,7 @@ class TestCourseOutline(CourseTestCase):
self.assertIsNotNone(json_response['display_name'])
self.assertIsNotNone(json_response['id'])
self.assertIsNotNone(json_response['category'])
self.assertTrue(json_response['published'])
if json_response.get('child_info', None):
for child_response in json_response['child_info']['children']:
self.assert_correct_json_response(child_response)
......
......@@ -46,7 +46,8 @@ define(["backbone", "underscore", "js/utils/module"], function(Backbone, _, Modu
"published_by": null,
/**
* True if the xblock has changes.
* Note: this is not always provided as a performance optimization.
* Note: this is not always provided as a performance optimization. It is only provided for
* verticals functioning as units.
*/
"has_changes": null,
/**
......
......@@ -118,6 +118,8 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers"
category: 'vertical',
studio_url: '/container/mock-unit',
is_container: true,
has_changes: false,
published: true,
visibility_state: 'unscheduled',
edited_on: 'Jul 02, 2014 at 20:56 UTC',
edited_by: 'MockUser'
......
......@@ -116,7 +116,6 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
published: this.model.get('published'),
publishedOn: this.model.get('published_on'),
publishedBy: this.model.get('published_by'),
releasedToStudents: this.model.get('released_to_students'),
releaseDate: this.model.get('release_date'),
releaseDateFrom: this.model.get('release_date_from')
}));
......
......@@ -4,7 +4,7 @@
define(["jquery", "underscore", "gettext", "js/views/utils/view_utils", "js/utils/module"],
function($, _, gettext, ViewUtils, ModuleUtils) {
var addXBlock, deleteXBlock, createUpdateRequestData, updateXBlockField, VisibilityState,
getXBlockVisibilityClass;
getXBlockVisibilityClass, getXBlockListTypeClass;
/**
* Represents the possible visibility states for an xblock:
......@@ -131,11 +131,24 @@ define(["jquery", "underscore", "gettext", "js/views/utils/view_utils", "js/util
return '';
};
getXBlockListTypeClass = function (xblockType) {
var listType = 'list-unknown';
if (xblockType === 'course') {
listType = 'list-sections';
} else if (xblockType === 'section') {
listType = 'list-subsections';
} else if (xblockType === 'subsection') {
listType = 'list-units';
}
return listType;
};
return {
'VisibilityState': VisibilityState,
'addXBlock': addXBlock,
'deleteXBlock': deleteXBlock,
'updateXBlockField': updateXBlockField,
'getXBlockVisibilityClass': getXBlockVisibilityClass
'getXBlockVisibilityClass': getXBlockVisibilityClass,
'getXBlockListTypeClass': getXBlockListTypeClass
};
});
......@@ -69,6 +69,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
html = this.template({
xblockInfo: xblockInfo,
visibilityClass: XBlockViewUtils.getXBlockVisibilityClass(xblockInfo.get('visibility_state')),
typeListClass: XBlockViewUtils.getXBlockListTypeClass(xblockType),
parentInfo: this.parentInfo,
xblockType: xblockType,
parentType: parentType,
......
......@@ -47,7 +47,7 @@ from contentstore.utils import reverse_usage_url
<h3 class="sr">${_("Page Actions")}</h3>
<ul>
<li class="nav-item">
<a href="#" class="button button-new" data-category="chapter" data-parent="${context_course.location}" data-default-name="Section">
<a href="#" class="button button-new" data-category="chapter" data-parent="${context_course.location}" data-default-name="${_('Section')}">
<i class="icon-plus"></i>${_('New Section')}
</a>
</li>
......
......@@ -2,28 +2,20 @@
var category = xblockInfo.get('category');
var releasedToStudents = xblockInfo.get('released_to_students');
var visibilityState = xblockInfo.get('visibility_state');
var listType = 'list-unknown';
if (xblockType === 'course') {
listType = 'list-sections';
} else if (xblockType === 'section') {
listType = 'list-subsections';
} else if (xblockType === 'subsection') {
listType = 'list-units';
}
var published = xblockInfo.get('published');
var statusMessage = null;
var statusType = null;
if (visibilityState === 'staff_only') {
statusType = 'staff-only';
statusMessage = 'Contains staff only content';
statusMessage = gettext('Contains staff only content');
} else if (visibilityState === 'needs_attention') {
if (category === 'vertical') {
statusType = 'warning';
if (releasedToStudents) {
statusMessage = 'Unpublished changes to live content';
if (published && releasedToStudents) {
statusMessage = gettext('Unpublished changes to live content');
} else {
statusMessage = 'Unpublished units will not be released';
statusMessage = gettext('Unpublished units will not be released');
}
}
}
......@@ -75,7 +67,7 @@ if (statusType === 'warning') {
<% if (category !== 'vertical') { %>
<div class="status-release">
<p>
<span class="sr status-release-label">Release Status:</span>
<span class="sr status-release-label"><%= gettext('Release Status:') %></span>
<span class="status-release-value">
<% if (xblockInfo.get('released_to_students')) { %>
<i class="icon icon-check-sign"></i>
......@@ -115,7 +107,7 @@ if (statusType === 'warning') {
</div>
<% } else { %>
<div class="outline-content <%= xblockType %>-content">
<ol class="<%= listType %> is-sortable">
<ol class="<%= typeListClass %> is-sortable">
</ol>
<% if (childType) { %>
<div class="add-<%= childType %> add-item">
......
......@@ -4,9 +4,9 @@ if (visibilityState === 'staff_only') {
title = gettext("Unpublished (Staff only)");
} else if (visibilityState === 'live') {
title = gettext("Published and Live");
} else if (visibilityState === 'ready') {
} else if (published && !hasChanges) {
title = gettext("Published");
} else if (visibilityState === 'needs_attention') {
} else if (published && hasChanges) {
title = gettext("Draft (Unpublished changes)");
}
......@@ -19,7 +19,7 @@ if (visibilityState === 'live') {
var visibleToStaffOnly = visibilityState === 'staff_only';
%>
<div class="bit-publishing <%= visibilityClass %>">
<div class="bit-publishing <%= visibilityClass %> <% if (releaseDate) { %>is-scheduled<% } %>">
<h3 class="bar-mod-title pub-status"><span class="sr"><%= gettext("Publishing Status") %></span>
<%= title %>
</h3>
......
<%
var listType = 'list-for-' + xblockType;
if (xblockType === 'course') {
listType = 'list-sections';
} else if (xblockType === 'section') {
listType = 'list-subsections';
} else if (xblockType === 'subsection') {
listType = 'list-units';
}
%>
<% if (parentInfo) { %>
<li class="outline-item outline-<%= xblockType %> <%= visibilityClass %>"
data-parent="<%= parentInfo.get('id') %>" data-locator="<%= xblockInfo.get('id') %>">
......@@ -21,7 +11,7 @@ if (xblockType === 'course') {
<% } %>
<div class="<%= xblockType %>-content outline-content">
<ol class="<%= listType %>">
<ol class="<%= typeListClass %>">
</ol>
<% if (childType) { %>
<div class="add-<%= childType %> add-item">
......
......@@ -18,4 +18,3 @@ class HtmlComponentEditorView(ComponentEditorView):
ActionChains(self.browser).click(editor).\
send_keys([Keys.CONTROL, 'a']).key_up(Keys.CONTROL).send_keys(content).perform()
click_css(self, 'a.action-save')
......@@ -380,7 +380,7 @@ class UnitPublishingTest(ContainerBase):
__test__ = True
PUBLISHED_STATUS = "Publishing Status\nPublished"
PUBLISHED_LIVE_STATUS ="Publishing Status\nPublished and Live"
PUBLISHED_LIVE_STATUS = "Publishing Status\nPublished and Live"
DRAFT_STATUS = "Publishing Status\nDraft (Unpublished changes)"
LOCKED_STATUS = "Publishing Status\nUnpublished (Staff only)"
RELEASE_TITLE_RELEASED = "RELEASED:"
......@@ -398,6 +398,7 @@ class UnitPublishingTest(ContainerBase):
self.courseware = CoursewarePage(self.browser, self.course_id)
past_start_date = datetime.datetime(1974, 6, 22)
self.past_start_date_text = "Jun 22, 1974 at 00:00 UTC"
future_start_date = datetime.datetime(2100, 9, 13)
course_fixture.add_children(
XBlockFixtureDesc('chapter', 'Test Section').add_children(
......@@ -407,20 +408,29 @@ class UnitPublishingTest(ContainerBase):
)
)
),
XBlockFixtureDesc('chapter', 'Unlocked Section', metadata={'start': past_start_date.isoformat()}).add_children(
XBlockFixtureDesc('sequential', 'Unlocked Subsection').add_children(
XBlockFixtureDesc('vertical', 'Unlocked Unit').add_children(
XBlockFixtureDesc('problem', '<problem></problem>', data=self.html_content)
)
)
XBlockFixtureDesc('chapter', 'Unlocked Section',
metadata={'start': past_start_date.isoformat()}).add_children(
XBlockFixtureDesc('sequential', 'Unlocked Subsection').add_children(
XBlockFixtureDesc('vertical', 'Unlocked Unit').add_children(
XBlockFixtureDesc('problem', '<problem></problem>', data=self.html_content)
)
)
),
XBlockFixtureDesc('chapter', 'Section With Locked Unit').add_children(
XBlockFixtureDesc('sequential', 'Subsection With Locked Unit', metadata={'start': past_start_date.isoformat()}).add_children(
XBlockFixtureDesc('vertical', 'Locked Unit', metadata={'visible_to_staff_only': True}).add_children(
XBlockFixtureDesc('discussion', '', data=self.html_content)
)
)
)
XBlockFixtureDesc('sequential', 'Subsection With Locked Unit',
metadata={'start': past_start_date.isoformat()}).add_children(
XBlockFixtureDesc('vertical', 'Locked Unit',
metadata={'visible_to_staff_only': True}).add_children(
XBlockFixtureDesc('discussion', '', data=self.html_content)
)
)
),
XBlockFixtureDesc('chapter', 'Unreleased Section',
metadata={'start': future_start_date.isoformat()}).add_children(
XBlockFixtureDesc('sequential', 'Unreleased Subsection').add_children(
XBlockFixtureDesc('vertical', 'Unreleased Unit')
)
)
)
def test_publishing(self):
......@@ -658,6 +668,25 @@ class UnitPublishingTest(ContainerBase):
self._view_published_version(unit)
self.assertEqual(0, self.courseware.num_xblock_components)
def test_published_not_live(self):
"""
Scenario: The publish title displays correctly for units that are not live
Given I have a published unit with no unpublished changes that releases in the future
When I go to the unit page in Studio
Then the title in the Publish information box is "Published"
And when I add a component to the unit
Then the title in the Publish information box is "Draft (Unpublished changes)"
And when I click the Publish button
Then the title in the Publish information box is "Published"
"""
unit = self.go_to_unit_page('Unreleased Section', 'Unreleased Subsection', 'Unreleased Unit')
self._verify_publish_title(unit, self.PUBLISHED_STATUS)
add_discussion(unit)
self._verify_publish_title(unit, self.DRAFT_STATUS)
unit.publish_action.click()
unit.wait_for_ajax()
self._verify_publish_title(unit, self.PUBLISHED_STATUS)
def _view_published_version(self, unit):
"""
Goes to the published version, then waits for the browser to load the page.
......
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