Commit 6d82cf09 by cahrens

Correct publish titles, code review feedback.

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