Commit a170c6f4 by cahrens

Change save_item and create_item to RESTful URL.

Part of STUD-847.
parent dffbb8a3
...@@ -385,3 +385,18 @@ def create_other_user(_step, name, has_extra_perms, role_name): ...@@ -385,3 +385,18 @@ def create_other_user(_step, name, has_extra_perms, role_name):
@step('I log out') @step('I log out')
def log_out(_step): def log_out(_step):
world.visit('logout') world.visit('logout')
@step(u'I click on "edit a draft"$')
def i_edit_a_draft(_step):
world.css_click("a.create-draft")
@step(u'I click on "replace with draft"$')
def i_edit_a_draft(_step):
world.css_click("a.publish-draft")
@step(u'I publish the unit$')
def publish_unit(_step):
world.select_option('visibility-select', 'public')
...@@ -87,13 +87,18 @@ def add_component_category(step, component, category): ...@@ -87,13 +87,18 @@ def add_component_category(step, component, category):
@step(u'I delete all components$') @step(u'I delete all components$')
def delete_all_components(step): def delete_all_components(step):
count = len(world.css_find('ol.components li.component'))
step.given('I delete "' + str(count) + '" component')
@step(u'I delete "([^"]*)" component$')
def delete_components(step, number):
world.wait_for_xmodule() world.wait_for_xmodule()
delete_btn_css = 'a.delete-button' delete_btn_css = 'a.delete-button'
prompt_css = 'div#prompt-warning' prompt_css = 'div#prompt-warning'
btn_css = '{} a.button.action-primary'.format(prompt_css) btn_css = '{} a.button.action-primary'.format(prompt_css)
saving_mini_css = 'div#page-notification .wrapper-notification-mini' saving_mini_css = 'div#page-notification .wrapper-notification-mini'
count = len(world.css_find('ol.components li.component')) for _ in range(int(number)):
for _ in range(int(count)):
world.css_click(delete_btn_css) world.css_click(delete_btn_css)
assert_true( assert_true(
world.is_css_present('{}.is-shown'.format(prompt_css)), world.is_css_present('{}.is-shown'.format(prompt_css)),
......
...@@ -81,6 +81,21 @@ Feature: CMS.Problem Editor ...@@ -81,6 +81,21 @@ Feature: CMS.Problem Editor
When I edit and select Settings When I edit and select Settings
Then Edit High Level Source is visible Then Edit High Level Source is visible
# This is a very specific scenario that was failing with some of the
# DB rearchitecture changes. It had to do with children IDs being stored
# with @draft at the end. To reproduce, must update children while in draft mode.
Scenario: Problems can be deleted after being public
Given I have created a Blank Common Problem
And I have created another Blank Common Problem
When I publish the unit
And I click on "edit a draft"
And I delete "1" component
And I click on "replace with draft"
And I click on "edit a draft"
And I delete "1" component
Then I see no components
# Disabled 11/13/2013 after failing in master # Disabled 11/13/2013 after failing in master
# The screenshot showed that the LaTeX editor had the text "hi", # The screenshot showed that the LaTeX editor had the text "hi",
# but Selenium timed out waiting for the text to appear. # but Selenium timed out waiting for the text to appear.
......
...@@ -19,6 +19,11 @@ SHOW_ANSWER = "Show Answer" ...@@ -19,6 +19,11 @@ SHOW_ANSWER = "Show Answer"
@step('I have created a Blank Common Problem$') @step('I have created a Blank Common Problem$')
def i_created_blank_common_problem(step): def i_created_blank_common_problem(step):
world.create_course_with_unit() world.create_course_with_unit()
step.given("I have created another Blank Common Problem")
@step('I have created another Blank Common Problem$')
def i_create_new_common_problem(step):
world.create_component_instance( world.create_component_instance(
step=step, step=step,
category='problem', category='problem',
...@@ -218,11 +223,6 @@ def i_import_the_file(_step, filename): ...@@ -218,11 +223,6 @@ def i_import_the_file(_step, filename):
import_file(filename) import_file(filename)
@step(u'I click on "edit a draft"$')
def i_edit_a_draft(_step):
world.css_click("a.create-draft")
@step(u'I go to the vertical "([^"]*)"$') @step(u'I go to the vertical "([^"]*)"$')
def i_go_to_vertical(_step, vertical): def i_go_to_vertical(_step, vertical):
world.css_click("span:contains('{0}')".format(vertical)) world.css_click("span:contains('{0}')".format(vertical))
......
...@@ -398,9 +398,15 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase): ...@@ -398,9 +398,15 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
self.assertEqual(course.tabs, expected_tabs) self.assertEqual(course.tabs, expected_tabs)
def test_static_tab_reordering(self): def test_static_tab_reordering(self):
def get_tab_locator(tab):
tab_location = 'i4x://MITx/999/static_tab/{0}'.format(tab['url_slug'])
return unicode(loc_mapper().translate_location(
course.location.course_id, Location(tab_location), False, True
))
module_store = modulestore('direct') module_store = modulestore('direct')
CourseFactory.create(org='edX', course='999', display_name='Robot Super Course') locator = _course_factory_create_course()
course_location = Location(['i4x', 'edX', '999', 'course', 'Robot_Super_Course', None]) course_location = loc_mapper().translate_locator_to_location(locator)
ItemFactory.create( ItemFactory.create(
parent_location=course_location, parent_location=course_location,
...@@ -411,23 +417,23 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase): ...@@ -411,23 +417,23 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
category="static_tab", category="static_tab",
display_name="Static_2") display_name="Static_2")
course = module_store.get_item(Location(['i4x', 'edX', '999', 'course', 'Robot_Super_Course', None])) course = module_store.get_item(course_location)
# reverse the ordering # reverse the ordering
reverse_tabs = [] reverse_tabs = []
for tab in course.tabs: for tab in course.tabs:
if tab['type'] == 'static_tab': if tab['type'] == 'static_tab':
reverse_tabs.insert(0, 'i4x://edX/999/static_tab/{0}'.format(tab['url_slug'])) reverse_tabs.insert(0, get_tab_locator(tab))
self.client.ajax_post(reverse('reorder_static_tabs'), {'tabs': reverse_tabs}) self.client.ajax_post(reverse('reorder_static_tabs'), {'tabs': reverse_tabs})
course = module_store.get_item(Location(['i4x', 'edX', '999', 'course', 'Robot_Super_Course', None])) course = module_store.get_item(course_location)
# compare to make sure that the tabs information is in the expected order after the server call # compare to make sure that the tabs information is in the expected order after the server call
course_tabs = [] course_tabs = []
for tab in course.tabs: for tab in course.tabs:
if tab['type'] == 'static_tab': if tab['type'] == 'static_tab':
course_tabs.append('i4x://edX/999/static_tab/{0}'.format(tab['url_slug'])) course_tabs.append(get_tab_locator(tab))
self.assertEqual(reverse_tabs, course_tabs) self.assertEqual(reverse_tabs, course_tabs)
...@@ -1528,22 +1534,22 @@ class ContentStoreTest(ModuleStoreTestCase): ...@@ -1528,22 +1534,22 @@ class ContentStoreTest(ModuleStoreTestCase):
resp = self._show_course_overview(loc) resp = self._show_course_overview(loc)
self.assertContains( self.assertContains(
resp, resp,
'<article class="courseware-overview" data-id="i4x://MITx/999/course/Robot_Super_Course">', '<article class="courseware-overview" data-locator="MITx.999.Robot_Super_Course/branch/draft/block/Robot_Super_Course">',
status_code=200, status_code=200,
html=True html=True
) )
def test_create_item(self): def test_create_item(self):
"""Test cloning an item. E.g. creating a new section""" """Test creating a new xblock instance."""
CourseFactory.create(org='MITx', course='999', display_name='Robot Super Course') locator = _course_factory_create_course()
section_data = { section_data = {
'parent_location': 'i4x://MITx/999/course/Robot_Super_Course', 'parent_locator': unicode(locator),
'category': 'chapter', 'category': 'chapter',
'display_name': 'Section One', 'display_name': 'Section One',
} }
resp = self.client.ajax_post(reverse('create_item'), section_data) resp = self.client.ajax_post('/xblock', section_data)
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
data = parse_json(resp) data = parse_json(resp)
...@@ -1554,14 +1560,14 @@ class ContentStoreTest(ModuleStoreTestCase): ...@@ -1554,14 +1560,14 @@ class ContentStoreTest(ModuleStoreTestCase):
def test_capa_module(self): def test_capa_module(self):
"""Test that a problem treats markdown specially.""" """Test that a problem treats markdown specially."""
CourseFactory.create(org='MITx', course='999', display_name='Robot Super Course') locator = _course_factory_create_course()
problem_data = { problem_data = {
'parent_location': 'i4x://MITx/999/course/Robot_Super_Course', 'parent_locator': unicode(locator),
'category': 'problem' 'category': 'problem'
} }
resp = self.client.ajax_post(reverse('create_item'), problem_data) resp = self.client.ajax_post('/xblock', problem_data)
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
payload = parse_json(resp) payload = parse_json(resp)
...@@ -1911,7 +1917,7 @@ class MetadataSaveTestCase(ModuleStoreTestCase): ...@@ -1911,7 +1917,7 @@ class MetadataSaveTestCase(ModuleStoreTestCase):
def _create_course(test, course_data): def _create_course(test, course_data):
""" """
Creates a course and verifies the URL returned in the response.. Creates a course via an AJAX request and verifies the URL returned in the response.
""" """
course_id = _get_course_id(course_data) course_id = _get_course_id(course_data)
new_location = loc_mapper().translate_location(course_id, CourseDescriptor.id_to_location(course_id), False, True) new_location = loc_mapper().translate_location(course_id, CourseDescriptor.id_to_location(course_id), False, True)
...@@ -1923,6 +1929,14 @@ def _create_course(test, course_data): ...@@ -1923,6 +1929,14 @@ def _create_course(test, course_data):
test.assertEqual(data['url'], new_location.url_reverse("course/", "")) test.assertEqual(data['url'], new_location.url_reverse("course/", ""))
def _course_factory_create_course():
"""
Creates a course via the CourseFactory and returns the locator for it.
"""
course = CourseFactory.create(org='MITx', course='999', display_name='Robot Super Course')
return loc_mapper().translate_location(course.location.course_id, course.location, False, True)
def _get_course_id(test_course_data): def _get_course_id(test_course_data):
"""Returns the course ID (org/number/run).""" """Returns the course ID (org/number/run)."""
return "{org}/{number}/{run}".format(**test_course_data) return "{org}/{number}/{run}".format(**test_course_data)
...@@ -19,6 +19,7 @@ from xmodule.modulestore.django import modulestore ...@@ -19,6 +19,7 @@ from xmodule.modulestore.django import modulestore
from xmodule.contentstore.django import contentstore, _CONTENTSTORE from xmodule.contentstore.django import contentstore, _CONTENTSTORE
from xmodule.contentstore.content import StaticContent from xmodule.contentstore.content import StaticContent
from xmodule.exceptions import NotFoundError from xmodule.exceptions import NotFoundError
from xmodule.modulestore.django import loc_mapper
from contentstore.tests.modulestore_config import TEST_MODULESTORE from contentstore.tests.modulestore_config import TEST_MODULESTORE
TEST_DATA_CONTENTSTORE = copy.deepcopy(settings.CONTENTSTORE) TEST_DATA_CONTENTSTORE = copy.deepcopy(settings.CONTENTSTORE)
...@@ -47,14 +48,17 @@ class Basetranscripts(CourseTestCase): ...@@ -47,14 +48,17 @@ class Basetranscripts(CourseTestCase):
def setUp(self): def setUp(self):
"""Create initial data.""" """Create initial data."""
super(Basetranscripts, self).setUp() super(Basetranscripts, self).setUp()
self.unicode_locator = unicode(loc_mapper().translate_location(
self.course.location.course_id, self.course.location, False, True
))
# Add video module # Add video module
data = { data = {
'parent_location': str(self.course_location), 'parent_locator': self.unicode_locator,
'category': 'video', 'category': 'video',
'type': 'video' 'type': 'video'
} }
resp = self.client.ajax_post(reverse('create_item'), data) resp = self.client.ajax_post('/xblock', data)
self.item_location = json.loads(resp.content).get('id') self.item_location = json.loads(resp.content).get('id')
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
...@@ -196,11 +200,11 @@ class TestUploadtranscripts(Basetranscripts): ...@@ -196,11 +200,11 @@ class TestUploadtranscripts(Basetranscripts):
def test_fail_for_non_video_module(self): def test_fail_for_non_video_module(self):
# non_video module: setup # non_video module: setup
data = { data = {
'parent_location': str(self.course_location), 'parent_locator': self.unicode_locator,
'category': 'non_video', 'category': 'non_video',
'type': 'non_video' 'type': 'non_video'
} }
resp = self.client.ajax_post(reverse('create_item'), data) resp = self.client.ajax_post('/xblock', data)
item_location = json.loads(resp.content).get('id') item_location = json.loads(resp.content).get('id')
data = '<non_video youtube="0.75:JMD_ifUUfsU,1.0:hI10vDNYz4M" />' data = '<non_video youtube="0.75:JMD_ifUUfsU,1.0:hI10vDNYz4M" />'
modulestore().update_item(item_location, data) modulestore().update_item(item_location, data)
...@@ -407,11 +411,11 @@ class TestDownloadtranscripts(Basetranscripts): ...@@ -407,11 +411,11 @@ class TestDownloadtranscripts(Basetranscripts):
def test_fail_for_non_video_module(self): def test_fail_for_non_video_module(self):
# Video module: setup # Video module: setup
data = { data = {
'parent_location': str(self.course_location), 'parent_locator': self.unicode_locator,
'category': 'videoalpha', 'category': 'videoalpha',
'type': 'videoalpha' 'type': 'videoalpha'
} }
resp = self.client.ajax_post(reverse('create_item'), data) resp = self.client.ajax_post('/xblock', data)
item_location = json.loads(resp.content).get('id') item_location = json.loads(resp.content).get('id')
subs_id = str(uuid4()) subs_id = str(uuid4())
data = textwrap.dedent(""" data = textwrap.dedent("""
...@@ -657,11 +661,11 @@ class TestChecktranscripts(Basetranscripts): ...@@ -657,11 +661,11 @@ class TestChecktranscripts(Basetranscripts):
def test_fail_for_non_video_module(self): def test_fail_for_non_video_module(self):
# Not video module: setup # Not video module: setup
data = { data = {
'parent_location': str(self.course_location), 'parent_locator': self.unicode_locator,
'category': 'not_video', 'category': 'not_video',
'type': 'not_video' 'type': 'not_video'
} }
resp = self.client.ajax_post(reverse('create_item'), data) resp = self.client.ajax_post('/xblock', data)
item_location = json.loads(resp.content).get('id') item_location = json.loads(resp.content).get('id')
subs_id = str(uuid4()) subs_id = str(uuid4())
data = textwrap.dedent(""" data = textwrap.dedent("""
......
...@@ -120,6 +120,10 @@ def edit_subsection(request, location): ...@@ -120,6 +120,10 @@ def edit_subsection(request, location):
can_view_live = True can_view_live = True
break break
locator = loc_mapper().translate_location(
course.location.course_id, item.location, False, True
)
return render_to_response( return render_to_response(
'edit_subsection.html', 'edit_subsection.html',
{ {
...@@ -129,8 +133,10 @@ def edit_subsection(request, location): ...@@ -129,8 +133,10 @@ def edit_subsection(request, location):
'lms_link': lms_link, 'lms_link': lms_link,
'preview_link': preview_link, 'preview_link': preview_link,
'course_graders': json.dumps(CourseGradingModel.fetch(course.location).graders), 'course_graders': json.dumps(CourseGradingModel.fetch(course.location).graders),
# For grader, which is not yet converted
'parent_location': course.location, 'parent_location': course.location,
'parent_item': parent, 'parent_item': parent,
'locator': locator,
'policy_metadata': policy_metadata, 'policy_metadata': policy_metadata,
'subsection_units': subsection_units, 'subsection_units': subsection_units,
'can_view_live': can_view_live 'can_view_live': can_view_live
...@@ -175,9 +181,9 @@ def edit_unit(request, location): ...@@ -175,9 +181,9 @@ def edit_unit(request, location):
# Note that the unit_state (draft, public, private) does not match up with the published value # Note that the unit_state (draft, public, private) does not match up with the published value
# passed to translate_location. The two concepts are different at this point. # passed to translate_location. The two concepts are different at this point.
unit_update_url = loc_mapper().translate_location( unit_locator = loc_mapper().translate_location(
course.location.course_id, Location(location), False, True course.location.course_id, Location(location), False, True
).url_reverse("xblock", "") )
component_templates = defaultdict(list) component_templates = defaultdict(list)
for category in COMPONENT_TYPES: for category in COMPONENT_TYPES:
...@@ -247,7 +253,7 @@ def edit_unit(request, location): ...@@ -247,7 +253,7 @@ def edit_unit(request, location):
component.location.url(), component.location.url(),
loc_mapper().translate_location( loc_mapper().translate_location(
course.location.course_id, component.location, False, True course.location.course_id, component.location, False, True
).url_reverse("xblock") )
] ]
for component for component
in item.get_children() in item.get_children()
...@@ -296,8 +302,9 @@ def edit_unit(request, location): ...@@ -296,8 +302,9 @@ def edit_unit(request, location):
return render_to_response('unit.html', { return render_to_response('unit.html', {
'context_course': course, 'context_course': course,
'unit': item, 'unit': item,
# Still needed for creating a draft.
'unit_location': location, 'unit_location': location,
'unit_update_url': unit_update_url, 'unit_locator': unit_locator,
'components': components, 'components': components,
'component_templates': component_templates, 'component_templates': component_templates,
'draft_preview_link': preview_lms_link, 'draft_preview_link': preview_lms_link,
......
...@@ -192,7 +192,9 @@ def course_index(request, course_id, branch, version_guid, block): ...@@ -192,7 +192,9 @@ def course_index(request, course_id, branch, version_guid, block):
'course_graders': json.dumps( 'course_graders': json.dumps(
CourseGradingModel.fetch(course.location).graders CourseGradingModel.fetch(course.location).graders
), ),
# This is used by course grader, which has not yet been updated.
'parent_location': course.location, 'parent_location': course.location,
'parent_locator': location,
'new_section_category': 'chapter', 'new_section_category': 'chapter',
'new_subsection_category': 'sequential', 'new_subsection_category': 'sequential',
'new_unit_category': 'vertical', 'new_unit_category': 'vertical',
......
...@@ -13,6 +13,7 @@ from xmodule.modulestore import Location ...@@ -13,6 +13,7 @@ from xmodule.modulestore import Location
from xmodule.modulestore.inheritance import own_metadata from xmodule.modulestore.inheritance import own_metadata
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from xmodule.modulestore.django import loc_mapper from xmodule.modulestore.django import loc_mapper
from xmodule.modulestore.locator import BlockUsageLocator
from ..utils import get_course_for_item, get_modulestore from ..utils import get_course_for_item, get_modulestore
...@@ -47,8 +48,12 @@ def initialize_course_tabs(course): ...@@ -47,8 +48,12 @@ def initialize_course_tabs(course):
@expect_json @expect_json
def reorder_static_tabs(request): def reorder_static_tabs(request):
"Order the static tabs in the requested order" "Order the static tabs in the requested order"
def get_location_for_tab(tab):
tab_locator = BlockUsageLocator(tab)
return loc_mapper().translate_locator_to_location(tab_locator)
tabs = request.json['tabs'] tabs = request.json['tabs']
course = get_course_for_item(tabs[0]) course = get_course_for_item(get_location_for_tab(tabs[0]))
if not has_access(request.user, course.location): if not has_access(request.user, course.location):
raise PermissionDenied() raise PermissionDenied()
...@@ -64,7 +69,7 @@ def reorder_static_tabs(request): ...@@ -64,7 +69,7 @@ def reorder_static_tabs(request):
# load all reference tabs, return BadRequest if we can't find any of them # load all reference tabs, return BadRequest if we can't find any of them
tab_items = [] tab_items = []
for tab in tabs: for tab in tabs:
item = modulestore('direct').get_item(Location(tab)) item = modulestore('direct').get_item(get_location_for_tab(tab))
if item is None: if item is None:
return HttpResponseBadRequest() return HttpResponseBadRequest()
...@@ -122,15 +127,20 @@ def edit_tabs(request, org, course, coursename): ...@@ -122,15 +127,20 @@ def edit_tabs(request, org, course, coursename):
static_tab.location.url(), static_tab.location.url(),
loc_mapper().translate_location( loc_mapper().translate_location(
course_item.location.course_id, static_tab.location, False, True course_item.location.course_id, static_tab.location, False, True
).url_reverse("xblock") )
] ]
for static_tab for static_tab
in static_tabs in static_tabs
] ]
course_locator = loc_mapper().translate_location(
course_item.location.course_id, course_item.location, False, True
)
return render_to_response('edit-tabs.html', { return render_to_response('edit-tabs.html', {
'context_course': course_item, 'context_course': course_item,
'components': components 'components': components,
'locator': course_locator
}) })
......
...@@ -182,7 +182,7 @@ define([ ...@@ -182,7 +182,7 @@ define([
"coffee/spec/main_spec", "coffee/spec/main_spec",
"coffee/spec/models/course_spec", "coffee/spec/models/metadata_spec", "coffee/spec/models/course_spec", "coffee/spec/models/metadata_spec",
"coffee/spec/models/module_spec", "coffee/spec/models/section_spec", "coffee/spec/models/section_spec",
"coffee/spec/models/settings_course_grader_spec", "coffee/spec/models/settings_course_grader_spec",
"coffee/spec/models/settings_grading_spec", "coffee/spec/models/textbook_spec", "coffee/spec/models/settings_grading_spec", "coffee/spec/models/textbook_spec",
"coffee/spec/models/upload_spec", "coffee/spec/models/upload_spec",
...@@ -193,9 +193,11 @@ define([ ...@@ -193,9 +193,11 @@ define([
"coffee/spec/views/overview_spec", "coffee/spec/views/overview_spec",
"coffee/spec/views/textbook_spec", "coffee/spec/views/upload_spec", "coffee/spec/views/textbook_spec", "coffee/spec/views/upload_spec",
"js_spec/transcripts/utils_spec", "js_spec/transcripts/editor_spec", "js/spec/transcripts/utils_spec", "js/spec/transcripts/editor_spec",
"js_spec/transcripts/videolist_spec", "js_spec/transcripts/message_manager_spec", "js/spec/transcripts/videolist_spec", "js/spec/transcripts/message_manager_spec",
"js_spec/transcripts/file_uploader_spec" "js/spec/transcripts/file_uploader_spec",
"js/spec/utils/module_spec"
# these tests are run separate in the cms-squire suite, due to process # these tests are run separate in the cms-squire suite, due to process
# isolation issues with Squire.js # isolation issues with Squire.js
......
define ["coffee/src/models/module"], (Module) ->
describe "Module", ->
it "set the correct URL", ->
expect(new Module().url).toEqual("/save_item")
it "set the correct default", ->
expect(new Module().defaults).toEqual(undefined)
define ["js/models/section", "sinon"], (Section, sinon) -> define ["js/models/section", "sinon", "js/utils/module"], (Section, sinon, ModuleUtils) ->
describe "Section", -> describe "Section", ->
describe "basic", -> describe "basic", ->
beforeEach -> beforeEach ->
@model = new Section({ @model = new Section({
id: 42, id: 42
name: "Life, the Universe, and Everything" name: "Life, the Universe, and Everything"
}) })
...@@ -14,11 +14,10 @@ define ["js/models/section", "sinon"], (Section, sinon) -> ...@@ -14,11 +14,10 @@ define ["js/models/section", "sinon"], (Section, sinon) ->
expect(@model.get("name")).toEqual("Life, the Universe, and Everything") expect(@model.get("name")).toEqual("Life, the Universe, and Everything")
it "should have a URL set", -> it "should have a URL set", ->
expect(@model.url).toEqual("/save_item") expect(@model.url()).toEqual(ModuleUtils.getUpdateUrl(42))
it "should serialize to JSON correctly", -> it "should serialize to JSON correctly", ->
expect(@model.toJSON()).toEqual({ expect(@model.toJSON()).toEqual({
id: 42,
metadata: metadata:
{ {
display_name: "Life, the Universe, and Everything" display_name: "Life, the Universe, and Everything"
...@@ -30,7 +29,7 @@ define ["js/models/section", "sinon"], (Section, sinon) -> ...@@ -30,7 +29,7 @@ define ["js/models/section", "sinon"], (Section, sinon) ->
spyOn(Section.prototype, 'showNotification') spyOn(Section.prototype, 'showNotification')
spyOn(Section.prototype, 'hideNotification') spyOn(Section.prototype, 'hideNotification')
@model = new Section({ @model = new Section({
id: 42, id: 42
name: "Life, the Universe, and Everything" name: "Life, the Universe, and Everything"
}) })
@requests = requests = [] @requests = requests = []
......
...@@ -4,6 +4,9 @@ define ["coffee/src/views/module_edit", "xmodule"], (ModuleEdit) -> ...@@ -4,6 +4,9 @@ define ["coffee/src/views/module_edit", "xmodule"], (ModuleEdit) ->
beforeEach -> beforeEach ->
@stubModule = jasmine.createSpy("Module") @stubModule = jasmine.createSpy("Module")
@stubModule.id = 'stub-id' @stubModule.id = 'stub-id'
@stubModule.get = (param)->
if param == 'old_id'
return 'stub-old-id'
setFixtures """ setFixtures """
<li class="component" id="stub-id"> <li class="component" id="stub-id">
...@@ -59,7 +62,7 @@ define ["coffee/src/views/module_edit", "xmodule"], (ModuleEdit) -> ...@@ -59,7 +62,7 @@ define ["coffee/src/views/module_edit", "xmodule"], (ModuleEdit) ->
@moduleEdit.render() @moduleEdit.render()
it "loads the module preview and editor via ajax on the view element", -> it "loads the module preview and editor via ajax on the view element", ->
expect(@moduleEdit.$el.load).toHaveBeenCalledWith("/preview_component/#{@moduleEdit.model.id}", jasmine.any(Function)) expect(@moduleEdit.$el.load).toHaveBeenCalledWith("/preview_component/#{@moduleEdit.model.get('old_id')}", jasmine.any(Function))
@moduleEdit.$el.load.mostRecentCall.args[1]() @moduleEdit.$el.load.mostRecentCall.args[1]()
expect(@moduleEdit.loadDisplay).toHaveBeenCalled() expect(@moduleEdit.loadDisplay).toHaveBeenCalled()
expect(@moduleEdit.delegateEvents).toHaveBeenCalled() expect(@moduleEdit.delegateEvents).toHaveBeenCalled()
......
...@@ -8,7 +8,7 @@ define ["js/views/overview", "js/views/feedback_notification", "sinon", "js/base ...@@ -8,7 +8,7 @@ define ["js/views/overview", "js/views/feedback_notification", "sinon", "js/base
<span class="published-status"> <span class="published-status">
<strong>Will Release:</strong> 06/12/2013 at 04:00 UTC <strong>Will Release:</strong> 06/12/2013 at 04:00 UTC
</span> </span>
<a href="#" class="edit-button" data-date="06/12/2013" data-time="04:00" data-id="i4x://pfogg/42/chapter/d6b47f7b084f49debcaf67fe5436c8e2">Edit</a> <a href="#" class="edit-button" data-date="06/12/2013" data-time="04:00" data-locator="i4x://pfogg/42/chapter/d6b47f7b084f49debcaf67fe5436c8e2">Edit</a>
</div> </div>
""" """
...@@ -35,8 +35,8 @@ define ["js/views/overview", "js/views/feedback_notification", "sinon", "js/base ...@@ -35,8 +35,8 @@ define ["js/views/overview", "js/views/feedback_notification", "sinon", "js/base
""" """
appendSetFixtures """ appendSetFixtures """
<section class="courseware-section branch" data-id="a-location-goes-here"> <section class="courseware-section branch" data-locator="a-location-goes-here">
<li class="branch collapsed id-holder" data-id="an-id-goes-here"> <li class="branch collapsed id-holder" data-id="an-id-goes-here" data-locator="an-id-goes-here">
<a href="#" class="delete-section-button"></a> <a href="#" class="delete-section-button"></a>
</li> </li>
</section> </section>
...@@ -44,19 +44,19 @@ define ["js/views/overview", "js/views/feedback_notification", "sinon", "js/base ...@@ -44,19 +44,19 @@ define ["js/views/overview", "js/views/feedback_notification", "sinon", "js/base
appendSetFixtures """ appendSetFixtures """
<ol> <ol>
<li class="subsection-list branch" data-id="subsection-1-id" id="subsection-1"> <li class="subsection-list branch" id="subsection-1" data-locator="subsection-1-id">
<ol class="sortable-unit-list" id="subsection-list-1"> <ol class="sortable-unit-list" id="subsection-list-1">
<li class="unit" id="unit-1" data-id="first-unit-id" data-parent-id="subsection-1-id"></li> <li class="unit" id="unit-1" data-parent="subsection-1-id" data-locator="first-unit-id"></li>
<li class="unit" id="unit-2" data-id="second-unit-id" data-parent-id="subsection-1-id"></li> <li class="unit" id="unit-2" data-parent="subsection-1-id" data-locator="second-unit-id"></li>
<li class="unit" id="unit-3" data-id="third-unit-id" data-parent-id="subsection-1-id"></li> <li class="unit" id="unit-3" data-parent="subsection-1-id" data-locator="third-unit-id"></li>
</ol> </ol>
</li> </li>
<li class="subsection-list branch" data-id="subsection-2-id" id="subsection-2"> <li class="subsection-list branch" id="subsection-2" data-locator="subsection-2-id">
<ol class="sortable-unit-list" id="subsection-list-2"> <ol class="sortable-unit-list" id="subsection-list-2">
<li class="unit" id="unit-4" data-id="fourth-unit-id" data-parent-id="subsection-2"></li> <li class="unit" id="unit-4" data-parent="subsection-2" data-locator="fourth-unit-id"></li>
</ol> </ol>
</li> </li>
<li class="subsection-list branch" data-id="subsection-3-id" id="subsection-3"> <li class="subsection-list branch" id="subsection-3" data-locator="subsection-3-id">
<ol class="sortable-unit-list" id="subsection-list-3"> <ol class="sortable-unit-list" id="subsection-list-3">
</li> </li>
</ol> </ol>
...@@ -366,10 +366,10 @@ define ["js/views/overview", "js/views/feedback_notification", "sinon", "js/base ...@@ -366,10 +366,10 @@ define ["js/views/overview", "js/views/feedback_notification", "sinon", "js/base
expect($('#unit-1')).toHaveClass('was-dropped') expect($('#unit-1')).toHaveClass('was-dropped')
# We expect 2 requests to be sent-- the first for removing Unit 1 from Subsection 1, # We expect 2 requests to be sent-- the first for removing Unit 1 from Subsection 1,
# and the second for adding Unit 1 to the end of Subsection 2. # and the second for adding Unit 1 to the end of Subsection 2.
expect(@requests[0].requestBody).toEqual('{"id":"subsection-1-id","children":["second-unit-id","third-unit-id"]}') expect(@requests[0].requestBody).toEqual('{"children":["second-unit-id","third-unit-id"]}')
@requests[0].respond(200) @requests[0].respond(200)
expect(@savingSpies.hide).not.toHaveBeenCalled() expect(@savingSpies.hide).not.toHaveBeenCalled()
expect(@requests[1].requestBody).toEqual('{"id":"subsection-2-id","children":["fourth-unit-id","first-unit-id"]}') expect(@requests[1].requestBody).toEqual('{"children":["fourth-unit-id","first-unit-id"]}')
@requests[1].respond(200) @requests[1].respond(200)
expect(@savingSpies.hide).toHaveBeenCalled() expect(@savingSpies.hide).toHaveBeenCalled()
# Class is removed in a timeout. # Class is removed in a timeout.
......
define ["backbone"], (Backbone) ->
class Module extends Backbone.Model
url: '/save_item'
...@@ -63,20 +63,21 @@ define ["backbone", "jquery", "underscore", "gettext", "xblock/runtime.v1", ...@@ -63,20 +63,21 @@ define ["backbone", "jquery", "underscore", "gettext", "xblock/runtime.v1",
return _.extend(@metadataEditor.getModifiedMetadataValues(), @customMetadata()) return _.extend(@metadataEditor.getModifiedMetadataValues(), @customMetadata())
createItem: (parent, payload) -> createItem: (parent, payload) ->
payload.parent_location = parent payload.parent_locator = parent
$.postJSON( $.postJSON(
"/create_item" @model.urlRoot
payload payload
(data) => (data) =>
@model.set(id: data.id) @model.set(id: data.locator)
@model.set(old_id: data.id)
@$el.data('id', data.id) @$el.data('id', data.id)
@$el.data('update_url', data.update_url) @$el.data('locator', data.locator)
@render() @render()
) )
render: -> render: ->
if @model.id if @model.get('old_id')
@$el.load("/preview_component/#{@model.id}", => @$el.load("/preview_component/#{@model.get('old_id')}", =>
@loadDisplay() @loadDisplay()
@delegateEvents() @delegateEvents()
) )
......
define ["jquery", "jquery.ui", "backbone", "js/views/feedback_prompt", "js/views/feedback_notification", "coffee/src/models/module", "coffee/src/views/module_edit"], define ["jquery", "jquery.ui", "backbone", "js/views/feedback_prompt", "js/views/feedback_notification",
($, ui, Backbone, PromptView, NotificationView, ModuleModel, ModuleEditView) -> "coffee/src/views/module_edit", "js/models/module_info", "js/utils/module"],
($, ui, Backbone, PromptView, NotificationView, ModuleEditView, ModuleModel, ModuleUtils) ->
class TabsEdit extends Backbone.View class TabsEdit extends Backbone.View
initialize: => initialize: =>
@$('.component').each((idx, element) => @$('.component').each((idx, element) =>
model = new ModuleModel({
id: $(element).data('locator'),
old_id:$(element).data('id')
})
new ModuleEditView( new ModuleEditView(
el: element, el: element,
onDelete: @deleteTab, onDelete: @deleteTab,
model: new ModuleModel( model: model
id: $(element).data('id'),
)
) )
) )
...@@ -28,7 +32,7 @@ define ["jquery", "jquery.ui", "backbone", "js/views/feedback_prompt", "js/views ...@@ -28,7 +32,7 @@ define ["jquery", "jquery.ui", "backbone", "js/views/feedback_prompt", "js/views
tabMoved: (event, ui) => tabMoved: (event, ui) =>
tabs = [] tabs = []
@$('.component').each((idx, element) => @$('.component').each((idx, element) =>
tabs.push($(element).data('id')) tabs.push($(element).data('locator'))
) )
analytics.track "Reordered Static Pages", analytics.track "Reordered Static Pages",
...@@ -78,13 +82,13 @@ define ["jquery", "jquery.ui", "backbone", "js/views/feedback_prompt", "js/views ...@@ -78,13 +82,13 @@ define ["jquery", "jquery.ui", "backbone", "js/views/feedback_prompt", "js/views
analytics.track "Deleted Static Page", analytics.track "Deleted Static Page",
course: course_location_analytics course: course_location_analytics
id: $component.data('id') id: $component.data('locator')
deleting = new NotificationView.Mini deleting = new NotificationView.Mini
title: gettext('Deleting&hellip;') title: gettext('Deleting&hellip;')
deleting.show() deleting.show()
$.ajax({ $.ajax({
type: 'DELETE', type: 'DELETE',
url: $component.data('update_url') url: ModuleUtils.getUpdateUrl($component.data('locator'))
}).success(=> }).success(=>
$component.remove() $component.remove()
deleting.hide() deleting.hide()
......
define ["jquery", "jquery.ui", "gettext", "backbone", define ["jquery", "jquery.ui", "gettext", "backbone",
"js/views/feedback_notification", "js/views/feedback_prompt", "js/views/feedback_notification", "js/views/feedback_prompt",
"coffee/src/models/module", "coffee/src/views/module_edit"], "coffee/src/views/module_edit", "js/models/module_info"],
($, ui, gettext, Backbone, NotificationView, PromptView, ModuleModel, ModuleEditView) -> ($, ui, gettext, Backbone, NotificationView, PromptView, ModuleEditView, ModuleModel) ->
class UnitEditView extends Backbone.View class UnitEditView extends Backbone.View
events: events:
'click .new-component .new-component-type a.multiple-templates': 'showComponentTemplates' 'click .new-component .new-component-type a.multiple-templates': 'showComponentTemplates'
...@@ -61,11 +61,13 @@ define ["jquery", "jquery.ui", "gettext", "backbone", ...@@ -61,11 +61,13 @@ define ["jquery", "jquery.ui", "gettext", "backbone",
) )
@$('.component').each (idx, element) => @$('.component').each (idx, element) =>
model = new ModuleModel
id: $(element).data('locator')
old_id: $(element).data('id')
new ModuleEditView new ModuleEditView
el: element, el: element,
onDelete: @deleteComponent, onDelete: @deleteComponent,
model: new ModuleModel model: model
id: $(element).data('id')
showComponentTemplates: (event) => showComponentTemplates: (event) =>
event.preventDefault() event.preventDefault()
...@@ -96,7 +98,7 @@ define ["jquery", "jquery.ui", "gettext", "backbone", ...@@ -96,7 +98,7 @@ define ["jquery", "jquery.ui", "gettext", "backbone",
@$newComponentItem.before(editor.$el) @$newComponentItem.before(editor.$el)
editor.createItem( editor.createItem(
@$el.data('id'), @$el.data('locator'),
$(event.currentTarget).data() $(event.currentTarget).data()
) )
...@@ -107,7 +109,7 @@ define ["jquery", "jquery.ui", "gettext", "backbone", ...@@ -107,7 +109,7 @@ define ["jquery", "jquery.ui", "gettext", "backbone",
@closeNewComponent(event) @closeNewComponent(event)
components: => @$('.component').map((idx, el) -> $(el).data('id')).get() components: => @$('.component').map((idx, el) -> $(el).data('locator')).get()
wait: (value) => wait: (value) =>
@$('.unit-body').toggleClass("waiting", value) @$('.unit-body').toggleClass("waiting", value)
...@@ -136,13 +138,13 @@ define ["jquery", "jquery.ui", "gettext", "backbone", ...@@ -136,13 +138,13 @@ define ["jquery", "jquery.ui", "gettext", "backbone",
$component = $(event.currentTarget).parents('.component') $component = $(event.currentTarget).parents('.component')
$.ajax({ $.ajax({
type: 'DELETE', type: 'DELETE',
url: $component.data('update_url') url: @model.urlRoot + "/" + $component.data('locator')
}).success(=> }).success(=>
deleting.hide() deleting.hide()
analytics.track "Deleted a Component", analytics.track "Deleted a Component",
course: course_location_analytics course: course_location_analytics
unit_id: unit_location_analytics unit_id: unit_location_analytics
id: $component.data('id') id: $component.data('locator')
$component.remove() $component.remove()
# b/c we don't vigilantly keep children up to date # b/c we don't vigilantly keep children up to date
...@@ -165,7 +167,7 @@ define ["jquery", "jquery.ui", "gettext", "backbone", ...@@ -165,7 +167,7 @@ define ["jquery", "jquery.ui", "gettext", "backbone",
@wait(true) @wait(true)
$.ajax({ $.ajax({
type: 'DELETE', type: 'DELETE',
url: @$el.data('update_url') + "?" + $.param({recurse: true}) url: @model.urlRoot + "/" + @$el.data('locator') + "?" + $.param({recurse: true})
}).success(=> }).success(=>
analytics.track "Deleted Draft", analytics.track "Deleted Draft",
......
require(["domReady", "jquery", "underscore", "gettext", "js/views/feedback_notification", "js/views/feedback_prompt", require(["domReady", "jquery", "underscore", "gettext", "js/views/feedback_notification", "js/views/feedback_prompt",
"js/utils/get_date", "jquery.ui", "jquery.leanModal", "jquery.form", "jquery.smoothScroll"], "js/utils/get_date", "js/utils/module", "jquery.ui", "jquery.leanModal", "jquery.form", "jquery.smoothScroll"],
function(domReady, $, _, gettext, NotificationView, PromptView, DateUtils) { function(domReady, $, _, gettext, NotificationView, PromptView, DateUtils, ModuleUtils) {
var $body; var $body;
var $newComponentItem; var $newComponentItem;
...@@ -178,7 +178,7 @@ function saveSubsection() { ...@@ -178,7 +178,7 @@ function saveSubsection() {
$spinner.show(); $spinner.show();
} }
var id = $('.subsection-body').data('id'); var locator = $('.subsection-body').data('locator');
// pull all 'normalized' metadata editable fields on page // pull all 'normalized' metadata editable fields on page
var metadata_fields = $('input[data-metadata-name]'); var metadata_fields = $('input[data-metadata-name]');
...@@ -202,12 +202,11 @@ function saveSubsection() { ...@@ -202,12 +202,11 @@ function saveSubsection() {
}); });
$.ajax({ $.ajax({
url: "/save_item", url: ModuleUtils.getUpdateUrl(locator),
type: "POST", type: "PUT",
dataType: "json", dataType: "json",
contentType: "application/json", contentType: "application/json",
data: JSON.stringify({ data: JSON.stringify({
'id': id,
'metadata': metadata 'metadata': metadata
}), }),
success: function() { success: function() {
...@@ -226,12 +225,12 @@ function createNewUnit(e) { ...@@ -226,12 +225,12 @@ function createNewUnit(e) {
analytics.track('Created a Unit', { analytics.track('Created a Unit', {
'course': course_location_analytics, 'course': course_location_analytics,
'parent_location': parent 'parent_locator': parent
}); });
$.postJSON('/create_item', { $.postJSON(ModuleUtils.getUpdateUrl(), {
'parent_location': parent, 'parent_locator': parent,
'category': category, 'category': category,
'display_name': 'New Unit' 'display_name': 'New Unit'
}, },
...@@ -267,11 +266,11 @@ function _deleteItem($el, type) { ...@@ -267,11 +266,11 @@ function _deleteItem($el, type) {
click: function(view) { click: function(view) {
view.hide(); view.hide();
var id = $el.data('id'); var locator = $el.data('locator');
analytics.track('Deleted an Item', { analytics.track('Deleted an Item', {
'course': course_location_analytics, 'course': course_location_analytics,
'id': id 'id': locator
}); });
var deleting = new NotificationView.Mini({ var deleting = new NotificationView.Mini({
...@@ -281,7 +280,7 @@ function _deleteItem($el, type) { ...@@ -281,7 +280,7 @@ function _deleteItem($el, type) {
$.ajax({ $.ajax({
type: 'DELETE', type: 'DELETE',
url: $el.data('update_url')+'?'+ $.param({recurse: true, all_versions: true}), url: ModuleUtils.getUpdateUrl(locator) +'?'+ $.param({recurse: true, all_versions: true}),
success: function () { success: function () {
$el.remove(); $el.remove();
deleting.hide(); deleting.hide();
......
define(["backbone"], function(Backbone) { define(["backbone", "js/utils/module"], function(Backbone, ModuleUtils) {
var ModuleInfo = Backbone.Model.extend({ var ModuleInfo = Backbone.Model.extend({
urlRoot: "/xblock", urlRoot: ModuleUtils.urlRoot,
defaults: { defaults: {
"id": null, "id": null,
......
define(["backbone", "gettext", "js/views/feedback_notification"], function(Backbone, gettext, NotificationView) { define(["backbone", "gettext", "js/views/feedback_notification", "js/utils/module"],
function(Backbone, gettext, NotificationView, ModuleUtils) {
var Section = Backbone.Model.extend({ var Section = Backbone.Model.extend({
defaults: { defaults: {
"name": "" "name": ""
...@@ -8,10 +10,9 @@ define(["backbone", "gettext", "js/views/feedback_notification"], function(Backb ...@@ -8,10 +10,9 @@ define(["backbone", "gettext", "js/views/feedback_notification"], function(Backb
return gettext("You must specify a name"); return gettext("You must specify a name");
} }
}, },
url: "/save_item", urlRoot: ModuleUtils.urlRoot,
toJSON: function() { toJSON: function() {
return { return {
id: this.get("id"),
metadata: { metadata: {
display_name: this.get("name") display_name: this.get("name")
} }
......
define(['js/utils/module'],
function (ModuleUtils) {
describe('urlRoot ', function () {
it('defines xblock urlRoot', function () {
expect(ModuleUtils.urlRoot).toBe('/xblock');
});
});
describe('getUpdateUrl ', function () {
it('can take no arguments', function () {
expect(ModuleUtils.getUpdateUrl()).toBe('/xblock');
});
it('appends a locator', function () {
expect(ModuleUtils.getUpdateUrl("locator")).toBe('/xblock/locator');
});
});
}
);
/**
* Utilities for modules/xblocks.
*
* Returns:
*
* urlRoot: the root for creating/updating an xblock.
* getUpdateUrl: a utility method that returns the xblock update URL, appending
* the location if passed in.
*/
define([], function () {
var urlRoot = '/xblock';
var getUpdateUrl = function (locator) {
if (locator === undefined) {
return urlRoot;
}
else {
return urlRoot + "/" + locator;
}
};
return {
urlRoot: urlRoot,
getUpdateUrl: getUpdateUrl
};
});
define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/feedback_notification", "draggabilly", define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/feedback_notification", "draggabilly",
"js/utils/modal", "js/utils/cancel_on_escape", "js/utils/get_date"], "js/utils/modal", "js/utils/cancel_on_escape", "js/utils/get_date", "js/utils/module"],
function (domReady, $, ui, _, gettext, NotificationView, Draggabilly, ModalUtils, CancelOnEscape, DateUtils) { function (domReady, $, ui, _, gettext, NotificationView, Draggabilly, ModalUtils, CancelOnEscape,
DateUtils, ModuleUtils) {
var modalSelector = '.edit-subsection-publish-settings'; var modalSelector = '.edit-subsection-publish-settings';
...@@ -37,7 +38,7 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe ...@@ -37,7 +38,7 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe
var editSectionPublishDate = function (e) { var editSectionPublishDate = function (e) {
e.preventDefault(); e.preventDefault();
var $modal = $(modalSelector); var $modal = $(modalSelector);
$modal.attr('data-id', $(this).attr('data-id')); $modal.attr('data-locator', $(this).attr('data-locator'));
$modal.find('.start-date').val($(this).attr('data-date')); $modal.find('.start-date').val($(this).attr('data-date'));
$modal.find('.start-time').val($(this).attr('data-time')); $modal.find('.start-time').val($(this).attr('data-time'));
if ($modal.find('.start-date').val() == '' && $modal.find('.start-time').val() == '') { if ($modal.find('.start-date').val() == '' && $modal.find('.start-time').val() == '') {
...@@ -55,11 +56,11 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe ...@@ -55,11 +56,11 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe
$('.edit-subsection-publish-settings .start-time') $('.edit-subsection-publish-settings .start-time')
); );
var id = $(modalSelector).attr('data-id'); var locator = $(modalSelector).attr('data-locator');
analytics.track('Edited Section Release Date', { analytics.track('Edited Section Release Date', {
'course': course_location_analytics, 'course': course_location_analytics,
'id': id, 'id': locator,
'start': datetime 'start': datetime
}); });
...@@ -69,12 +70,11 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe ...@@ -69,12 +70,11 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe
saving.show(); saving.show();
// call into server to commit the new order // call into server to commit the new order
$.ajax({ $.ajax({
url: "/save_item", url: ModuleUtils.getUpdateUrl(locator),
type: "POST", type: "PUT",
dataType: "json", dataType: "json",
contentType: "application/json", contentType: "application/json",
data: JSON.stringify({ data: JSON.stringify({
'id': id,
'metadata': { 'metadata': {
'start': datetime 'start': datetime
} }
...@@ -86,18 +86,18 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe ...@@ -86,18 +86,18 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe
return (number < 10 ? '0' : '') + number; return (number < 10 ? '0' : '') + number;
}; };
var $thisSection = $('.courseware-section[data-id="' + id + '"]'); var $thisSection = $('.courseware-section[data-locator="' + locator + '"]');
var html = _.template( var html = _.template(
'<span class="published-status">' + '<span class="published-status">' +
'<strong>' + gettext("Will Release:") + '&nbsp;</strong>' + '<strong>' + gettext("Will Release:") + '&nbsp;</strong>' +
gettext("{month}/{day}/{year} at {hour}:{minute} UTC") + gettext("{month}/{day}/{year} at {hour}:{minute} UTC") +
'</span>' + '</span>' +
'<a href="#" class="edit-button" data-date="{month}/{day}/{year}" data-time="{hour}:{minute}" data-id="{id}">' + '<a href="#" class="edit-button" data-date="{month}/{day}/{year}" data-time="{hour}:{minute}" data-locator="{locator}">' +
gettext("Edit") + gettext("Edit") +
'</a>', '</a>',
{year: datetime.getUTCFullYear(), month: pad2(datetime.getUTCMonth() + 1), day: pad2(datetime.getUTCDate()), {year: datetime.getUTCFullYear(), month: pad2(datetime.getUTCMonth() + 1), day: pad2(datetime.getUTCDate()),
hour: pad2(datetime.getUTCHours()), minute: pad2(datetime.getUTCMinutes()), hour: pad2(datetime.getUTCHours()), minute: pad2(datetime.getUTCMinutes()),
id: id}, locator: locator},
{interpolate: /\{(.+?)\}/g}); {interpolate: /\{(.+?)\}/g});
$thisSection.find('.section-published-date').html(html); $thisSection.find('.section-published-date').html(html);
ModalUtils.hideModal(); ModalUtils.hideModal();
...@@ -132,14 +132,14 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe ...@@ -132,14 +132,14 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe
'display_name': display_name 'display_name': display_name
}); });
$.postJSON('/create_item', { $.postJSON(ModuleUtils.getUpdateUrl(), {
'parent_location': parent, 'parent_locator': parent,
'category': category, 'category': category,
'display_name': display_name 'display_name': display_name
}, },
function(data) { function(data) {
if (data.id != undefined) location.reload(); if (data.locator != undefined) location.reload();
}); });
}; };
...@@ -159,7 +159,7 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe ...@@ -159,7 +159,7 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe
var $saveButton = $newSubsection.find('.new-subsection-name-save'); var $saveButton = $newSubsection.find('.new-subsection-name-save');
var $cancelButton = $newSubsection.find('.new-subsection-name-cancel'); var $cancelButton = $newSubsection.find('.new-subsection-name-cancel');
var parent = $(this).parents("section.branch").data("id"); var parent = $(this).parents("section.branch").data("locator");
$saveButton.data('parent', parent); $saveButton.data('parent', parent);
$saveButton.data('category', $(this).data('category')); $saveButton.data('category', $(this).data('category'));
...@@ -182,14 +182,14 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe ...@@ -182,14 +182,14 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe
}); });
$.postJSON('/create_item', { $.postJSON(ModuleUtils.getUpdateUrl(), {
'parent_location': parent, 'parent_locator': parent,
'category': category, 'category': category,
'display_name': display_name 'display_name': display_name
}, },
function(data) { function(data) {
if (data.id != undefined) { if (data.locator != undefined) {
location.reload(); location.reload();
} }
}); });
...@@ -219,7 +219,7 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe ...@@ -219,7 +219,7 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe
// Exclude the 'new unit' buttons, and make sure we don't // Exclude the 'new unit' buttons, and make sure we don't
// prepend an element to itself // prepend an element to itself
var siblings = container.children().filter(function () { var siblings = container.children().filter(function () {
return $(this).data('id') !== undefined && !$(this).is(ele); return $(this).data('locator') !== undefined && !$(this).is(ele);
}); });
// If the container is collapsed, check to see if the // If the container is collapsed, check to see if the
// element is on top of its parent list -- don't check the // element is on top of its parent list -- don't check the
...@@ -416,16 +416,16 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe ...@@ -416,16 +416,16 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe
var parentSelector = ele.data('parent-location-selector'); var parentSelector = ele.data('parent-location-selector');
var childrenSelector = ele.data('child-selector'); var childrenSelector = ele.data('child-selector');
var newParentEle = ele.parents(parentSelector).first(); var newParentEle = ele.parents(parentSelector).first();
var newParentID = newParentEle.data('id'); var newParentLocator = newParentEle.data('locator');
var oldParentID = ele.data('parent-id'); var oldParentLocator = ele.data('parent');
// If the parent has changed, update the children of the old parent. // If the parent has changed, update the children of the old parent.
if (oldParentID !== newParentID) { if (newParentLocator !== oldParentLocator) {
// Find the old parent element. // Find the old parent element.
var oldParentEle = $(parentSelector).filter(function () { var oldParentEle = $(parentSelector).filter(function () {
return $(this).data('id') === oldParentID; return $(this).data('locator') === oldParentLocator;
}); });
this.saveItem(oldParentEle, childrenSelector, function () { this.saveItem(oldParentEle, childrenSelector, function () {
ele.data('parent-id', newParentID); ele.data('parent', newParentLocator);
}); });
} }
var saving = new NotificationView.Mini({ var saving = new NotificationView.Mini({
...@@ -452,16 +452,15 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe ...@@ -452,16 +452,15 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe
var children = _.map( var children = _.map(
ele.find(childrenSelector), ele.find(childrenSelector),
function (child) { function (child) {
return $(child).data('id'); return $(child).data('locator');
} }
); );
$.ajax({ $.ajax({
url: '/save_item', url: ModuleUtils.getUpdateUrl(ele.data('locator')),
type: 'POST', type: 'PUT',
dataType: 'json', dataType: 'json',
contentType: 'application/json', contentType: 'application/json',
data: JSON.stringify({ data: JSON.stringify({
id: ele.data('id'),
children: children children: children
}), }),
success: success success: success
......
...@@ -68,7 +68,7 @@ src_paths: ...@@ -68,7 +68,7 @@ src_paths:
spec_paths: spec_paths:
- coffee/spec/main.js - coffee/spec/main.js
- coffee/spec - coffee/spec
- js_spec - js/spec
# Paths to fixture files (optional) # Paths to fixture files (optional)
# The fixture path will be set automatically when using jasmine-jquery. # The fixture path will be set automatically when using jasmine-jquery.
......
...@@ -9,11 +9,11 @@ ...@@ -9,11 +9,11 @@
<%block name="jsextra"> <%block name="jsextra">
<script type='text/javascript'> <script type='text/javascript'>
require(["coffee/src/views/tabs", "coffee/src/models/module"], function(TabsEditView, ModuleModel) { require(["backbone", "coffee/src/views/tabs"], function(Backbone, TabsEditView) {
new TabsEditView({ new TabsEditView({
el: $('.main-wrapper'), el: $('.main-wrapper'),
model: new ModuleModel({ model: new Backbone.Model({
id: '${context_course.location}' id: '${locator}'
}), }),
mast: $('.wrapper-mast') mast: $('.wrapper-mast')
}); });
...@@ -61,8 +61,8 @@ require(["coffee/src/views/tabs", "coffee/src/models/module"], function(TabsEdit ...@@ -61,8 +61,8 @@ require(["coffee/src/views/tabs", "coffee/src/models/module"], function(TabsEdit
<div class="tab-list"> <div class="tab-list">
<ol class='components'> <ol class='components'>
% for id, update_url in components: % for id, locator in components:
<li class="component" data-id="${id}" data-update_url="${update_url}"/> <li class="component" data-id="${id}" data-locator="${locator}"/>
% endfor % endfor
<li class="new-component-item"> <li class="new-component-item">
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
<div class="main-wrapper"> <div class="main-wrapper">
<div class="inner-wrapper"> <div class="inner-wrapper">
<div class="main-column"> <div class="main-column">
<article class="subsection-body window" data-id="${subsection.location}"> <article class="subsection-body window" data-locator="${locator}">
<div class="subsection-name-input"> <div class="subsection-name-input">
<label>${_("Display Name:")}</label> <label>${_("Display Name:")}</label>
<input type="text" value="${subsection.display_name_with_default | h}" class="subsection-display-name-input" data-metadata-name="display_name"/> <input type="text" value="${subsection.display_name_with_default | h}" class="subsection-display-name-input" data-metadata-name="display_name"/>
......
...@@ -39,7 +39,7 @@ require(["domReady!", "jquery", "js/models/location", "js/models/section", "js/v ...@@ -39,7 +39,7 @@ require(["domReady!", "jquery", "js/models/location", "js/models/section", "js/v
$(".section-name").each(function() { $(".section-name").each(function() {
var model = new SectionModel({ var model = new SectionModel({
id: $(this).parent(".item-details").data("id"), id: $(this).parent(".item-details").data("locator"),
name: $(this).data("name") name: $(this).data("name")
}); });
new SectionShowView({model: model, el: this}).render(); new SectionShowView({model: model, el: this}).render();
...@@ -57,7 +57,7 @@ require(["domReady!", "jquery", "js/models/location", "js/models/section", "js/v ...@@ -57,7 +57,7 @@ require(["domReady!", "jquery", "js/models/location", "js/models/section", "js/v
<h3 class="section-name"> <h3 class="section-name">
<form class="section-name-form"> <form class="section-name-form">
<input type="text" value="${_('New Section Name')}" class="new-section-name" /> <input type="text" value="${_('New Section Name')}" class="new-section-name" />
<input type="submit" class="new-section-name-save" data-parent="${parent_location}" <input type="submit" class="new-section-name-save" data-parent="${parent_locator}"
data-category="${new_section_category}" value="${_('Save')}" /> data-category="${new_section_category}" value="${_('Save')}" />
<input type="button" class="new-section-name-cancel" value="${_('Cancel')}" /></h3> <input type="button" class="new-section-name-cancel" value="${_('Cancel')}" /></h3>
</form> </form>
...@@ -75,7 +75,7 @@ require(["domReady!", "jquery", "js/models/location", "js/models/section", "js/v ...@@ -75,7 +75,7 @@ require(["domReady!", "jquery", "js/models/location", "js/models/section", "js/v
<span class="section-name-span">Click here to set the section name</span> <span class="section-name-span">Click here to set the section name</span>
<form class="section-name-form"> <form class="section-name-form">
<input type="text" value="${_('New Section Name')}" class="new-section-name" /> <input type="text" value="${_('New Section Name')}" class="new-section-name" />
<input type="submit" class="new-section-name-save" data-parent="${parent_location}" <input type="submit" class="new-section-name-save" data-parent="${parent_locator}"
data-category="${new_section_category}" value="${_('Save')}" /> data-category="${new_section_category}" value="${_('Save')}" />
<input type="button" class="new-section-name-cancel" value="$(_('Cancel')}" /></h3> <input type="button" class="new-section-name-cancel" value="$(_('Cancel')}" /></h3>
</form> </form>
...@@ -140,22 +140,26 @@ require(["domReady!", "jquery", "js/models/location", "js/models/section", "js/v ...@@ -140,22 +140,26 @@ require(["domReady!", "jquery", "js/models/location", "js/models/section", "js/v
<div class="inner-wrapper"> <div class="inner-wrapper">
<div class="wrapper-dnd"> <div class="wrapper-dnd">
<article class="courseware-overview" data-id="${context_course.location.url()}"> <%
course_locator = loc_mapper().translate_location(
context_course.location.course_id, context_course.location, False, True
)
%>
<article class="courseware-overview" data-locator="${course_locator}">
% for section in sections: % for section in sections:
<% <%
section_update_url = loc_mapper().translate_location( section_locator = loc_mapper().translate_location(
context_course.location.course_id, section.location, False, True context_course.location.course_id, section.location, False, True
).url_reverse('xblock') )
%> %>
<section class="courseware-section branch is-draggable" data-id="${section.location}" <section class="courseware-section branch is-draggable" data-parent="${course_locator}"
data-parent-id="${context_course.location.url()}" data-update_url="${section_update_url}"> data-locator="${section_locator}">
<%include file="widgets/_ui-dnd-indicator-before.html" /> <%include file="widgets/_ui-dnd-indicator-before.html" />
<header> <header>
<a href="#" data-tooltip="${_('Expand/collapse this section')}" class="expand-collapse-icon collapse"></a> <a href="#" data-tooltip="${_('Expand/collapse this section')}" class="expand-collapse-icon collapse"></a>
<div class="item-details" data-id="${section.location}"> <div class="item-details" data-locator="${section_locator}">
<h3 class="section-name" data-name="${section.display_name_with_default | h}"></h3> <h3 class="section-name" data-name="${section.display_name_with_default | h}"></h3>
<div class="section-published-date"> <div class="section-published-date">
<% <%
...@@ -168,12 +172,12 @@ require(["domReady!", "jquery", "js/models/location", "js/models/section", "js/v ...@@ -168,12 +172,12 @@ require(["domReady!", "jquery", "js/models/location", "js/models/section", "js/v
%> %>
%if section.start is None: %if section.start is None:
<span class="published-status">${_("This section has not been released.")}</span> <span class="published-status">${_("This section has not been released.")}</span>
<a href="#" class="schedule-button" data-date="" data-time="" data-id="${section.location}">${_("Schedule")}</a> <a href="#" class="schedule-button" data-date="" data-time="" data-locator="${section_locator}">${_("Schedule")}</a>
%else: %else:
<span class="published-status"><strong>${_("Will Release:")}</strong> <span class="published-status"><strong>${_("Will Release:")}</strong>
${date_utils.get_default_time_display(section.start)}</span> ${date_utils.get_default_time_display(section.start)}</span>
<a href="#" class="edit-button" data-date="${start_date_str}" <a href="#" class="edit-button" data-date="${start_date_str}"
data-time="${start_time_str}" data-id="${section.location}">${_("Edit")}</a> data-time="${start_time_str}" data-locator="${section_locator}">${_("Edit")}</a>
%endif %endif
</div> </div>
</div> </div>
...@@ -189,15 +193,15 @@ require(["domReady!", "jquery", "js/models/location", "js/models/section", "js/v ...@@ -189,15 +193,15 @@ require(["domReady!", "jquery", "js/models/location", "js/models/section", "js/v
<span class="new-folder-icon"></span>${_("New Subsection")} <span class="new-folder-icon"></span>${_("New Subsection")}
</a> </a>
</div> </div>
<ol class="sortable-subsection-list" data-id="${section.location.url()}"> <ol class="sortable-subsection-list">
% for subsection in section.get_children(): % for subsection in section.get_children():
<% <%
subsection_update_url = loc_mapper().translate_location( subsection_locator = loc_mapper().translate_location(
context_course.location.course_id, subsection.location, False, True context_course.location.course_id, subsection.location, False, True
).url_reverse('xblock') )
%> %>
<li class="courseware-subsection branch collapsed id-holder is-draggable" data-id="${subsection.location}" <li class="courseware-subsection branch collapsed id-holder is-draggable" data-id="${subsection.location}"
data-parent-id="${section.location.url()}" data-update_url="${subsection_update_url}"> data-parent="${section_locator}" data-locator="${subsection_locator}">
<%include file="widgets/_ui-dnd-indicator-before.html" /> <%include file="widgets/_ui-dnd-indicator-before.html" />
......
...@@ -24,7 +24,6 @@ CMS.URL.LMS_BASE = "${settings.LMS_BASE}" ...@@ -24,7 +24,6 @@ CMS.URL.LMS_BASE = "${settings.LMS_BASE}"
require(["js/models/section", "js/collections/textbook", "js/views/list_textbooks"], require(["js/models/section", "js/collections/textbook", "js/views/list_textbooks"],
function(Section, TextbookCollection, ListTextbooksView) { function(Section, TextbookCollection, ListTextbooksView) {
window.section = new Section({ window.section = new Section({
id: "${course.id}",
name: "${course.display_name_with_default | h}", name: "${course.display_name_with_default | h}",
url_name: "${course.location.name | h}", url_name: "${course.location.name | h}",
org: "${course.location.org | h}", org: "${course.location.org | h}",
......
...@@ -10,9 +10,9 @@ from xmodule.modulestore.django import loc_mapper ...@@ -10,9 +10,9 @@ from xmodule.modulestore.django import loc_mapper
<%block name="jsextra"> <%block name="jsextra">
<script type='text/javascript'> <script type='text/javascript'>
require(["domReady!", "jquery", "coffee/src/models/module", "coffee/src/views/unit", "jquery.ui"], require(["domReady!", "jquery", "js/models/module_info", "coffee/src/views/unit", "jquery.ui"],
function(doc, $, ModuleModel, UnitEditView, ui) { function(doc, $, ModuleModel, UnitEditView, ui) {
window.unit_location_analytics = '${unit_location}'; window.unit_location_analytics = '${unit_locator}';
// tabs // tabs
$('.tab-group').tabs(); $('.tab-group').tabs();
...@@ -20,7 +20,7 @@ require(["domReady!", "jquery", "coffee/src/models/module", "coffee/src/views/un ...@@ -20,7 +20,7 @@ require(["domReady!", "jquery", "coffee/src/models/module", "coffee/src/views/un
new UnitEditView({ new UnitEditView({
el: $('.main-wrapper'), el: $('.main-wrapper'),
model: new ModuleModel({ model: new ModuleModel({
id: '${unit_location}', id: '${unit_locator}',
state: '${unit_state}' state: '${unit_state}'
}) })
}); });
...@@ -34,7 +34,7 @@ require(["domReady!", "jquery", "coffee/src/models/module", "coffee/src/views/un ...@@ -34,7 +34,7 @@ require(["domReady!", "jquery", "coffee/src/models/module", "coffee/src/views/un
</%block> </%block>
<%block name="content"> <%block name="content">
<div class="main-wrapper edit-state-${unit_state}" data-id="${unit_location}" data-update_url="${unit_update_url}"> <div class="main-wrapper edit-state-${unit_state}" data-id="${unit_location}" data-locator="${unit_locator}">
<div class="inner-wrapper"> <div class="inner-wrapper">
<div class="alert editing-draft-alert"> <div class="alert editing-draft-alert">
<p class="alert-message"><strong>${_("You are editing a draft.")}</strong> <p class="alert-message"><strong>${_("You are editing a draft.")}</strong>
...@@ -48,8 +48,8 @@ require(["domReady!", "jquery", "coffee/src/models/module", "coffee/src/views/un ...@@ -48,8 +48,8 @@ require(["domReady!", "jquery", "coffee/src/models/module", "coffee/src/views/un
<article class="unit-body window"> <article class="unit-body window">
<p class="unit-name-input"><label>${_("Display Name:")}</label><input type="text" value="${unit.display_name_with_default | h}" class="unit-display-name-input" /></p> <p class="unit-name-input"><label>${_("Display Name:")}</label><input type="text" value="${unit.display_name_with_default | h}" class="unit-display-name-input" /></p>
<ol class="components"> <ol class="components">
% for id, component_update_url in components: % for id, locator in components:
<li class="component" data-id="${id}" data-update_url="${component_update_url}"/> <li class="component" data-id="${id}" data-locator="${locator}"/>
% endfor % endfor
<li class="new-component-item adding"> <li class="new-component-item adding">
<div class="new-component"> <div class="new-component">
...@@ -141,7 +141,7 @@ require(["domReady!", "jquery", "coffee/src/models/module", "coffee/src/views/un ...@@ -141,7 +141,7 @@ require(["domReady!", "jquery", "coffee/src/models/module", "coffee/src/views/un
<div class="window-contents"> <div class="window-contents">
<div class="row visibility"> <div class="row visibility">
<label class="inline-label">${_("Visibility:")}</label> <label class="inline-label">${_("Visibility:")}</label>
<select class='visibility-select'> <select name="visibility-select" class='visibility-select'>
<option value="public">${_("Public")}</option> <option value="public">${_("Public")}</option>
<option value="private">${_("Private")}</option> <option value="private">${_("Private")}</option>
</select> </select>
......
...@@ -11,12 +11,15 @@ This def will enumerate through a passed in subsection and list all of the units ...@@ -11,12 +11,15 @@ This def will enumerate through a passed in subsection and list all of the units
if subsection_units is None: if subsection_units is None:
subsection_units = subsection.get_children() subsection_units = subsection.get_children()
%> %>
<%
subsection_locator = loc_mapper().translate_location(context_course.location.course_id, subsection.location, False, True)
%>
% for unit in subsection_units: % for unit in subsection_units:
<% <%
unit_update_url = loc_mapper().translate_location(context_course.location.course_id, unit.location, False, True).url_reverse('xblock') unit_locator = loc_mapper().translate_location(context_course.location.course_id, unit.location, False, True)
%> %>
<li class="courseware-unit leaf unit is-draggable" data-id="${unit.location}" data-parent-id="${subsection.location.url()}" <li class="courseware-unit leaf unit is-draggable" data-locator="${unit_locator}"
data-update_url="${unit_update_url}" > data-parent="${subsection_locator}">
<%include file="_ui-dnd-indicator-before.html" /> <%include file="_ui-dnd-indicator-before.html" />
...@@ -34,8 +37,7 @@ This def will enumerate through a passed in subsection and list all of the units ...@@ -34,8 +37,7 @@ This def will enumerate through a passed in subsection and list all of the units
</a> </a>
% if actions: % if actions:
<div class="item-actions"> <div class="item-actions">
<a href="#" data-tooltip="Delete this unit" class="delete-button" data-id="${unit.location}" <a href="#" data-tooltip="Delete this unit" class="delete-button" data-locator="${unit_locator}">
data-update_url="${unit_update_url}">
<span class="delete-icon"></span></a> <span class="delete-icon"></span></a>
<span data-tooltip="Drag to sort" class="drag-handle unit-drag-handle"></span> <span data-tooltip="Drag to sort" class="drag-handle unit-drag-handle"></span>
</div> </div>
...@@ -48,7 +50,7 @@ This def will enumerate through a passed in subsection and list all of the units ...@@ -48,7 +50,7 @@ This def will enumerate through a passed in subsection and list all of the units
<li> <li>
<%include file="_ui-dnd-indicator-initial.html" /> <%include file="_ui-dnd-indicator-initial.html" />
<a href="#" class="new-unit-item" data-category="${new_unit_category}" data-parent="${subsection.location}"> <a href="#" class="new-unit-item" data-category="${new_unit_category}" data-parent="${subsection_locator}">
<span class="new-unit-icon"></span>New Unit <span class="new-unit-icon"></span>New Unit
</a> </a>
</li> </li>
......
...@@ -15,8 +15,6 @@ urlpatterns = patterns('', # nopep8 ...@@ -15,8 +15,6 @@ urlpatterns = patterns('', # nopep8
url(r'^edit/(?P<location>.*?)$', 'contentstore.views.edit_unit', name='edit_unit'), url(r'^edit/(?P<location>.*?)$', 'contentstore.views.edit_unit', name='edit_unit'),
url(r'^subsection/(?P<location>.*?)$', 'contentstore.views.edit_subsection', name='edit_subsection'), url(r'^subsection/(?P<location>.*?)$', 'contentstore.views.edit_subsection', name='edit_subsection'),
url(r'^preview_component/(?P<location>.*?)$', 'contentstore.views.preview_component', name='preview_component'), url(r'^preview_component/(?P<location>.*?)$', 'contentstore.views.preview_component', name='preview_component'),
url(r'^save_item$', 'contentstore.views.save_item', name='save_item'),
url(r'^create_item$', 'contentstore.views.create_item', name='create_item'),
url(r'^transcripts/upload$', 'contentstore.views.upload_transcripts', name='upload_transcripts'), url(r'^transcripts/upload$', 'contentstore.views.upload_transcripts', name='upload_transcripts'),
url(r'^transcripts/download$', 'contentstore.views.download_transcripts', name='download_transcripts'), url(r'^transcripts/download$', 'contentstore.views.download_transcripts', name='download_transcripts'),
...@@ -115,7 +113,7 @@ urlpatterns += patterns( ...@@ -115,7 +113,7 @@ urlpatterns += patterns(
url(r'(?ix)^import/{}$'.format(parsers.URL_RE_SOURCE), 'import_handler'), url(r'(?ix)^import/{}$'.format(parsers.URL_RE_SOURCE), 'import_handler'),
url(r'(?ix)^import_status/{}/(?P<filename>.+)$'.format(parsers.URL_RE_SOURCE), 'import_status_handler'), url(r'(?ix)^import_status/{}/(?P<filename>.+)$'.format(parsers.URL_RE_SOURCE), 'import_status_handler'),
url(r'(?ix)^export/{}$'.format(parsers.URL_RE_SOURCE), 'export_handler'), url(r'(?ix)^export/{}$'.format(parsers.URL_RE_SOURCE), 'export_handler'),
url(r'(?ix)^xblock/{}$'.format(parsers.URL_RE_SOURCE), 'xblock_handler'), url(r'(?ix)^xblock($|/){}$'.format(parsers.URL_RE_SOURCE), 'xblock_handler'),
) )
js_info_dict = { js_info_dict = {
......
...@@ -31,7 +31,7 @@ REQUIREJS_WAIT = { ...@@ -31,7 +31,7 @@ REQUIREJS_WAIT = {
# Individual Unit (editing) # Individual Unit (editing)
re.compile('^Individual Unit \|'): [ re.compile('^Individual Unit \|'): [
"js/base", "coffee/src/models/module", "coffee/src/views/unit", "js/base", "coffee/src/views/unit",
"coffee/src/views/module_edit"], "coffee/src/views/module_edit"],
# Content - Outline # Content - Outline
......
...@@ -184,12 +184,21 @@ class DraftModuleStore(MongoModuleStore): ...@@ -184,12 +184,21 @@ class DraftModuleStore(MongoModuleStore):
location: Something that can be passed to Location location: Something that can be passed to Location
children: A list of child item identifiers children: A list of child item identifiers
""" """
# We expect the children IDs to always be the non-draft version. With view refactoring
# for split, we are now passing the draft version in some cases.
children_ids = [
Location(child).replace(revision=None).url()
for child
in children
]
draft_loc = as_draft(location) draft_loc = as_draft(location)
draft_item = self.get_item(location) draft_item = self.get_item(location)
if not getattr(draft_item, 'is_draft', False): if not getattr(draft_item, 'is_draft', False):
self.convert_to_draft(as_published(location)) self.convert_to_draft(as_published(location))
return super(DraftModuleStore, self).update_children(draft_loc, children) return super(DraftModuleStore, self).update_children(draft_loc, children_ids)
def update_metadata(self, location, metadata): def update_metadata(self, location, metadata):
""" """
......
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