-
+

You haven't added any content to this course yet. - + Add Section

diff --git a/cms/templates/js/publish-history.underscore b/cms/templates/js/publish-history.underscore index d28c329ccf..3f4e5c5a90 100644 --- a/cms/templates/js/publish-history.underscore +++ b/cms/templates/js/publish-history.underscore @@ -1,16 +1,16 @@ +<% +var copy = gettext("Never published"); +if (published_on && published_by) { + var message = gettext("Last published %(last_published_date)s by %(publish_username)s"); + copy = interpolate(message, { + last_published_date: '' + published_on + '', + publish_username: '' + published_by + '' + }, true); +} else if (published) { + copy = gettext("Previously published"); +} +%> +
-

- <% if (published) { - if (published_on && published_by) { - var message = gettext("Last published %(last_published_date)s by %(publish_username)s"); %> - <%= interpolate(message, { - last_published_date: '' + published_on + '', - publish_username: '' + published_by + '' }, true) %> - <% } else { %> - <%= gettext("Previously published") %> - <% } %> - <% } else { %> - <%= gettext("Never published") %> - <% } %> -

-
\ No newline at end of file +

<%= copy %>

+
diff --git a/cms/templates/js/publish-xblock.underscore b/cms/templates/js/publish-xblock.underscore index f9b30bcb38..21fc7a1fe7 100644 --- a/cms/templates/js/publish-xblock.underscore +++ b/cms/templates/js/publish-xblock.underscore @@ -1,31 +1,45 @@ <% -var publishClasses = ""; +var publishClass = ''; +if (publishState === 'staff_only') { + publishClass = 'is-staff-only'; +} else if (publishState === 'live') { + publishClass = 'is-live is-published is-released'; +} else if (publishState === 'ready') { + publishClass = 'is-ready is-published'; +} else if (publishState === 'has_unpublished_content') { + publishClass = 'has-warnings is-draft'; +} + var title = gettext("Draft (Never published)"); -if (published) { - if (published && hasChanges) { - publishClasses = publishClasses + " is-draft"; - title = gettext("Draft (Unpublished changes)"); - } else { - publishClasses = publishClasses + " is-published"; - title = gettext("Published"); - } -} -if (releaseDate) { - publishClasses = publishClasses + " is-scheduled"; -} -if (visibleToStaffOnly) { - publishClasses = publishClasses + " is-staff-only"; +if (publishState === 'staff_only') { title = gettext("Unpublished (Staff only)"); +} else if (publishState === 'live') { + title = gettext("Published and Live"); +} else if (publishState === 'ready') { + title = gettext("Published"); +} else if (publishState === 'has_unpublished_content') { + title = gettext("Draft (Unpublished changes)"); } + +var releaseLabel = gettext("Release:"); +if (publishState === 'live') { + releaseLabel = gettext("Released:"); +} else if (publishState === 'ready') { + releaseLabel = gettext("Scheduled:"); +} + +var canPublish = publishState !== 'ready' && publishState !== 'live'; +var canDiscardChanges = publishState === 'has_unpublished_content'; +var visibleToStaffOnly = publishState === 'staff_only'; %> -
+

<%= gettext("Publishing Status") %> <%= title %>

- <% if (hasChanges && editedOn && editedBy) { + <% if (publishState === 'has_unpublished_content' && editedOn && editedBy) { var message = gettext("Draft saved on %(last_saved_date)s by %(edit_username)s") %> <%= interpolate(message, { last_saved_date: '' + editedOn + '', @@ -42,17 +56,7 @@ if (visibleToStaffOnly) {

-
- <% if (published && releaseDate) { - if (releasedToStudents) { %> - <%= gettext("Released:") %> - <% } else { %> - <%= gettext("Scheduled:") %> - <% } - } else { %> - <%= gettext("Release:") %> - <% } %> -
+
<%= releaseLabel %>

<% if (releaseDate) { %> <% var message = gettext("%(release_date)s with %(section_or_subsection)s") %> @@ -87,12 +91,12 @@ if (visibleToStaffOnly) {

  • - <%= gettext("Publish") %>
  • - <%= gettext("Discard Changes") %>
  • diff --git a/cms/templates/js/unit-outline.underscore b/cms/templates/js/unit-outline.underscore index 70d3cf4123..ae30d8d565 100644 --- a/cms/templates/js/unit-outline.underscore +++ b/cms/templates/js/unit-outline.underscore @@ -1,27 +1,50 @@ +<% +var publishState = xblockInfo.get('publish_state'); +var publishClass = ''; +if (publishState === 'staff_only') { + publishClass = 'is-staff-only'; +} else if (publishState === 'live') { + publishClass = 'is-live'; +} else if (publishState === 'ready') { + publishClass = 'is-ready'; +} else if (publishState === 'has_unpublished_content') { + publishClass = 'has_warnings'; +} + +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 (childType) { %> - - <% } %> +
    +
      +
    + <% if (childType) { %> + + <% } %> +
    <% if (parentInfo) { %> -
  • + <% } %> diff --git a/cms/templates/js/xblock-outline.underscore b/cms/templates/js/xblock-outline.underscore index 2dec21afd6..0443094088 100644 --- a/cms/templates/js/xblock-outline.underscore +++ b/cms/templates/js/xblock-outline.underscore @@ -1,5 +1,5 @@ <% if (parentInfo) { %> -
  • @@ -56,7 +56,7 @@ <% if (!parentInfo && xblockInfo.get('child_info') && xblockInfo.get('child_info').children.length === 0) { %>

    <%= gettext("You haven't added any content to this course yet.") %> - <%= addChildLabel %> @@ -68,7 +68,7 @@ <% if (childType) { %>

    - <%= addChildLabel %> diff --git a/common/lib/xmodule/xmodule/modulestore/__init__.py b/common/lib/xmodule/xmodule/modulestore/__init__.py index 2a38f4e677..b14b85ca03 100644 --- a/common/lib/xmodule/xmodule/modulestore/__init__.py +++ b/common/lib/xmodule/xmodule/modulestore/__init__.py @@ -89,11 +89,11 @@ class ModuleStoreEnum(object): # user ID to use for tests that do not have a django user available test = -3 -class PublishState(object): - """ - The publish state for a given xblock-- either 'draft', 'private', or 'public'. - Currently in CMS, an xblock can only be in 'draft' or 'private' if it is at or below the Unit level. +class LegacyPublishState(object): + """ + The legacy publish state for a given xblock-- either 'draft', 'private', or 'public'. These states + are no longer used in Studio directly, but are still referenced in a few places. """ draft = 'draft' private = 'private' @@ -301,10 +301,10 @@ class ModuleStoreRead(object): Returns whether this xblock is draft, public, or private. Returns: - PublishState.draft - content is in the process of being edited, but still has a previous + LegacyPublishState.draft - content is in the process of being edited, but still has a previous version deployed to LMS - PublishState.public - content is locked and deployed to LMS - PublishState.private - content is editable and not deployed to LMS + LegacyPublishState.public - content is locked and deployed to LMS + LegacyPublishState.private - content is editable and not deployed to LMS """ pass @@ -522,7 +522,7 @@ class ModuleStoreReadBase(ModuleStoreRead): """ Returns PublishState.public since this is a read-only store. """ - return PublishState.public + return LegacyPublishState.public def heartbeat(self): """ diff --git a/common/lib/xmodule/xmodule/modulestore/mixed.py b/common/lib/xmodule/xmodule/modulestore/mixed.py index e847ea9131..30d75d64fe 100644 --- a/common/lib/xmodule/xmodule/modulestore/mixed.py +++ b/common/lib/xmodule/xmodule/modulestore/mixed.py @@ -439,10 +439,10 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): Returns whether this xblock is draft, public, or private. Returns: - PublishState.draft - content is in the process of being edited, but still has a previous + LegacyPublishState.draft - content is in the process of being edited, but still has a previous version deployed to LMS - PublishState.public - content is locked and deployed to LMS - PublishState.private - content is editable and not deployed to LMS + LegacyPublishState.public - content is locked and deployed to LMS + LegacyPublishState.private - content is editable and not deployed to LMS """ course_id = xblock.scope_ids.usage_id.course_key store = self._get_modulestore_for_courseid(course_id) diff --git a/common/lib/xmodule/xmodule/modulestore/mongo/draft.py b/common/lib/xmodule/xmodule/modulestore/mongo/draft.py index cd7e0bc4c2..1c0d6ae877 100644 --- a/common/lib/xmodule/xmodule/modulestore/mongo/draft.py +++ b/common/lib/xmodule/xmodule/modulestore/mongo/draft.py @@ -11,7 +11,7 @@ import logging from opaque_keys.edx.locations import Location from xmodule.exceptions import InvalidVersionError -from xmodule.modulestore import PublishState, ModuleStoreEnum +from xmodule.modulestore import LegacyPublishState, ModuleStoreEnum from xmodule.modulestore.exceptions import ( ItemNotFoundError, DuplicateItemError, InvalidBranchSetting, DuplicateCourseError ) @@ -613,7 +613,7 @@ class DraftModuleStore(MongoModuleStore): return False # don't check children if this block has changes (is not public) - if self.compute_publish_state(item) != PublishState.public: + if self.compute_publish_state(item) != LegacyPublishState.public: return True # if this block doesn't have changes, then check its children elif item.has_children: @@ -792,10 +792,10 @@ class DraftModuleStore(MongoModuleStore): Returns whether this xblock is draft, public, or private. Returns: - PublishState.draft - content is in the process of being edited, but still has a previous + LegacyPublishState.draft - content is in the process of being edited, but still has a previous version deployed to LMS - PublishState.public - content is locked and deployed to LMS - PublishState.private - content is editable and not deployed to LMS + LegacyPublishState.public - content is locked and deployed to LMS + LegacyPublishState.private - content is editable and not deployed to LMS """ if getattr(xblock, 'is_draft', False): published_xblock_location = as_published(xblock.location) @@ -803,11 +803,11 @@ class DraftModuleStore(MongoModuleStore): {'_id': published_xblock_location.to_deprecated_son()} ) if published_item is None: - return PublishState.private + return LegacyPublishState.private else: - return PublishState.draft + return LegacyPublishState.draft else: - return PublishState.public + return LegacyPublishState.public def _verify_branch_setting(self, expected_branch_setting): """ diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py index c41644ee14..9b8bf4986e 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py @@ -4,7 +4,7 @@ Module for the dual-branch fall-back Draft->Published Versioning ModuleStore from ..exceptions import ItemNotFoundError from split import SplitMongoModuleStore, EXCLUDE_ALL -from xmodule.modulestore import ModuleStoreEnum, PublishState +from xmodule.modulestore import ModuleStoreEnum, LegacyPublishState from xmodule.modulestore.exceptions import InsufficientSpecificationError from xmodule.modulestore.draft_and_published import ModuleStoreDraftAndPublished, DIRECT_ONLY_CATEGORIES, UnsupportedRevisionError @@ -251,10 +251,11 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS Returns whether this xblock is draft, public, or private. Returns: - PublishState.draft - published exists and is different from draft - PublishState.public - published exists and is the same as draft - PublishState.private - no published version exists + LegacyPublishState.draft - published exists and is different from draft + LegacyPublishState.public - published exists and is the same as draft + LegacyPublishState.private - no published version exists """ + # TODO figure out what to say if xblock is not from the HEAD of its branch def get_head(branch): course_structure = self._lookup_course(xblock.location.course_key.for_branch(branch))['structure'] return self._get_block_from_structure(course_structure, xblock.location.block_id) @@ -271,13 +272,13 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS if not published_head: # published version does not exist - return PublishState.private + return LegacyPublishState.private elif get_version(draft_head) == get_version(published_head): # published and draft versions are equal - return PublishState.public + return LegacyPublishState.public else: # published and draft versions differ - return PublishState.draft + return LegacyPublishState.draft def convert_to_draft(self, location, user_id): """ diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py b/common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py index 8667d8956f..e61cab8ae9 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py @@ -9,7 +9,7 @@ from pytz import UTC from xmodule.tests import DATA_DIR from opaque_keys.edx.locations import Location -from xmodule.modulestore import ModuleStoreEnum, PublishState +from xmodule.modulestore import ModuleStoreEnum, LegacyPublishState from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.exceptions import InvalidVersionError @@ -991,22 +991,22 @@ class TestMixedModuleStore(unittest.TestCase): item_location = item.location.version_agnostic() mongo_store = self.store._get_modulestore_for_courseid(self._course_key_from_string(self.MONGO_COURSEID)) with check_mongo_calls(mongo_store, max_find, max_send): - self.assertEquals(self.store.compute_publish_state(item), PublishState.private) + self.assertEquals(self.store.compute_publish_state(item), LegacyPublishState.private) # Private -> Public self.store.publish(item_location, self.user_id) item = self.store.get_item(item_location) - self.assertEquals(self.store.compute_publish_state(item), PublishState.public) + self.assertEquals(self.store.compute_publish_state(item), LegacyPublishState.public) # Public -> Private self.store.unpublish(item_location, self.user_id) item = self.store.get_item(item_location) - self.assertEquals(self.store.compute_publish_state(item), PublishState.private) + self.assertEquals(self.store.compute_publish_state(item), LegacyPublishState.private) # Private -> Public self.store.publish(item_location, self.user_id) item = self.store.get_item(item_location) - self.assertEquals(self.store.compute_publish_state(item), PublishState.public) + self.assertEquals(self.store.compute_publish_state(item), LegacyPublishState.public) # Public -> Draft with NO changes # Note: This is where Split and Mongo differ @@ -1014,14 +1014,14 @@ class TestMixedModuleStore(unittest.TestCase): item = self.store.get_item(item_location) self.assertEquals( self.store.compute_publish_state(item), - PublishState.draft if default_ms == 'draft' else PublishState.public + LegacyPublishState.draft if default_ms == 'draft' else LegacyPublishState.public ) # Draft WITH changes item.display_name = 'new name' item = self.store.update_item(item, self.user_id) self.assertTrue(self.store.has_changes(item.location)) - self.assertEquals(self.store.compute_publish_state(item), PublishState.draft) + self.assertEquals(self.store.compute_publish_state(item), LegacyPublishState.draft) @ddt.data('draft', 'split') def test_auto_publish(self, default_ms): diff --git a/common/test/acceptance/pages/studio/overview.py b/common/test/acceptance/pages/studio/overview.py index 5e4edfedda..08a2b30993 100644 --- a/common/test/acceptance/pages/studio/overview.py +++ b/common/test/acceptance/pages/studio/overview.py @@ -113,7 +113,7 @@ class CourseOutlineContainer(CourseOutlineItem): """ click_css( self, - self._bounded_selector(".add-xblock-component a.add-button"), + self._bounded_selector(".add-item a.button-new"), require_notification=require_notification, ) @@ -125,7 +125,7 @@ class CourseOutlineContainer(CourseOutlineItem): self.browser.execute_script("jQuery.fx.off = true;") def subsection_expanded(): - add_button = self.q(css=self._bounded_selector('> .add-xblock-component a.add-button')).first.results + add_button = self.q(css=self._bounded_selector('> .outline-content > .add-item a.button-new')).first.results return add_button and add_button[0].is_displayed() currently_expanded = subsection_expanded() @@ -171,8 +171,8 @@ class CourseOutlineUnit(CourseOutlineChild): PageObject that wraps a unit link on the Studio Course Outline page. """ url = None - BODY_SELECTOR = '.outline-item-unit' - NAME_SELECTOR = '.xblock-title a' + BODY_SELECTOR = '.outline-unit' + NAME_SELECTOR = '.unit-title a' def go_to(self): """ @@ -191,7 +191,9 @@ class CourseOutlineSubsection(CourseOutlineChild, CourseOutlineContainer): """ url = None - BODY_SELECTOR = '.outline-item-subsection' + BODY_SELECTOR = '.outline-subsection' + NAME_SELECTOR = '.subsection-title' + NAME_FIELD_WRAPPER_SELECTOR = '.subsection-header .wrapper-xblock-field' CHILD_CLASS = CourseOutlineUnit def unit(self, title): @@ -224,7 +226,9 @@ class CourseOutlineSection(CourseOutlineChild, CourseOutlineContainer): :class`.PageObject` that wraps a section block on the Studio Course Outline page. """ url = None - BODY_SELECTOR = '.outline-item-section' + BODY_SELECTOR = '.outline-section' + NAME_SELECTOR = '.section-title' + NAME_FIELD_WRAPPER_SELECTOR = '.section-header .wrapper-xblock-field' CHILD_CLASS = CourseOutlineSubsection def subsection(self, title): @@ -268,7 +272,7 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer): url_path = "course" CHILD_CLASS = CourseOutlineSection EXPAND_COLLAPSE_CSS = '.toggle-button-expand-collapse' - BOTTOM_ADD_SECTION_BUTTON = '.course-outline > .add-xblock-component .add-button' + BOTTOM_ADD_SECTION_BUTTON = '.outline > .add-section .button-new' def is_browser_on_page(self): return self.q(css='body.view-outline').present @@ -337,7 +341,7 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer): """ Clicks the button for adding a section which resides at the top of the screen. """ - click_css(self, '.wrapper-mast nav.nav-actions .add-button') + click_css(self, '.wrapper-mast nav.nav-actions .button-new') def add_section_from_bottom_button(self): """