diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py index 18afd331d0..508236a1e9 100644 --- a/cms/djangoapps/contentstore/utils.py +++ b/cms/djangoapps/contentstore/utils.py @@ -33,6 +33,31 @@ def get_course_location_for_item(location): return location +def get_course_for_item(location): + ''' + cdodge: for a given Xmodule, return the course that it belongs to + NOTE: This makes a lot of assumptions about the format of the course location + Also we have to assert that this module maps to only one course item - it'll throw an + assert if not + ''' + item_loc = Location(location) + + # @hack! We need to find the course location however, we don't + # know the 'name' parameter in this context, so we have + # to assume there's only one item in this query even though we are not specifying a name + course_search_location = ['i4x', item_loc.org, item_loc.course, 'course', None] + courses = modulestore().get_items(course_search_location) + + # make sure we found exactly one match on this above course search + found_cnt = len(courses) + if found_cnt == 0: + raise BaseException('Could not find course at {0}'.format(course_search_location)) + + if found_cnt > 1: + raise BaseException('Found more than one course at {0}. There should only be one!!! Dump = {1}'.format(course_search_location, courses)) + + return courses[0] + def get_lms_link_for_item(location, preview=False): location = Location(location) diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py index 5495b5ea0b..61cd6cde01 100644 --- a/cms/djangoapps/contentstore/views.py +++ b/cms/djangoapps/contentstore/views.py @@ -55,7 +55,7 @@ from cache_toolbox.core import set_cached_content, get_cached_content, del_cache from auth.authz import is_user_in_course_group_role, get_users_in_course_group_by_role from auth.authz import get_user_by_email, add_user_to_course_group, remove_user_from_course_group from auth.authz import INSTRUCTOR_ROLE_NAME, STAFF_ROLE_NAME, create_all_course_groups -from .utils import get_course_location_for_item, get_lms_link_for_item, compute_unit_state, get_date_display, UnitState +from .utils import get_course_location_for_item, get_lms_link_for_item, compute_unit_state, get_date_display, UnitState, get_course_for_item from xmodule.templates import all_templates from xmodule.modulestore.xml_importer import import_from_xml @@ -68,6 +68,9 @@ COMPONENT_TYPES = ['customtag', 'discussion', 'html', 'problem', 'video'] DIRECT_ONLY_CATEGORIES = ['course', 'chapter', 'sequential', 'about', 'static_tab', 'course_info'] +# cdodge: these are categories which should not be parented, they are detached from the hierarchy +DETACHED_CATEGORIES = ['about', 'static_tab', 'course_info'] + def _modulestore(location): """ @@ -616,6 +619,17 @@ def save_item(request): # commit to datastore store.update_metadata(item_location, existing_item.metadata) + # cdodge: special case logic for updating static_tabs + # unfortunately tabs are enumerated in the course policy data structure, so if we change the display name of + # the tab, we need to update the course policy (which has a nice .tabs property on it) + item_loc = Location(item_location) + if item_loc.category == 'static_tab': + # VS[compat] Rework when we can stop having to support tabs lists in the policy + tag_module = store.get_item(item_location) + course = get_course_for_item(item_location) + course.update_tab_reference(tag_module) + modulestore('direct').update_metadata(course.location, course.metadata) + return HttpResponse() @@ -689,7 +703,16 @@ def clone_item(request): new_item.metadata['display_name'] = display_name _modulestore(template).update_metadata(new_item.location.url(), new_item.own_metadata) - _modulestore(parent.location).update_children(parent_location, parent.definition.get('children', []) + [new_item.location.url()]) + + if new_item.location.category not in DETACHED_CATEGORIES: + _modulestore(parent.location).update_children(parent_location, parent.definition.get('children', []) + [new_item.location.url()]) + elif new_item.location.category == 'static_tab': + # static tabs - in our data model - are described in the course policy + # VS[compat]: Rework when we can stop having to support tabs lists in the policy + if parent.location.category != 'course': + raise BaseException('adding a new static_tab must be on a course object') + parent.add_tab_reference(new_item) + _modulestore(parent.location).update_metadata(parent.location.url(), parent.metadata) return HttpResponse(json.dumps({'id': dest_location.url()})) @@ -877,7 +900,7 @@ def edit_tabs(request, org, course, coursename): static_tabs = modulestore('direct').get_items(static_tabs_loc) - # import pudb; pudb.set_trace() + logging.debug('tabs in policy = %s', course_item.tabs) components = [ static_tab.location.url() @@ -995,6 +1018,17 @@ def create_new_course(request): # set a default start date to now new_course.metadata['start'] = stringify_time(time.gmtime()) + # set up the default tabs + # I've added this because when we add static tabs, the LMS either expects a None for the tabs list or + # at least a list populated with the minimal times + # @TODO: I don't like the fact that the presentation tier is away of these data related constraints, let's find a better + # place for this. Also rather than using a simple list of dictionaries a nice class model would be helpful here + new_course.tabs = [{"type": "courseware"}, + {"type": "course_info", "name": "Course Info"}, + {"type": "discussion", "name": "Discussion"}, + {"type": "wiki", "name": "Wiki"}, + {"type": "progress", "name": "Progress"}] + modulestore('direct').update_metadata(new_course.location.url(), new_course.own_metadata) create_all_course_groups(request.user, new_course.location) diff --git a/cms/static/coffee/src/views/tabs.coffee b/cms/static/coffee/src/views/tabs.coffee index 9797b9495b..34d86a3051 100644 --- a/cms/static/coffee/src/views/tabs.coffee +++ b/cms/static/coffee/src/views/tabs.coffee @@ -15,7 +15,7 @@ class CMS.Views.TabsEdit extends Backbone.View @$('.components').sortable( handle: '.drag-handle' - update: (event, ui) => alert 'got it!' + update: (event, ui) => alert 'not yet implemented!' helper: 'clone' opacity: '0.5' placeholder: 'component-placeholder' diff --git a/cms/templates/edit-tabs.html b/cms/templates/edit-tabs.html index 7c4fc57ec2..94c5e38260 100644 --- a/cms/templates/edit-tabs.html +++ b/cms/templates/edit-tabs.html @@ -17,7 +17,9 @@ <%block name="content">