Commit ef581e11 by Andy Armstrong Committed by cahrens

Apply code review comments and fix tests

parent 9b986049
...@@ -9,7 +9,7 @@ from django.test.client import Client ...@@ -9,7 +9,7 @@ from django.test.client import Client
from django.contrib.auth.models import User from django.contrib.auth.models import User
from xmodule.contentstore.django import contentstore from xmodule.contentstore.django import contentstore
from xmodule.modulestore import LegacyPublishState, ModuleStoreEnum, mongo from xmodule.modulestore import PublishState, ModuleStoreEnum, mongo
from xmodule.modulestore.inheritance import own_metadata from xmodule.modulestore.inheritance import own_metadata
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
...@@ -151,16 +151,16 @@ class CourseTestCase(ModuleStoreTestCase): ...@@ -151,16 +151,16 @@ class CourseTestCase(ModuleStoreTestCase):
# create a Draft vertical # create a Draft vertical
vertical = self.store.get_item(course_id.make_usage_key('vertical', self.TEST_VERTICAL), depth=1) vertical = self.store.get_item(course_id.make_usage_key('vertical', self.TEST_VERTICAL), depth=1)
draft_vertical = self.store.convert_to_draft(vertical.location, self.user.id) draft_vertical = self.store.convert_to_draft(vertical.location, self.user.id)
self.assertEqual(self.store.compute_publish_state(draft_vertical), LegacyPublishState.draft) self.assertEqual(self.store.compute_publish_state(draft_vertical), PublishState.draft)
# create a Private (draft only) vertical # create a Private (draft only) vertical
private_vertical = self.store.create_item(self.user.id, course_id, 'vertical', self.PRIVATE_VERTICAL) private_vertical = self.store.create_item(self.user.id, course_id, 'vertical', self.PRIVATE_VERTICAL)
self.assertEqual(self.store.compute_publish_state(private_vertical), LegacyPublishState.private) self.assertEqual(self.store.compute_publish_state(private_vertical), PublishState.private)
# create a Published (no draft) vertical # create a Published (no draft) vertical
public_vertical = self.store.create_item(self.user.id, course_id, 'vertical', self.PUBLISHED_VERTICAL) public_vertical = self.store.create_item(self.user.id, course_id, 'vertical', self.PUBLISHED_VERTICAL)
public_vertical = self.store.publish(public_vertical.location, self.user.id) public_vertical = self.store.publish(public_vertical.location, self.user.id)
self.assertEqual(self.store.compute_publish_state(public_vertical), LegacyPublishState.public) self.assertEqual(self.store.compute_publish_state(public_vertical), PublishState.public)
# add the new private and new public as children of the sequential # add the new private and new public as children of the sequential
sequential = self.store.get_item(course_id.make_usage_key('sequential', self.SEQUENTIAL)) sequential = self.store.get_item(course_id.make_usage_key('sequential', self.SEQUENTIAL))
...@@ -197,7 +197,7 @@ class CourseTestCase(ModuleStoreTestCase): ...@@ -197,7 +197,7 @@ class CourseTestCase(ModuleStoreTestCase):
def verify_item_publish_state(item, publish_state): def verify_item_publish_state(item, publish_state):
"""Verifies the publish state of the item is as expected.""" """Verifies the publish state of the item is as expected."""
if publish_state in (LegacyPublishState.private, LegacyPublishState.draft): if publish_state in (PublishState.private, PublishState.draft):
self.assertTrue(getattr(item, 'is_draft', False)) self.assertTrue(getattr(item, 'is_draft', False))
else: else:
self.assertFalse(getattr(item, 'is_draft', False)) self.assertFalse(getattr(item, 'is_draft', False))
...@@ -210,18 +210,18 @@ class CourseTestCase(ModuleStoreTestCase): ...@@ -210,18 +210,18 @@ class CourseTestCase(ModuleStoreTestCase):
return item return item
# verify that the draft vertical is draft # verify that the draft vertical is draft
vertical = get_and_verify_publish_state('vertical', self.TEST_VERTICAL, LegacyPublishState.draft) vertical = get_and_verify_publish_state('vertical', self.TEST_VERTICAL, PublishState.draft)
for child in vertical.get_children(): for child in vertical.get_children():
verify_item_publish_state(child, LegacyPublishState.draft) verify_item_publish_state(child, PublishState.draft)
# make sure that we don't have a sequential that is not in draft mode # make sure that we don't have a sequential that is not in draft mode
sequential = get_and_verify_publish_state('sequential', self.SEQUENTIAL, LegacyPublishState.public) sequential = get_and_verify_publish_state('sequential', self.SEQUENTIAL, PublishState.public)
# verify that we have the private vertical # verify that we have the private vertical
private_vertical = get_and_verify_publish_state('vertical', self.PRIVATE_VERTICAL, LegacyPublishState.private) private_vertical = get_and_verify_publish_state('vertical', self.PRIVATE_VERTICAL, PublishState.private)
# verify that we have the public vertical # verify that we have the public vertical
public_vertical = get_and_verify_publish_state('vertical', self.PUBLISHED_VERTICAL, LegacyPublishState.public) public_vertical = get_and_verify_publish_state('vertical', self.PUBLISHED_VERTICAL, PublishState.public)
# verify verticals are children of sequential # verify verticals are children of sequential
for vert in [vertical, private_vertical, public_vertical]: for vert in [vertical, private_vertical, public_vertical]:
...@@ -332,7 +332,7 @@ class CourseTestCase(ModuleStoreTestCase): ...@@ -332,7 +332,7 @@ class CourseTestCase(ModuleStoreTestCase):
it'll return public in that case it'll return public in that case
""" """
supposed_state = self.store.compute_publish_state(item) supposed_state = self.store.compute_publish_state(item)
if supposed_state == LegacyPublishState.draft and isinstance(item.runtime.modulestore, DraftModuleStore): if supposed_state == PublishState.draft and isinstance(item.runtime.modulestore, DraftModuleStore):
# see if the draft differs from the published # see if the draft differs from the published
published = self.store.get_item(item.location, revision=ModuleStoreEnum.RevisionOption.published_only) published = self.store.get_item(item.location, revision=ModuleStoreEnum.RevisionOption.published_only)
if item.get_explicitly_set_fields_by_scope() != published.get_explicitly_set_fields_by_scope(): if item.get_explicitly_set_fields_by_scope() != published.get_explicitly_set_fields_by_scope():
...@@ -345,13 +345,13 @@ class CourseTestCase(ModuleStoreTestCase): ...@@ -345,13 +345,13 @@ class CourseTestCase(ModuleStoreTestCase):
# checking children: if published differs from item, return draft # checking children: if published differs from item, return draft
return supposed_state return supposed_state
# published == item in all respects, so return public # published == item in all respects, so return public
return LegacyPublishState.public return PublishState.public
elif supposed_state == LegacyPublishState.public and item.location.category in DIRECT_ONLY_CATEGORIES: elif supposed_state == PublishState.public and item.location.category in DIRECT_ONLY_CATEGORIES:
if not all([ if not all([
self.store.has_item(child_loc, revision=ModuleStoreEnum.RevisionOption.draft_only) self.store.has_item(child_loc, revision=ModuleStoreEnum.RevisionOption.draft_only)
for child_loc in item.children for child_loc in item.children
]): ]):
return LegacyPublishState.draft return PublishState.draft
else: else:
return supposed_state return supposed_state
else: else:
......
...@@ -150,20 +150,6 @@ def course_image_url(course): ...@@ -150,20 +150,6 @@ def course_image_url(course):
return path return path
def compute_publish_state(xblock):
"""
Returns whether this xblock is draft, public, or private.
Returns:
LegacyPublishState.draft - content is in the process of being edited, but still has a previous
version deployed to LMS
LegacyPublishState.public - content is locked and deployed to LMS
LegacyPublishState.private - content is editable and not deployed to LMS
"""
return modulestore().compute_publish_state(xblock)
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.
......
...@@ -11,8 +11,8 @@ from django.conf import settings ...@@ -11,8 +11,8 @@ from django.conf import settings
from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.modulestore.exceptions import ItemNotFoundError
from edxmako.shortcuts import render_to_response from edxmako.shortcuts import render_to_response
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from xmodule.modulestore import LegacyPublishState
from xblock.core import XBlock from xblock.core import XBlock
from xblock.django.request import webob_to_django_response, django_to_webob_request from xblock.django.request import webob_to_django_response, django_to_webob_request
...@@ -21,7 +21,7 @@ from xblock.fields import Scope ...@@ -21,7 +21,7 @@ from xblock.fields import Scope
from xblock.plugin import PluginMissingError from xblock.plugin import PluginMissingError
from xblock.runtime import Mixologist from xblock.runtime import Mixologist
from contentstore.utils import get_lms_link_for_item, compute_publish_state from contentstore.utils import get_lms_link_for_item
from contentstore.views.helpers import get_parent_xblock, is_unit, xblock_type_display_name from contentstore.views.helpers import get_parent_xblock, is_unit, xblock_type_display_name
from contentstore.views.item import create_xblock_info from contentstore.views.item import create_xblock_info
...@@ -100,8 +100,8 @@ def subsection_handler(request, usage_key_string): ...@@ -100,8 +100,8 @@ def subsection_handler(request, usage_key_string):
can_view_live = False can_view_live = False
subsection_units = item.get_children() subsection_units = item.get_children()
for unit in subsection_units: for unit in subsection_units:
state = compute_publish_state(unit) has_published = modulestore().has_item(unit.location, revision=ModuleStoreEnum.RevisionOption.published_only)
if state in (LegacyPublishState.public, LegacyPublishState.draft): if has_published:
can_view_live = True can_view_live = True
break break
...@@ -178,6 +178,10 @@ def container_handler(request, usage_key_string): ...@@ -178,6 +178,10 @@ 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
......
...@@ -636,9 +636,9 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F ...@@ -636,9 +636,9 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
"id": unicode(xblock.location), "id": unicode(xblock.location),
"display_name": xblock.display_name_with_default, "display_name": xblock.display_name_with_default,
"category": xblock.category, "category": xblock.category,
"published": published,
"edited_on": get_default_time_display(xblock.subtree_edited_on) if xblock.subtree_edited_on else None, "edited_on": get_default_time_display(xblock.subtree_edited_on) if xblock.subtree_edited_on else None,
"edited_by": safe_get_username(xblock.subtree_edited_by), "edited_by": safe_get_username(xblock.subtree_edited_by),
"published": published,
"published_on": get_default_time_display(xblock.published_date) if xblock.published_date else None, "published_on": get_default_time_display(xblock.published_date) if xblock.published_date else None,
"published_by": safe_get_username(xblock.published_by), "published_by": safe_get_username(xblock.published_by),
'studio_url': xblock_studio_url(xblock), 'studio_url': xblock_studio_url(xblock),
...@@ -646,7 +646,7 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F ...@@ -646,7 +646,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,
"publish_state": _compute_publish_state(xblock, child_info) if not xblock.category == 'course' else None "visibility_state": _compute_visibility_state(xblock, child_info) 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
...@@ -659,63 +659,71 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F ...@@ -659,63 +659,71 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
return xblock_info return xblock_info
class PublishState(object): class VisibilityState(object):
""" """
Represents the possible publish states for an xblock: Represents the possible visibility states for an xblock:
live - the block and all of its children are live to students (except for staff only items)
ready - the block and all of its children are ready to go live in the future live - the block and all of its descendants are live to students (excluding staff only items)
unscheduled - the block and all of its children are unscheduled Note: Live means both published and released.
has_unpublished_content - the block or its children have unpublished content that is not staff only
ready - the block is ready to go live and all of its descendants are live or ready (excluding staff only items)
Note: content is ready when it is published and scheduled with a release date in the future.
unscheduled - the block and all of its descendants have no release date (excluding staff only items)
Note: it is valid for items to be published with no release date in which case they are still unscheduled.
needs_attention - the block or its descendants are not fully live, ready or unscheduled (excluding staff only items)
For example: one subsection has draft content, or there's both unreleased and released content in one section.
staff_only - all of the block's content is to be shown to staff only staff_only - all of the block's content is to be shown to staff only
Note: staff only items do not affect their parent's state.
""" """
live = 'live' live = 'live'
ready = 'ready' ready = 'ready'
unscheduled = 'unscheduled' unscheduled = 'unscheduled' # unscheduled
has_unpublished_content = 'has_unpublished_content' needs_attention = 'needs_attention'
staff_only = 'staff_only' staff_only = 'staff_only'
def _compute_publish_state(xblock, child_info): def _compute_visibility_state(xblock, child_info):
""" """
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 PublishState.staff_only return VisibilityState.staff_only
elif is_unit(xblock) and modulestore().has_changes(xblock.location): elif is_unit(xblock) and modulestore().has_changes(xblock.location):
return PublishState.has_unpublished_content 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
children = child_info and child_info['children'] children = child_info and child_info['children']
if children and len(children) > 0: if children and len(children) > 0:
all_staff_only = True all_staff_only = True
all_unscheduled = True all_unscheduled = True
all_live = True all_live = True
for child in child_info['children']: for child in child_info['children']:
child_state = child['publish_state'] child_state = child['visibility_state']
if child_state == PublishState.has_unpublished_content: if child_state == VisibilityState.needs_attention:
return child_state return child_state
elif not child_state == PublishState.staff_only: elif not child_state == VisibilityState.staff_only:
all_staff_only = False all_staff_only = False
if not child_state == PublishState.unscheduled: if not child_state == VisibilityState.unscheduled:
all_unscheduled = False all_unscheduled = False
if not child_state == PublishState.live: if not child_state == VisibilityState.live:
all_live = False all_live = False
if all_staff_only: if all_staff_only:
return PublishState.staff_only return VisibilityState.staff_only
elif all_unscheduled: elif all_unscheduled:
if not is_unscheduled: return VisibilityState.unscheduled if is_unscheduled else VisibilityState.needs_attention
return PublishState.has_unpublished_content
else:
return PublishState.unscheduled
elif all_live: elif all_live:
return PublishState.live return VisibilityState.live if is_live else VisibilityState.needs_attention
else: else:
return PublishState.ready return VisibilityState.ready if not is_unscheduled else VisibilityState.needs_attention
if is_unscheduled: if is_unscheduled:
return PublishState.unscheduled return VisibilityState.unscheduled
elif datetime.now(UTC) > xblock.start: elif is_live:
return PublishState.live return VisibilityState.live
else: else:
return PublishState.ready return VisibilityState.ready
def _create_xblock_ancestor_info(xblock): def _create_xblock_ancestor_info(xblock):
......
...@@ -8,6 +8,7 @@ from contentstore.tests.utils import CourseTestCase ...@@ -8,6 +8,7 @@ from contentstore.tests.utils import CourseTestCase
from contentstore.utils import reverse_course_url, add_instructor from contentstore.utils import reverse_course_url, add_instructor
from contentstore.views.access import has_course_access from contentstore.views.access import has_course_access
from contentstore.views.course import course_outline_initial_state from contentstore.views.course import course_outline_initial_state
from contentstore.views.item import create_xblock_info, VisibilityState
from course_action_state.models import CourseRerunState from course_action_state.models import CourseRerunState
from contentstore.views.item import create_xblock_info from contentstore.views.item import create_xblock_info
from contentstore.views.item import create_xblock_info, PublishState from contentstore.views.item import create_xblock_info, PublishState
...@@ -230,7 +231,7 @@ class TestCourseOutline(CourseTestCase): ...@@ -230,7 +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.assertIsNone(json_response['publish_state']) self.assertIsNone(json_response['visibility_state'])
# Now verify the first child # Now verify the first child
children = json_response['child_info']['children'] children = json_response['child_info']['children']
...@@ -239,7 +240,7 @@ class TestCourseOutline(CourseTestCase): ...@@ -239,7 +240,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.assertEqual(first_child_response['publish_state'], PublishState.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)
# Finally, validate the entire response for consistency # Finally, validate the entire response for consistency
......
...@@ -45,14 +45,15 @@ define(["backbone", "underscore", "js/utils/module"], function(Backbone, _, Modu ...@@ -45,14 +45,15 @@ define(["backbone", "underscore", "js/utils/module"], function(Backbone, _, Modu
*/ */
"published_by": null, "published_by": null,
/** /**
* Represents the possible publish states for an xblock: * True if the xblock has changes.
* is_live - the block and all of its children are live to students (except for staff only items) * Note: this is not always provided as a performance optimization.
* is_ready - the block and all of its children are ready to go live in the future
* unscheduled - the block and all of its children are unscheduled
* has_unpublished_content - the block or its children have unpublished content that is not staff only
* is_staff_only - all of the block's content is to be shown to staff only
*/ */
"publish_state": null, "has_changes": null,
/**
* Represents the possible publish states for an xblock. See the documentation
* for XBlockVisibility to see a comprehensive enumeration of the states.
*/
"visibility_state": null,
/** /**
* True iff the release date of the xblock is in the past. * True iff the release date of the xblock is in the past.
*/ */
......
...@@ -118,7 +118,7 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers" ...@@ -118,7 +118,7 @@ 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,
publish_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'
}]) }])
...@@ -432,9 +432,5 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers" ...@@ -432,9 +432,5 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers"
expect(unitAnchor.attr('href')).toBe('/container/mock-unit'); expect(unitAnchor.attr('href')).toBe('/container/mock-unit');
}); });
}); });
describe("Publishing State", function() {
// TODO: implement this!!!!
});
}); });
}); });
...@@ -25,14 +25,14 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers" ...@@ -25,14 +25,14 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers"
category: 'vertical', category: 'vertical',
display_name: displayName, display_name: displayName,
studio_url: '/container/mock-unit', studio_url: '/container/mock-unit',
publish_state: 'unscheduled', visibility_state: 'unscheduled',
ancestor_info: { ancestor_info: {
ancestors: [{ ancestors: [{
id: 'mock-subsection', id: 'mock-subsection',
category: 'sequential', category: 'sequential',
display_name: 'Mock Subsection', display_name: 'Mock Subsection',
studio_url: '/course/mock-course?show=mock-subsection', studio_url: '/course/mock-course?show=mock-subsection',
publish_state: 'unscheduled', visibility_state: 'unscheduled',
child_info: { child_info: {
category: 'vertical', category: 'vertical',
display_name: 'Unit', display_name: 'Unit',
...@@ -41,13 +41,13 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers" ...@@ -41,13 +41,13 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers"
category: 'vertical', category: 'vertical',
display_name: displayName, display_name: displayName,
studio_url: '/container/mock-unit', studio_url: '/container/mock-unit',
publish_state: 'unscheduled' visibility_state: 'unscheduled'
}, { }, {
id: 'mock-unit-2', id: 'mock-unit-2',
category: 'vertical', category: 'vertical',
display_name: 'Mock Unit 2', display_name: 'Mock Unit 2',
studio_url: '/container/mock-unit-2', studio_url: '/container/mock-unit-2',
publish_state: 'unscheduled' visibility_state: 'unscheduled'
}] }]
} }
}, { }, {
...@@ -55,13 +55,13 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers" ...@@ -55,13 +55,13 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers"
category: 'chapter', category: 'chapter',
display_name: 'Section', display_name: 'Section',
studio_url: '/course/slashes:mock-course?show=mock-section', studio_url: '/course/slashes:mock-course?show=mock-section',
publish_state: 'unscheduled' visibility_state: 'unscheduled'
}, { }, {
id: 'mock-course', id: 'mock-course',
category: 'course', category: 'course',
display_name: 'Mock Course', display_name: 'Mock Course',
studio_url: '/course/mock-course', studio_url: '/course/mock-course',
publish_state: 'unscheduled' visibility_state: 'unscheduled'
}] }]
}, },
metadata: { metadata: {
......
...@@ -17,6 +17,9 @@ define(["jquery", "underscore", "backbone", "gettext", "js/utils/handle_iframe_b ...@@ -17,6 +17,9 @@ define(["jquery", "underscore", "backbone", "gettext", "js/utils/handle_iframe_b
}, },
options: { options: {
// UX is moving towards using 'is-collapsed' in preference over 'collapsed',
// but use the old scheme as the default so that existing code doesn't need
// to be rewritten.
collapsedClass: 'collapsed' collapsedClass: 'collapsed'
}, },
......
/** /**
* Subviews (usually small side panels) for XBlockContainerPage. * Subviews (usually small side panels) for XBlockContainerPage.
*/ */
define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/view_utils"], define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/view_utils",
function ($, _, gettext, BaseView, ViewUtils) { "js/views/utils/xblock_utils"],
function ($, _, gettext, BaseView, ViewUtils, XBlockViewUtils) {
var disabledCss = "is-disabled"; var VisibilityState = XBlockViewUtils.VisibilityState,
disabledCss = "is-disabled";
/** /**
* A view that refreshes the view when certain values in the XBlockInfo have changed * A view that refreshes the view when certain values in the XBlockInfo have changed
...@@ -53,20 +54,19 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/ ...@@ -53,20 +54,19 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
*/ */
var PreviewActionController = ContainerStateListenerView.extend({ var PreviewActionController = ContainerStateListenerView.extend({
shouldRefresh: function(model) { shouldRefresh: function(model) {
return ViewUtils.hasChangedAttributes(model, ['edited_on', 'published_on', 'publish_state']); return ViewUtils.hasChangedAttributes(model, ['has_changes', 'published']);
}, },
render: function() { render: function() {
var previewAction = this.$el.find('.button-preview'), var previewAction = this.$el.find('.button-preview'),
viewLiveAction = this.$el.find('.button-view'), viewLiveAction = this.$el.find('.button-view');
publishState = this.model.get('publish_state'); if (this.model.get('published')) {
if (publishState !== 'unscheduled') {
viewLiveAction.removeClass(disabledCss); viewLiveAction.removeClass(disabledCss);
} }
else { else {
viewLiveAction.addClass(disabledCss); viewLiveAction.addClass(disabledCss);
} }
if (publishState !== 'live' && publishState !== 'ready') { if (this.model.get('has_changes') || !this.model.get('published')) {
previewAction.removeClass(disabledCss); previewAction.removeClass(disabledCss);
} }
else { else {
...@@ -99,14 +99,18 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/ ...@@ -99,14 +99,18 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
}, },
onSync: function(model) { onSync: function(model) {
if (ViewUtils.hasChangedAttributes(model, [ 'edited_on', 'published_on', 'publish_state' ])) { if (ViewUtils.hasChangedAttributes(model, [
'has_changes', 'published', 'edited_on', 'edited_by', 'visibility_state'
])) {
this.render(); this.render();
} }
}, },
render: function () { render: function () {
this.$el.html(this.template({ this.$el.html(this.template({
publishState: this.model.get('publish_state'), visibilityState: this.model.get('visibility_state'),
visibilityClass: XBlockViewUtils.getXBlockVisibilityClass(this.model.get('visibility_state')),
hasChanges: this.model.get('has_changes'),
editedOn: this.model.get('edited_on'), editedOn: this.model.get('edited_on'),
editedBy: this.model.get('edited_by'), editedBy: this.model.get('edited_by'),
published: this.model.get('published'), published: this.model.get('published'),
...@@ -162,7 +166,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/ ...@@ -162,7 +166,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
if (e && e.preventDefault) { if (e && e.preventDefault) {
e.preventDefault(); e.preventDefault();
} }
enableStaffLock = xblockInfo.get('publish_state') !== 'staff_only'; enableStaffLock = xblockInfo.get('visibility_state') !== VisibilityState.staffOnly;
revertCheckBox = function() { revertCheckBox = function() {
self.checkStaffLock(!enableStaffLock); self.checkStaffLock(!enableStaffLock);
...@@ -221,7 +225,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/ ...@@ -221,7 +225,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
}, },
onSync: function(model) { onSync: function(model) {
if (ViewUtils.hasChangedAttributes(model, ['published_on'])) { if (ViewUtils.hasChangedAttributes(model, ['published', 'published_on', 'published_by'])) {
this.render(); this.render();
} }
}, },
......
...@@ -10,7 +10,6 @@ define(['js/views/xblock_outline'], ...@@ -10,7 +10,6 @@ define(['js/views/xblock_outline'],
// takes XBlockInfo as a model // takes XBlockInfo as a model
templateName: 'unit-outline', templateName: 'unit-outline',
className: 'group-configurations-list',
render: function() { render: function() {
XBlockOutlineView.prototype.render.call(this); XBlockOutlineView.prototype.render.call(this);
......
...@@ -3,7 +3,36 @@ ...@@ -3,7 +3,36 @@
*/ */
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; var addXBlock, deleteXBlock, createUpdateRequestData, updateXBlockField, VisibilityState,
getXBlockVisibilityClass;
/**
* Represents the possible visibility states for an xblock:
*
* live - the block and all of its descendants are live to students (excluding staff only)
* Note: Live means both published and released.
*
* ready - the block is ready to go live and all of its descendants are live or ready (excluding staff only)
* Note: content is ready when it is published and scheduled with a release date in the future.
*
* unscheduled - the block and all of its descendants have no release date (excluding staff only)
* Note: it is valid for items to be published with no release date in which case they are unscheduled.
*
* needsAttention - the block or its descendants need attention
* i.e. there is some content that is not fully live, ready, unscheduled or staff only.
* For example: one subsection has draft content, or there's both unreleased and released content
* in one section.
*
* staffOnly - all of the block's content is to be shown to staff only
* Note: staff only items do not affect their parent's state.
*/
VisibilityState = {
live: 'live',
ready: 'ready',
unscheduled: 'unscheduled',
needsAttention: 'needs_attention',
staffOnly: 'staff_only'
};
/** /**
* Adds an xblock based upon the data attributes of the specified add button. A promise * Adds an xblock based upon the data attributes of the specified add button. A promise
...@@ -83,9 +112,30 @@ define(["jquery", "underscore", "gettext", "js/views/utils/view_utils", "js/util ...@@ -83,9 +112,30 @@ define(["jquery", "underscore", "gettext", "js/views/utils/view_utils", "js/util
}); });
}; };
/**
* Returns the CSS class to represent the specified xblock visibility state.
*/
getXBlockVisibilityClass = function(visibilityState) {
if (visibilityState === VisibilityState.staffOnly) {
return 'is-staff-only';
}
if (visibilityState === VisibilityState.live) {
return 'is-live';
}
if (visibilityState === VisibilityState.ready) {
return 'is-ready';
}
if (visibilityState === VisibilityState.needsAttention) {
return 'has-warnings';
}
return '';
};
return { return {
'VisibilityState': VisibilityState,
'addXBlock': addXBlock, 'addXBlock': addXBlock,
'deleteXBlock': deleteXBlock, 'deleteXBlock': deleteXBlock,
'updateXBlockField': updateXBlockField 'updateXBlockField': updateXBlockField,
'getXBlockVisibilityClass': getXBlockVisibilityClass
}; };
}); });
...@@ -68,6 +68,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/ ...@@ -68,6 +68,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')),
parentInfo: this.parentInfo, parentInfo: this.parentInfo,
xblockType: xblockType, xblockType: xblockType,
parentType: parentType, parentType: parentType,
...@@ -200,7 +201,11 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/ ...@@ -200,7 +201,11 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
} else { } else {
locatorElement = this.$('.outline-item[data-locator="' + locatorToShow + '"]'); locatorElement = this.$('.outline-item[data-locator="' + locatorToShow + '"]');
} }
if (locatorElement.length > 0) {
ViewUtils.setScrollOffset(locatorElement, scrollOffset); ViewUtils.setScrollOffset(locatorElement, scrollOffset);
} else {
console.error("Failed to show item with locator " + locatorToShow + "");
}
if (editDisplayName) { if (editDisplayName) {
locatorElement.find('> div[class$="header"] .xblock-field-value-edit').click(); locatorElement.find('> div[class$="header"] .xblock-field-value-edit').click();
} }
......
...@@ -72,7 +72,7 @@ from contentstore.utils import reverse_usage_url ...@@ -72,7 +72,7 @@ from contentstore.utils import reverse_usage_url
<% <%
course_locator = context_course.location course_locator = context_course.location
%> %>
<article class="outline" data-locator="${course_locator}" data-course-key="${course_locator.course_key}"> <article class="outline outline-course" data-locator="${course_locator}" data-course-key="${course_locator.course_key}">
</article> </article>
</div> </div>
<div class="ui-loading"> <div class="ui-loading">
......
<% <%
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 publishState = xblockInfo.get('publish_state'); var visibilityState = xblockInfo.get('visibility_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-unknown'; var listType = 'list-unknown';
if (xblockType === 'course') { if (xblockType === 'course') {
...@@ -25,10 +14,10 @@ if (xblockType === 'course') { ...@@ -25,10 +14,10 @@ if (xblockType === 'course') {
var statusMessage = null; var statusMessage = null;
var statusType = null; var statusType = null;
if (publishState === 'staff_only') { if (visibilityState === 'staff_only') {
statusType = 'staff-only'; statusType = 'staff-only';
statusMessage = 'Contains staff only content'; statusMessage = 'Contains staff only content';
} else if (publishState === 'has_unpublished_content') { } else if (visibilityState === 'needs_attention') {
if (category === 'vertical') { if (category === 'vertical') {
statusType = 'warning'; statusType = 'warning';
if (releasedToStudents) { if (releasedToStudents) {
...@@ -49,7 +38,7 @@ if (statusType === 'warning') { ...@@ -49,7 +38,7 @@ if (statusType === 'warning') {
} }
%> %>
<% if (parentInfo) { %> <% if (parentInfo) { %>
<li class="outline-item outline-<%= xblockType %> <%= publishClass %> is-draggable <%= includesChildren ? 'is-collapsible' : '' %> <%= isCollapsed ? 'is-collapsed' : '' %>" <li class="outline-item outline-<%= xblockType %> <%= visibilityClass %> is-draggable <%= includesChildren ? 'is-collapsible' : '' %> <%= isCollapsed ? 'is-collapsed' : '' %>"
data-parent="<%= parentInfo.get('id') %>" data-locator="<%= xblockInfo.get('id') %>"> data-parent="<%= parentInfo.get('id') %>" data-locator="<%= xblockInfo.get('id') %>">
<div class="<%= xblockType %>-header"> <div class="<%= xblockType %>-header">
......
<% <%
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 is-draft';
}
var title = gettext("Draft (Never published)"); var title = gettext("Draft (Never published)");
if (publishState === 'staff_only') { if (visibilityState === 'staff_only') {
title = gettext("Unpublished (Staff only)"); title = gettext("Unpublished (Staff only)");
} else if (publishState === 'live') { } else if (visibilityState === 'live') {
title = gettext("Published and Live"); title = gettext("Published and Live");
} else if (publishState === 'ready') { } else if (visibilityState === 'ready') {
title = gettext("Published"); title = gettext("Published");
} else if (publishState === 'has_unpublished_content') { } else if (visibilityState === 'needs_attention') {
title = gettext("Draft (Unpublished changes)"); title = gettext("Draft (Unpublished changes)");
} }
var releaseLabel = gettext("Release:"); var releaseLabel = gettext("Release:");
if (publishState === 'live') { if (visibilityState === 'live') {
releaseLabel = gettext("Released:"); releaseLabel = gettext("Released:");
} else if (publishState === 'ready') { } else if (visibilityState === 'ready') {
releaseLabel = gettext("Scheduled:"); releaseLabel = gettext("Scheduled:");
} }
var canPublish = publishState !== 'ready' && publishState !== 'live'; var visibleToStaffOnly = visibilityState === 'staff_only';
var canDiscardChanges = publishState === 'has_unpublished_content';
var visibleToStaffOnly = publishState === 'staff_only';
%> %>
<div class="bit-publishing <%= publishClass %>"> <div class="bit-publishing <%= visibilityClass %>">
<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>
<div class="wrapper-last-draft bar-mod-content"> <div class="wrapper-last-draft bar-mod-content">
<p class="copy meta"> <p class="copy meta">
<% if (publishState === 'has_unpublished_content' && editedOn && editedBy) { <% if (hasChanges && editedOn && editedBy) {
var message = gettext("Draft saved on %(last_saved_date)s by %(edit_username)s") %> var message = gettext("Draft saved on %(last_saved_date)s by %(edit_username)s") %>
<%= interpolate(message, { <%= interpolate(message, {
last_saved_date: '<span class="date">' + editedOn + '</span>', last_saved_date: '<span class="date">' + editedOn + '</span>',
...@@ -91,12 +78,12 @@ var visibleToStaffOnly = publishState === 'staff_only'; ...@@ -91,12 +78,12 @@ var visibleToStaffOnly = publishState === 'staff_only';
<div class="wrapper-pub-actions bar-mod-actions"> <div class="wrapper-pub-actions bar-mod-actions">
<ul class="action-list"> <ul class="action-list">
<li class="action-item"> <li class="action-item">
<a class="action-publish action-primary <% if (!canPublish) { %>is-disabled<% } %>" <a class="action-publish action-primary <% if (published && !hasChanges) { %>is-disabled<% } %>"
href=""><%= gettext("Publish") %> href=""><%= gettext("Publish") %>
</a> </a>
</li> </li>
<li class="action-item"> <li class="action-item">
<a class="action-discard action-secondary <% if (!canDiscardChanges) { %>is-disabled<% } %>" <a class="action-discard action-secondary <% if (!published || !hasChanges) { %>is-disabled<% } %>"
href=""><%= gettext("Discard Changes") %> href=""><%= gettext("Discard Changes") %>
</a> </a>
</li> </li>
......
<% <%
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; var listType = 'list-for-' + xblockType;
if (xblockType === 'course') { if (xblockType === 'course') {
listType = 'list-sections'; listType = 'list-sections';
...@@ -21,7 +9,7 @@ if (xblockType === 'course') { ...@@ -21,7 +9,7 @@ if (xblockType === 'course') {
} }
%> %>
<% if (parentInfo) { %> <% if (parentInfo) { %>
<li class="outline-item outline-<%= xblockType %> <%= publishClass %>" <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') %>">
<div class="<%= xblockType %>-header"> <div class="<%= xblockType %>-header">
<h3 class="<%= xblockType %>-header-details"> <h3 class="<%= xblockType %>-header-details">
......
<%! from django.utils.translation import ugettext as _ %> <%! from django.utils.translation import ugettext as _ %>
<%! from contentstore.utils import compute_publish_state %>
<%! from contentstore.views.helpers import xblock_studio_url %> <%! from contentstore.views.helpers import xblock_studio_url %>
<!-- <!--
...@@ -18,7 +17,7 @@ This def will enumerate through a passed in subsection and list all of the units ...@@ -18,7 +17,7 @@ This def will enumerate through a passed in subsection and list all of the units
<%include file="_ui-dnd-indicator-before.html" /> <%include file="_ui-dnd-indicator-before.html" />
<% <%
unit_state = compute_publish_state(unit) unit_state = 'draft' # Note: this is a hack since this HTML file is being removed in bulk-publishing
if unit.location == selected: if unit.location == selected:
selected_class = 'editing' selected_class = 'editing'
else: else:
......
...@@ -90,10 +90,10 @@ class ModuleStoreEnum(object): ...@@ -90,10 +90,10 @@ class ModuleStoreEnum(object):
test = -3 test = -3
class LegacyPublishState(object): class PublishState(object):
""" """
The legacy publish state for a given xblock-- either 'draft', 'private', or 'public'. These states 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. are no longer used in Studio, but they are still referenced in a few places in LMS.
""" """
draft = 'draft' draft = 'draft'
private = 'private' private = 'private'
...@@ -301,10 +301,10 @@ class ModuleStoreRead(object): ...@@ -301,10 +301,10 @@ class ModuleStoreRead(object):
Returns whether this xblock is draft, public, or private. Returns whether this xblock is draft, public, or private.
Returns: Returns:
LegacyPublishState.draft - content is in the process of being edited, but still has a previous PublishState.draft - content is in the process of being edited, but still has a previous
version deployed to LMS version deployed to LMS
LegacyPublishState.public - content is locked and deployed to LMS PublishState.public - content is locked and deployed to LMS
LegacyPublishState.private - content is editable and not deployed to LMS PublishState.private - content is editable and not deployed to LMS
""" """
pass pass
...@@ -522,7 +522,7 @@ class ModuleStoreReadBase(ModuleStoreRead): ...@@ -522,7 +522,7 @@ class ModuleStoreReadBase(ModuleStoreRead):
""" """
Returns PublishState.public since this is a read-only store. Returns PublishState.public since this is a read-only store.
""" """
return LegacyPublishState.public return PublishState.public
def heartbeat(self): def heartbeat(self):
""" """
......
...@@ -439,10 +439,10 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): ...@@ -439,10 +439,10 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase):
Returns whether this xblock is draft, public, or private. Returns whether this xblock is draft, public, or private.
Returns: Returns:
LegacyPublishState.draft - content is in the process of being edited, but still has a previous PublishState.draft - content is in the process of being edited, but still has a previous
version deployed to LMS version deployed to LMS
LegacyPublishState.public - content is locked and deployed to LMS PublishState.public - content is locked and deployed to LMS
LegacyPublishState.private - content is editable and not deployed to LMS PublishState.private - content is editable and not deployed to LMS
""" """
course_id = xblock.scope_ids.usage_id.course_key course_id = xblock.scope_ids.usage_id.course_key
store = self._get_modulestore_for_courseid(course_id) store = self._get_modulestore_for_courseid(course_id)
......
...@@ -11,7 +11,7 @@ import logging ...@@ -11,7 +11,7 @@ import logging
from opaque_keys.edx.locations import Location from opaque_keys.edx.locations import Location
from xmodule.exceptions import InvalidVersionError from xmodule.exceptions import InvalidVersionError
from xmodule.modulestore import LegacyPublishState, ModuleStoreEnum from xmodule.modulestore import PublishState, ModuleStoreEnum
from xmodule.modulestore.exceptions import ( from xmodule.modulestore.exceptions import (
ItemNotFoundError, DuplicateItemError, InvalidBranchSetting, DuplicateCourseError ItemNotFoundError, DuplicateItemError, InvalidBranchSetting, DuplicateCourseError
) )
...@@ -613,7 +613,7 @@ class DraftModuleStore(MongoModuleStore): ...@@ -613,7 +613,7 @@ class DraftModuleStore(MongoModuleStore):
return False return False
# don't check children if this block has changes (is not public) # don't check children if this block has changes (is not public)
if self.compute_publish_state(item) != LegacyPublishState.public: if self.compute_publish_state(item) != PublishState.public:
return True return True
# if this block doesn't have changes, then check its children # if this block doesn't have changes, then check its children
elif item.has_children: elif item.has_children:
...@@ -792,10 +792,10 @@ class DraftModuleStore(MongoModuleStore): ...@@ -792,10 +792,10 @@ class DraftModuleStore(MongoModuleStore):
Returns whether this xblock is draft, public, or private. Returns whether this xblock is draft, public, or private.
Returns: Returns:
LegacyPublishState.draft - content is in the process of being edited, but still has a previous PublishState.draft - content is in the process of being edited, but still has a previous
version deployed to LMS version deployed to LMS
LegacyPublishState.public - content is locked and deployed to LMS PublishState.public - content is locked and deployed to LMS
LegacyPublishState.private - content is editable and not deployed to LMS PublishState.private - content is editable and not deployed to LMS
""" """
if getattr(xblock, 'is_draft', False): if getattr(xblock, 'is_draft', False):
published_xblock_location = as_published(xblock.location) published_xblock_location = as_published(xblock.location)
...@@ -803,11 +803,11 @@ class DraftModuleStore(MongoModuleStore): ...@@ -803,11 +803,11 @@ class DraftModuleStore(MongoModuleStore):
{'_id': published_xblock_location.to_deprecated_son()} {'_id': published_xblock_location.to_deprecated_son()}
) )
if published_item is None: if published_item is None:
return LegacyPublishState.private return PublishState.private
else: else:
return LegacyPublishState.draft return PublishState.draft
else: else:
return LegacyPublishState.public return PublishState.public
def _verify_branch_setting(self, expected_branch_setting): def _verify_branch_setting(self, expected_branch_setting):
""" """
......
...@@ -4,7 +4,7 @@ Module for the dual-branch fall-back Draft->Published Versioning ModuleStore ...@@ -4,7 +4,7 @@ Module for the dual-branch fall-back Draft->Published Versioning ModuleStore
from ..exceptions import ItemNotFoundError from ..exceptions import ItemNotFoundError
from split import SplitMongoModuleStore, EXCLUDE_ALL from split import SplitMongoModuleStore, EXCLUDE_ALL
from xmodule.modulestore import ModuleStoreEnum, LegacyPublishState from xmodule.modulestore import ModuleStoreEnum, PublishState
from xmodule.modulestore.exceptions import InsufficientSpecificationError from xmodule.modulestore.exceptions import InsufficientSpecificationError
from xmodule.modulestore.draft_and_published import ModuleStoreDraftAndPublished, DIRECT_ONLY_CATEGORIES, UnsupportedRevisionError from xmodule.modulestore.draft_and_published import ModuleStoreDraftAndPublished, DIRECT_ONLY_CATEGORIES, UnsupportedRevisionError
...@@ -251,9 +251,9 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS ...@@ -251,9 +251,9 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
Returns whether this xblock is draft, public, or private. Returns whether this xblock is draft, public, or private.
Returns: Returns:
LegacyPublishState.draft - published exists and is different from draft PublishState.draft - published exists and is different from draft
LegacyPublishState.public - published exists and is the same as draft PublishState.public - published exists and is the same as draft
LegacyPublishState.private - no published version exists PublishState.private - no published version exists
""" """
# TODO figure out what to say if xblock is not from the HEAD of its branch # TODO figure out what to say if xblock is not from the HEAD of its branch
def get_head(branch): def get_head(branch):
...@@ -272,13 +272,13 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS ...@@ -272,13 +272,13 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
if not published_head: if not published_head:
# published version does not exist # published version does not exist
return LegacyPublishState.private return PublishState.private
elif get_version(draft_head) == get_version(published_head): elif get_version(draft_head) == get_version(published_head):
# published and draft versions are equal # published and draft versions are equal
return LegacyPublishState.public return PublishState.public
else: else:
# published and draft versions differ # published and draft versions differ
return LegacyPublishState.draft return PublishState.draft
def convert_to_draft(self, location, user_id): def convert_to_draft(self, location, user_id):
""" """
......
...@@ -9,7 +9,7 @@ from pytz import UTC ...@@ -9,7 +9,7 @@ from pytz import UTC
from xmodule.tests import DATA_DIR from xmodule.tests import DATA_DIR
from opaque_keys.edx.locations import Location from opaque_keys.edx.locations import Location
from xmodule.modulestore import ModuleStoreEnum, LegacyPublishState from xmodule.modulestore import ModuleStoreEnum, PublishState
from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.exceptions import InvalidVersionError from xmodule.exceptions import InvalidVersionError
...@@ -991,22 +991,22 @@ class TestMixedModuleStore(unittest.TestCase): ...@@ -991,22 +991,22 @@ class TestMixedModuleStore(unittest.TestCase):
item_location = item.location.version_agnostic() item_location = item.location.version_agnostic()
mongo_store = self.store._get_modulestore_for_courseid(self._course_key_from_string(self.MONGO_COURSEID)) 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): with check_mongo_calls(mongo_store, max_find, max_send):
self.assertEquals(self.store.compute_publish_state(item), LegacyPublishState.private) self.assertEquals(self.store.compute_publish_state(item), PublishState.private)
# Private -> Public # Private -> Public
self.store.publish(item_location, self.user_id) self.store.publish(item_location, self.user_id)
item = self.store.get_item(item_location) item = self.store.get_item(item_location)
self.assertEquals(self.store.compute_publish_state(item), LegacyPublishState.public) self.assertEquals(self.store.compute_publish_state(item), PublishState.public)
# Public -> Private # Public -> Private
self.store.unpublish(item_location, self.user_id) self.store.unpublish(item_location, self.user_id)
item = self.store.get_item(item_location) item = self.store.get_item(item_location)
self.assertEquals(self.store.compute_publish_state(item), LegacyPublishState.private) self.assertEquals(self.store.compute_publish_state(item), PublishState.private)
# Private -> Public # Private -> Public
self.store.publish(item_location, self.user_id) self.store.publish(item_location, self.user_id)
item = self.store.get_item(item_location) item = self.store.get_item(item_location)
self.assertEquals(self.store.compute_publish_state(item), LegacyPublishState.public) self.assertEquals(self.store.compute_publish_state(item), PublishState.public)
# Public -> Draft with NO changes # Public -> Draft with NO changes
# Note: This is where Split and Mongo differ # Note: This is where Split and Mongo differ
...@@ -1014,14 +1014,14 @@ class TestMixedModuleStore(unittest.TestCase): ...@@ -1014,14 +1014,14 @@ class TestMixedModuleStore(unittest.TestCase):
item = self.store.get_item(item_location) item = self.store.get_item(item_location)
self.assertEquals( self.assertEquals(
self.store.compute_publish_state(item), self.store.compute_publish_state(item),
LegacyPublishState.draft if default_ms == 'draft' else LegacyPublishState.public PublishState.draft if default_ms == 'draft' else PublishState.public
) )
# Draft WITH changes # Draft WITH changes
item.display_name = 'new name' item.display_name = 'new name'
item = self.store.update_item(item, self.user_id) item = self.store.update_item(item, self.user_id)
self.assertTrue(self.store.has_changes(item.location)) self.assertTrue(self.store.has_changes(item.location))
self.assertEquals(self.store.compute_publish_state(item), LegacyPublishState.draft) self.assertEquals(self.store.compute_publish_state(item), PublishState.draft)
@ddt.data('draft', 'split') @ddt.data('draft', 'split')
def test_auto_publish(self, default_ms): def test_auto_publish(self, default_ms):
......
from ..pages.studio.auto_auth import AutoAuthPage from ..pages.studio.auto_auth import AutoAuthPage
from ..fixtures.course import CourseFixture from ..fixtures.course import CourseFixture
from acceptance.tests.helpers import UniqueCourseTest from .helpers import UniqueCourseTest
class StudioCourseTest(UniqueCourseTest): class StudioCourseTest(UniqueCourseTest):
......
...@@ -25,7 +25,7 @@ from ..pages.studio.signup import SignupPage ...@@ -25,7 +25,7 @@ from ..pages.studio.signup import SignupPage
from ..pages.studio.textbooks import TextbooksPage from ..pages.studio.textbooks import TextbooksPage
from ..fixtures.course import XBlockFixtureDesc from ..fixtures.course import XBlockFixtureDesc
from acceptance.tests.base_studio_test import StudioCourseTest from .base_studio_test import StudioCourseTest
@attr('shard_1') @attr('shard_1')
......
...@@ -8,7 +8,7 @@ from ..pages.studio.overview import CourseOutlinePage, ContainerPage, ExpandColl ...@@ -8,7 +8,7 @@ from ..pages.studio.overview import CourseOutlinePage, ContainerPage, ExpandColl
from ..pages.lms.courseware import CoursewarePage from ..pages.lms.courseware import CoursewarePage
from ..fixtures.course import XBlockFixtureDesc from ..fixtures.course import XBlockFixtureDesc
from acceptance.tests.base_studio_test import StudioCourseTest from .base_studio_test import StudioCourseTest
class CourseOutlineTest(StudioCourseTest): class CourseOutlineTest(StudioCourseTest):
......
...@@ -20,7 +20,7 @@ from ..pages.studio.utils import add_advanced_component ...@@ -20,7 +20,7 @@ from ..pages.studio.utils import add_advanced_component
from ..pages.studio.unit import UnitPage from ..pages.studio.unit import UnitPage
from ..pages.xblock.utils import wait_for_xblock_initialization from ..pages.xblock.utils import wait_for_xblock_initialization
from acceptance.tests.base_studio_test import StudioCourseTest from .base_studio_test import StudioCourseTest
from test_studio_container import ContainerBase from test_studio_container import ContainerBase
......
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