diff --git a/cms/djangoapps/contentstore/features/section.feature b/cms/djangoapps/contentstore/features/section.feature
index ad00ba2911..75e7a4af10 100644
--- a/cms/djangoapps/contentstore/features/section.feature
+++ b/cms/djangoapps/contentstore/features/section.feature
@@ -11,6 +11,14 @@ Feature: Create Section
And I see a release date for my section
And I see a link to create a new subsection
+ Scenario: Add a new section (with a quote in the name) to a course (bug #216)
+ Given I have opened a new course in Studio
+ When I click the New Section link
+ And I enter a section name with a quote and click save
+ Then I see my section name with a quote on the Courseware page
+ And I click to edit the section name
+ Then I see the complete section name with a quote in the editor
+
Scenario: Edit section release date
Given I have opened a new course in Studio
And I have added a new section
diff --git a/cms/djangoapps/contentstore/features/section.py b/cms/djangoapps/contentstore/features/section.py
index ca67c477fb..cfa4e4bb52 100644
--- a/cms/djangoapps/contentstore/features/section.py
+++ b/cms/djangoapps/contentstore/features/section.py
@@ -1,5 +1,6 @@
from lettuce import world, step
from common import *
+from nose.tools import assert_equal
############### ACTIONS ####################
@@ -12,10 +13,12 @@ def i_click_new_section_link(step):
@step('I enter the section name and click save$')
def i_save_section_name(step):
- name_css = '.new-section-name'
- save_css = '.new-section-name-save'
- css_fill(name_css, 'My Section')
- css_click(save_css)
+ save_section_name('My Section')
+
+
+@step('I enter a section name with a quote and click save$')
+def i_save_section_name_with_quote(step):
+ save_section_name('Section with "Quote"')
@step('I have added a new section$')
@@ -45,8 +48,24 @@ def i_save_a_new_section_release_date(step):
@step('I see my section on the Courseware page$')
def i_see_my_section_on_the_courseware_page(step):
- section_css = 'span.section-name-span'
- assert_css_with_text(section_css, 'My Section')
+ see_my_section_on_the_courseware_page('My Section')
+
+
+@step('I see my section name with a quote on the Courseware page$')
+def i_see_my_section_name_with_quote_on_the_courseware_page(step):
+ see_my_section_on_the_courseware_page('Section with "Quote"')
+
+
+@step('I click to edit the section name$')
+def i_click_to_edit_section_name(step):
+ css_click('span.section-name-span')
+
+
+@step('I see the complete section name with a quote in the editor$')
+def i_see_complete_section_name_with_quote_in_editor(step):
+ css = '.edit-section-name'
+ assert world.browser.is_element_present_by_css(css, 5)
+ assert_equal(world.browser.find_by_css(css).value, 'Section with "Quote"')
@step('the section does not exist$')
@@ -88,3 +107,17 @@ def the_section_release_date_is_updated(step):
css = 'span.published-status'
status_text = world.browser.find_by_css(css).text
assert status_text == 'Will Release: 12/25/2013 at 12:00am'
+
+
+############ HELPER METHODS ###################
+
+def save_section_name(name):
+ name_css = '.new-section-name'
+ save_css = '.new-section-name-save'
+ css_fill(name_css, name)
+ css_click(save_css)
+
+
+def see_my_section_on_the_courseware_page(name):
+ section_css = 'span.section-name-span'
+ assert_css_with_text(section_css, name)
\ No newline at end of file
diff --git a/cms/djangoapps/contentstore/features/subsection.feature b/cms/djangoapps/contentstore/features/subsection.feature
index 5acb5bfe44..4b5f5b869d 100644
--- a/cms/djangoapps/contentstore/features/subsection.feature
+++ b/cms/djangoapps/contentstore/features/subsection.feature
@@ -9,6 +9,14 @@ Feature: Create Subsection
And I enter the subsection name and click save
Then I see my subsection on the Courseware page
+ Scenario: Add a new subsection (with a name containing a quote) to a section (bug #216)
+ Given I have opened a new course section in Studio
+ When I click the New Subsection link
+ And I enter a subsection name with a quote and click save
+ Then I see my subsection name with a quote on the Courseware page
+ And I click to edit the subsection name
+ Then I see the complete subsection name with a quote in the editor
+
Scenario: Delete a subsection
Given I have opened a new course section in Studio
And I have added a new subsection
diff --git a/cms/djangoapps/contentstore/features/subsection.py b/cms/djangoapps/contentstore/features/subsection.py
index e2041b8dbf..88e1424898 100644
--- a/cms/djangoapps/contentstore/features/subsection.py
+++ b/cms/djangoapps/contentstore/features/subsection.py
@@ -1,5 +1,6 @@
from lettuce import world, step
from common import *
+from nose.tools import assert_equal
############### ACTIONS ####################
@@ -20,28 +21,60 @@ def i_click_the_new_subsection_link(step):
@step('I enter the subsection name and click save$')
def i_save_subsection_name(step):
- name_css = 'input.new-subsection-name-input'
- save_css = 'input.new-subsection-name-save'
- css_fill(name_css, 'Subsection One')
- css_click(save_css)
+ save_subsection_name('Subsection One')
+
+
+@step('I enter a subsection name with a quote and click save$')
+def i_save_subsection_name_with_quote(step):
+ save_subsection_name('Subsection With "Quote"')
+
+
+@step('I click to edit the subsection name$')
+def i_click_to_edit_subsection_name(step):
+ css_click('span.subsection-name-value')
+
+
+@step('I see the complete subsection name with a quote in the editor$')
+def i_see_complete_subsection_name_with_quote_in_editor(step):
+ css = '.subsection-display-name-input'
+ assert world.browser.is_element_present_by_css(css, 5)
+ assert_equal(world.browser.find_by_css(css).value, 'Subsection With "Quote"')
@step('I have added a new subsection$')
def i_have_added_a_new_subsection(step):
add_subsection()
+
############ ASSERTIONS ###################
@step('I see my subsection on the Courseware page$')
def i_see_my_subsection_on_the_courseware_page(step):
- css = 'span.subsection-name'
- assert world.browser.is_element_present_by_css(css)
- css = 'span.subsection-name-value'
- assert_css_with_text(css, 'Subsection One')
+ see_subsection_name('Subsection One')
+
+
+@step('I see my subsection name with a quote on the Courseware page$')
+def i_see_my_subsection_name_with_quote_on_the_courseware_page(step):
+ see_subsection_name('Subsection With "Quote"')
@step('the subsection does not exist$')
def the_subsection_does_not_exist(step):
css = 'span.subsection-name'
assert world.browser.is_element_not_present_by_css(css)
+
+
+############ HELPER METHODS ###################
+
+def save_subsection_name(name):
+ name_css = 'input.new-subsection-name-input'
+ save_css = 'input.new-subsection-name-save'
+ css_fill(name_css, name)
+ css_click(save_css)
+
+def see_subsection_name(name):
+ css = 'span.subsection-name'
+ assert world.browser.is_element_present_by_css(css)
+ css = 'span.subsection-name-value'
+ assert_css_with_text(css, name)
diff --git a/cms/djangoapps/contentstore/tests/factories.py b/cms/djangoapps/contentstore/tests/factories.py
deleted file mode 100644
index d15610f11c..0000000000
--- a/cms/djangoapps/contentstore/tests/factories.py
+++ /dev/null
@@ -1,49 +0,0 @@
-from factory import Factory
-from datetime import datetime
-from uuid import uuid4
-from student.models import (User, UserProfile, Registration,
- CourseEnrollmentAllowed)
-from django.contrib.auth.models import Group
-
-
-class UserProfileFactory(Factory):
- FACTORY_FOR = UserProfile
-
- user = None
- name = 'Robot Studio'
- courseware = 'course.xml'
-
-
-class RegistrationFactory(Factory):
- FACTORY_FOR = Registration
-
- user = None
- activation_key = uuid4().hex
-
-
-class UserFactory(Factory):
- FACTORY_FOR = User
-
- username = 'robot'
- email = 'robot@edx.org'
- password = 'test'
- first_name = 'Robot'
- last_name = 'Tester'
- is_staff = False
- is_active = True
- is_superuser = False
- last_login = datetime.now()
- date_joined = datetime.now()
-
-
-class GroupFactory(Factory):
- FACTORY_FOR = Group
-
- name = 'test_group'
-
-
-class CourseEnrollmentAllowedFactory(Factory):
- FACTORY_FOR = CourseEnrollmentAllowed
-
- email = 'test@edx.org'
- course_id = 'edX/test/2012_Fall'
diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py
index 00b8b6cd4f..df40a1972d 100644
--- a/cms/djangoapps/contentstore/views.py
+++ b/cms/djangoapps/contentstore/views.py
@@ -68,6 +68,10 @@ log = logging.getLogger(__name__)
COMPONENT_TYPES = ['customtag', 'discussion', 'html', 'problem', 'video']
+ADVANCED_COMPONENT_TYPES = ['annotatable','combinedopenended', 'peergrading']
+ADVANCED_COMPONENT_CATEGORY = 'advanced'
+ADVANCED_COMPONENT_POLICY_KEY = 'advanced_modules'
+
# cdodge: these are categories which should not be parented, they are detached from the hierarchy
DETACHED_CATEGORIES = ['about', 'static_tab', 'course_info']
@@ -284,10 +288,31 @@ def edit_unit(request, location):
component_templates = defaultdict(list)
+ # Check if there are any advanced modules specified in the course policy. These modules
+ # should be specified as a list of strings, where the strings are the names of the modules
+ # in ADVANCED_COMPONENT_TYPES that should be enabled for the course.
+ course_metadata = CourseMetadata.fetch(course.location)
+ course_advanced_keys = course_metadata.get(ADVANCED_COMPONENT_POLICY_KEY, [])
+
+ # Set component types according to course policy file
+ component_types = list(COMPONENT_TYPES)
+ if isinstance(course_advanced_keys, list):
+ course_advanced_keys = [c for c in course_advanced_keys if c in ADVANCED_COMPONENT_TYPES]
+ if len(course_advanced_keys) > 0:
+ component_types.append(ADVANCED_COMPONENT_CATEGORY)
+ else:
+ log.error("Improper format for course advanced keys! {0}".format(course_advanced_keys))
+
templates = modulestore().get_items(Location('i4x', 'edx', 'templates'))
for template in templates:
- if template.location.category in COMPONENT_TYPES:
- component_templates[template.location.category].append((
+ category = template.location.category
+
+ if category in course_advanced_keys:
+ category = ADVANCED_COMPONENT_CATEGORY
+
+ if category in component_types:
+ #This is a hack to create categories for different xmodules
+ component_templates[category].append((
template.display_name,
template.location.url(),
'markdown' in template.metadata,
diff --git a/cms/envs/test.py b/cms/envs/test.py
index 7f39e6818b..abe03edd41 100644
--- a/cms/envs/test.py
+++ b/cms/envs/test.py
@@ -27,6 +27,9 @@ STATIC_ROOT = TEST_ROOT / "staticfiles"
GITHUB_REPO_ROOT = TEST_ROOT / "data"
COMMON_TEST_DATA_ROOT = COMMON_ROOT / "test" / "data"
+# Makes the tests run much faster...
+SOUTH_TESTS_MIGRATE = False # To disable migrations and use syncdb instead
+
# TODO (cpennington): We need to figure out how envs/test.py can inject things into common.py so that we don't have to repeat this sort of thing
STATICFILES_DIRS = [
COMMON_ROOT / "static",
diff --git a/cms/static/img/large-advanced-icon.png b/cms/static/img/large-advanced-icon.png
new file mode 100644
index 0000000000..c6a19ea5a9
Binary files /dev/null and b/cms/static/img/large-advanced-icon.png differ
diff --git a/cms/static/img/large-annotations-icon.png b/cms/static/img/large-annotations-icon.png
new file mode 100644
index 0000000000..249193521f
Binary files /dev/null and b/cms/static/img/large-annotations-icon.png differ
diff --git a/cms/static/img/large-openended-icon.png b/cms/static/img/large-openended-icon.png
new file mode 100644
index 0000000000..4d31815413
Binary files /dev/null and b/cms/static/img/large-openended-icon.png differ
diff --git a/cms/static/sass/_graphics.scss b/cms/static/sass/_graphics.scss
index 4ed22c570d..300cf3b692 100644
--- a/cms/static/sass/_graphics.scss
+++ b/cms/static/sass/_graphics.scss
@@ -254,6 +254,30 @@
background: url(../img/html-icon.png) center no-repeat;
}
+.large-openended-icon {
+ display: inline-block;
+ width: 100px;
+ height: 60px;
+ margin-right: 5px;
+ background: url(../img/large-openended-icon.png) center no-repeat;
+}
+
+.large-annotations-icon {
+ display: inline-block;
+ width: 100px;
+ height: 60px;
+ margin-right: 5px;
+ background: url(../img/large-annotations-icon.png) center no-repeat;
+}
+
+.large-advanced-icon {
+ display: inline-block;
+ width: 100px;
+ height: 60px;
+ margin-right: 5px;
+ background: url(../img/large-advanced-icon.png) center no-repeat;
+}
+
.large-textbook-icon {
display: inline-block;
width: 100px;
diff --git a/cms/templates/asset_index.html b/cms/templates/asset_index.html
index 5ace98df56..a5a9144b07 100644
--- a/cms/templates/asset_index.html
+++ b/cms/templates/asset_index.html
@@ -28,7 +28,7 @@
{{uploadDate}}