From 7dbabce4db2d1bce7b7f272631c18b02f168092d Mon Sep 17 00:00:00 2001 From: Chris Dodge Date: Thu, 8 Nov 2012 09:55:52 -0500 Subject: [PATCH] fix up styling by reusing existing CSS class names. Also refactor some code regarding the management of static_tabs so that it's in the course as course contains references to static_tabs in the current data model --- cms/djangoapps/contentstore/utils.py | 25 +++++++++++++ cms/djangoapps/contentstore/views.py | 40 +++++++++++++++++++-- cms/static/coffee/src/views/tabs.coffee | 2 +- cms/templates/edit-tabs.html | 4 ++- common/lib/xmodule/xmodule/course_module.py | 27 ++++++++++++++ 5 files changed, 93 insertions(+), 5 deletions(-) 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">
-

Static Tabs

+
+

Static Tabs

+
diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py index 6e3f450324..fe2c6ca87a 100644 --- a/common/lib/xmodule/xmodule/course_module.py +++ b/common/lib/xmodule/xmodule/course_module.py @@ -266,6 +266,33 @@ class CourseDescriptor(SequenceDescriptor): """ return self.metadata.get('tabs') + @tabs.setter + def tabs(self, value): + self.metadata['tabs'] = value + + def add_tab_reference(self, tab_module): + ''' + Since in CMS we can add static tabs via data, the current data model expects that + the list of tabs be encapsulated in the course. + VS[compat] we can rework this to avoid pointers to static tab modules once we fully deprecate filesystem support + ''' + existing_tabs = self.tabs or [] + existing_tabs.append({'type':'static_tab', 'name' : tab_module.metadata.get('display_name'), 'url_slug' : tab_module.location.name}) + self.tabs = existing_tabs + + + def update_tab_reference(self, tab_module): + ''' + Since in CMS we can add static tabs via data, the current data model expects that + the list of tabs be encapsulated in the course. + VS[compat] we can rework this to avoid pointers to static tab modules once we fully deprecate filesystem support + ''' + existing_tabs = self.tabs + for tab in existing_tabs: + if tab.get('url_slug') == tab_module.location.name: + tab['name'] = tab_module.metadata.get('display_name') + self.tabs = existing_tabs + @property def show_calculator(self): return self.metadata.get("show_calculator", None) == "Yes"