Commit ad271073 by Andy Armstrong Committed by cahrens

Support making drafts of nested xblocks

parent a51c993f
...@@ -194,30 +194,30 @@ def course_image_url(course): ...@@ -194,30 +194,30 @@ def course_image_url(course):
return path return path
class UnitState(object): class PublishState(object):
draft = 'draft' draft = 'draft'
private = 'private' private = 'private'
public = 'public' public = 'public'
def compute_unit_state(unit): def compute_publish_state(xblock):
""" """
Returns whether this unit is 'draft', 'public', or 'private'. Returns whether this xblock is 'draft', 'public', or 'private'.
'draft' content is in the process of being edited, but still has a previous 'draft' content is in the process of being edited, but still has a previous
version visible in the LMS version visible in the LMS
'public' content is locked and visible in the LMS 'public' content is locked and visible in the LMS
'private' content is editabled and not visible in the LMS 'private' content is editable and not visible in the LMS
""" """
if getattr(unit, 'is_draft', False): if getattr(xblock, 'is_draft', False):
try: try:
modulestore('direct').get_item(unit.location) modulestore('direct').get_item(xblock.location)
return UnitState.draft return PublishState.draft
except ItemNotFoundError: except ItemNotFoundError:
return UnitState.private return PublishState.private
else: else:
return UnitState.public return PublishState.public
def add_extra_panel_tab(tab_type, course): def add_extra_panel_tab(tab_type, course):
......
...@@ -26,7 +26,7 @@ from xblock.runtime import Mixologist ...@@ -26,7 +26,7 @@ from xblock.runtime import Mixologist
from lms.lib.xblock.runtime import unquote_slashes from lms.lib.xblock.runtime import unquote_slashes
from contentstore.utils import get_lms_link_for_item, compute_unit_state, UnitState, get_modulestore from contentstore.utils import get_lms_link_for_item, compute_publish_state, PublishState, get_modulestore
from contentstore.views.helpers import get_parent_xblock from contentstore.views.helpers import get_parent_xblock
from models.settings.course_grading import CourseGradingModel from models.settings.course_grading import CourseGradingModel
...@@ -282,7 +282,7 @@ def unit_handler(request, tag=None, package_id=None, branch=None, version_guid=N ...@@ -282,7 +282,7 @@ def unit_handler(request, tag=None, package_id=None, branch=None, version_guid=N
), ),
'section': containing_section, 'section': containing_section,
'new_unit_category': 'vertical', 'new_unit_category': 'vertical',
'unit_state': compute_unit_state(item), 'unit_state': compute_publish_state(item),
'published_date': ( 'published_date': (
get_default_time_display(item.published_date) get_default_time_display(item.published_date)
if item.published_date is not None else None if item.published_date is not None else None
......
...@@ -97,5 +97,5 @@ def xblock_studio_url(xblock, course=None): ...@@ -97,5 +97,5 @@ def xblock_studio_url(xblock, course=None):
course_id = None course_id = None
if course: if course:
course_id = course.location.course_id course_id = course.location.course_id
locator = loc_mapper().translate_location(course_id, xblock.location) locator = loc_mapper().translate_location(course_id, xblock.location, published=False)
return locator.url_reverse(prefix) return locator.url_reverse(prefix)
...@@ -296,9 +296,9 @@ def _save_item(request, usage_loc, item_location, data=None, children=None, meta ...@@ -296,9 +296,9 @@ def _save_item(request, usage_loc, item_location, data=None, children=None, meta
if publish == 'make_private': if publish == 'make_private':
_xmodule_recurse(existing_item, lambda i: modulestore().unpublish(i.location)) _xmodule_recurse(existing_item, lambda i: modulestore().unpublish(i.location))
elif publish == 'create_draft': elif publish == 'create_draft':
# This clones the existing item location to a draft location (the draft is # This recursively clones the existing item location to a draft location (the draft is
# implicit, because modulestore is a Draft modulestore) # implicit, because modulestore is a Draft modulestore)
modulestore().convert_to_draft(item_location) _xmodule_recurse(existing_item, lambda i: modulestore().convert_to_draft(i.location))
if data: if data:
# TODO Allow any scope.content fields not just "data" (exactly like the get below this) # TODO Allow any scope.content fields not just "data" (exactly like the get below this)
......
...@@ -15,6 +15,7 @@ from django.test.client import RequestFactory ...@@ -15,6 +15,7 @@ from django.test.client import RequestFactory
from contentstore.views.component import component_handler from contentstore.views.component import component_handler
from contentstore.tests.utils import CourseTestCase from contentstore.tests.utils import CourseTestCase
from contentstore.utils import compute_publish_state, PublishState
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
from xmodule.capa_module import CapaDescriptor from xmodule.capa_module import CapaDescriptor
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
...@@ -647,6 +648,7 @@ class TestEditItem(ItemTest): ...@@ -647,6 +648,7 @@ class TestEditItem(ItemTest):
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
# Activate the editing view # Activate the editing view
view_url = '/xblock/{locator}/studio_view'.format(locator=self.problem_locator)
resp = self.client.get(view_url, HTTP_ACCEPT='application/json') resp = self.client.get(view_url, HTTP_ACCEPT='application/json')
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
...@@ -656,6 +658,50 @@ class TestEditItem(ItemTest): ...@@ -656,6 +658,50 @@ class TestEditItem(ItemTest):
self.assertNotEqual(draft.data, published.data) self.assertNotEqual(draft.data, published.data)
def test_publish_states_of_nested_xblocks(self):
""" Test publishing of a unit page containing a nested xblock """
resp = self.create_xblock(parent_locator=self.seq_locator, display_name='Test Unit', category='vertical')
unit_locator = self.response_locator(resp)
resp = self.create_xblock(parent_locator=unit_locator, category='wrapper')
wrapper_locator = self.response_locator(resp)
resp = self.create_xblock(parent_locator=wrapper_locator, category='html')
html_locator = self.response_locator(resp)
# The unit and its children should be private initially
unit_update_url = '/xblock/' + unit_locator
unit = self.get_item_from_modulestore(unit_locator, True)
html = self.get_item_from_modulestore(html_locator, True)
self.assertEqual(compute_publish_state(unit), PublishState.private)
self.assertEqual(compute_publish_state(html), PublishState.private)
# Make the unit public and verify that the problem is also made public
resp = self.client.ajax_post(
unit_update_url,
data={'publish': 'make_public'}
)
self.assertEqual(resp.status_code, 200)
unit = self.get_item_from_modulestore(unit_locator, True)
html = self.get_item_from_modulestore(html_locator, True)
self.assertEqual(compute_publish_state(unit), PublishState.public)
self.assertEqual(compute_publish_state(html), PublishState.public)
# Make a draft for the unit and verify that the problem also has a draft
resp = self.client.ajax_post(
unit_update_url,
data={
'id': unit_locator,
'metadata': {},
'publish': 'create_draft'
}
)
self.assertEqual(resp.status_code, 200)
unit = self.get_item_from_modulestore(unit_locator, True)
html = self.get_item_from_modulestore(html_locator, True)
self.assertEqual(compute_publish_state(unit), PublishState.draft)
self.assertEqual(compute_publish_state(html), PublishState.draft)
@ddt.ddt @ddt.ddt
class TestComponentHandler(TestCase): class TestComponentHandler(TestCase):
def setUp(self): def setUp(self):
......
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