Commit 40f152ea by Matt Drayer

Merge pull request #2374 from edx/hotfix/2014-01-30

Hotfix deployed to Edge Production and Production on 2014-01-31, merging PR to release branch.
parents b60487c8 4751322f
...@@ -18,10 +18,9 @@ from xmodule.error_module import ErrorDescriptor ...@@ -18,10 +18,9 @@ from xmodule.error_module import ErrorDescriptor
from xmodule.errortracker import make_error_tracker, exc_info_to_str from xmodule.errortracker import make_error_tracker, exc_info_to_str
from xmodule.course_module import CourseDescriptor from xmodule.course_module import CourseDescriptor
from xmodule.mako_module import MakoDescriptorSystem from xmodule.mako_module import MakoDescriptorSystem
from xmodule.x_module import XMLParsingSystem, prefer_xmodules, policy_key from xmodule.x_module import XMLParsingSystem, policy_key
from xmodule.html_module import HtmlDescriptor from xmodule.html_module import HtmlDescriptor
from xblock.core import XBlock
from xblock.fields import ScopeIds from xblock.fields import ScopeIds
from xblock.field_data import DictFieldData from xblock.field_data import DictFieldData
from xblock.runtime import DictKeyValueStore, IdReader, IdGenerator from xblock.runtime import DictKeyValueStore, IdReader, IdGenerator
...@@ -222,6 +221,7 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem): ...@@ -222,6 +221,7 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem):
# load_item should actually be get_instance, because it expects the course-specific # load_item should actually be get_instance, because it expects the course-specific
# policy to be loaded. For now, just add the course_id here... # policy to be loaded. For now, just add the course_id here...
def load_item(location): def load_item(location):
"""Return the XBlock for the specified location"""
return xmlstore.get_instance(course_id, Location(location)) return xmlstore.get_instance(course_id, Location(location))
resources_fs = OSFS(xmlstore.data_dir / course_dir) resources_fs = OSFS(xmlstore.data_dir / course_dir)
...@@ -509,6 +509,9 @@ class XMLModuleStore(ModuleStoreReadBase): ...@@ -509,6 +509,9 @@ class XMLModuleStore(ModuleStoreReadBase):
course_id = CourseDescriptor.make_id(org, course, url_name) course_id = CourseDescriptor.make_id(org, course, url_name)
def get_policy(usage_id): def get_policy(usage_id):
"""
Return the policy dictionary to be applied to the specified XBlock usage
"""
return policy.get(policy_key(usage_id), {}) return policy.get(policy_key(usage_id), {})
system = ImportSystem( system = ImportSystem(
...@@ -570,9 +573,9 @@ class XMLModuleStore(ModuleStoreReadBase): ...@@ -570,9 +573,9 @@ class XMLModuleStore(ModuleStoreReadBase):
html = f.read().decode('utf-8') html = f.read().decode('utf-8')
# tabs are referenced in policy.json through a 'slug' which is just the filename without the .html suffix # tabs are referenced in policy.json through a 'slug' which is just the filename without the .html suffix
slug = os.path.splitext(os.path.basename(filepath))[0] slug = os.path.splitext(os.path.basename(filepath))[0]
loc = course_descriptor.scope_ids.usage_id._replace(category=category, name=slug) loc = course_descriptor.scope_ids.usage_id.replace(category=category, name=slug)
module = system.construct_xblock_from_class( module = system.construct_xblock(
HtmlDescriptor, category,
# We're loading a descriptor, so student_id is meaningless # We're loading a descriptor, so student_id is meaningless
# We also don't have separate notions of definition and usage ids yet, # We also don't have separate notions of definition and usage ids yet,
# so we use the location for both # so we use the location for both
......
<section class="about">
<h2>About This Course</h2>
<p>Include your long course description here. The long course description should contain 150-400 words. about page 463139</p>
<p>This is paragraph 2 of the long course description. Add more paragraphs as needed. Make sure to enclose them in paragraph tags.</p>
</section>
<section class="prerequisites">
<h2>Prerequisites</h2>
<p>Add information about course prerequisites here.</p>
</section>
<section class="course-staff">
<h2>Course Staff</h2>
<article class="teacher">
<div class="teacher-image">
<img src="/static/images/pl-faculty.png" align="left" style="margin:0 20 px 0" alt="Course Staff Image #1">
</div>
<h3>Staff Member #1</h3>
<p>Biography of instructor/staff member #1</p>
</article>
<article class="teacher">
<div class="teacher-image">
<img src="/static/images/pl-faculty.png" align="left" style="margin:0 20 px 0" alt="Course Staff Image #2">
</div>
<h3>Staff Member #2</h3>
<p>Biography of instructor/staff member #2</p>
</article>
</section>
<section class="faq">
<section class="responses">
<h2>Frequently Asked Questions</h2>
<article class="response">
<h3>Do I need to buy a textbook?</h3>
<p>No, a free online version of Chemistry: Principles, Patterns, and Applications, First Edition by Bruce Averill and Patricia Eldredge will be available, though you can purchase a printed version (published by FlatWorld Knowledge) if you’d like.</p>
</article>
<article class="response">
<h3>Question #2</h3>
<p>Your answer would be displayed here.</p>
</article>
</section>
</section>
<course url_name="2014" org="edX" course="detached_pages"/>
<course display_name="detached_pages" end="2014-01-22T05:00:00Z" start="2013-01-01T00:00:00Z"/>
<ol></ol>
\ No newline at end of file
<ol><li><h2>January 29, 2014</h2>course info 463139</li></ol>
\ No newline at end of file
{"GRADER": [{"short_label": "HW", "min_count": 12, "type": "Homework", "drop_count": 2, "weight": 0.15}, {"min_count": 12, "type": "Lab", "drop_count": 2, "weight": 0.15}, {"short_label": "Midterm", "min_count": 1, "type": "Midterm Exam", "drop_count": 0, "weight": 0.3}, {"short_label": "Final", "min_count": 1, "type": "Final Exam", "drop_count": 0, "weight": 0.4}], "GRADE_CUTOFFS": {"Pass": 0.5}}
\ No newline at end of file
{
"course/2014": {
"discussion_topics": {
"General": {
"id": "i4x-edX-detached_pages-course-2014"
}
},
"display_name": "detached_pages",
"end": "2014-01-22T05:00:00Z",
"start": "2013-01-01T00:00:00Z",
"tabs": [
{
"name": "Courseware",
"type": "courseware"
},
{
"name": "Course Info",
"type": "course_info"
},
{
"name": "Discussion",
"type": "discussion"
},
{
"name": "Wiki",
"type": "wiki"
},
{
"name": "Progress",
"type": "progress"
},
{
"name": "Empty",
"type": "static_tab",
"url_slug": "8e4cce2b4aaf4ba28b1220804619e41f"
}
]
}
}
\ No newline at end of file
{}
\ No newline at end of file
<p>static 463139</p>
\ No newline at end of file
...@@ -374,7 +374,7 @@ def _get_source_address(course_id, course_title): ...@@ -374,7 +374,7 @@ def _get_source_address(course_id, course_title):
invalid_chars = re.compile(r"[^\w.-]") invalid_chars = re.compile(r"[^\w.-]")
course_num = invalid_chars.sub('_', course_num) course_num = invalid_chars.sub('_', course_num)
from_addr = '"{0}" Course Staff <{1}-{2}>'.format(course_title_no_quotes, course_num, settings.BULK_EMAIL_DEFAULT_FROM_EMAIL) from_addr = u'"{0}" Course Staff <{1}-{2}>'.format(course_title_no_quotes, course_num, settings.BULK_EMAIL_DEFAULT_FROM_EMAIL)
return from_addr return from_addr
...@@ -677,5 +677,5 @@ def _statsd_tag(course_title): ...@@ -677,5 +677,5 @@ def _statsd_tag(course_title):
""" """
Calculate the tag we will use for DataDog. Calculate the tag we will use for DataDog.
""" """
tag = "course_email:{0}".format(course_title) tag = u"course_email:{0}".format(course_title)
return tag[:200] return tag[:200]
# -*- coding: utf-8 -*-
""" """
Unit tests for student optouts from course email Unit tests for student optouts from course email
""" """
...@@ -26,7 +27,8 @@ class TestOptoutCourseEmails(ModuleStoreTestCase): ...@@ -26,7 +27,8 @@ class TestOptoutCourseEmails(ModuleStoreTestCase):
""" """
def setUp(self): def setUp(self):
self.course = CourseFactory.create() course_title = u"ẗëṡẗ title イ乇丂イ ᄊ乇丂丂ムg乇 キo尺 ムレレ тэѕт мэѕѕаБэ"
self.course = CourseFactory.create(display_name=course_title)
self.instructor = AdminFactory.create() self.instructor = AdminFactory.create()
self.student = UserFactory.create() self.student = UserFactory.create()
CourseEnrollmentFactory.create(user=self.student, course_id=self.course.id) CourseEnrollmentFactory.create(user=self.student, course_id=self.course.id)
......
...@@ -48,7 +48,8 @@ class TestEmailSendFromDashboard(ModuleStoreTestCase): ...@@ -48,7 +48,8 @@ class TestEmailSendFromDashboard(ModuleStoreTestCase):
@patch.dict(settings.FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True, 'REQUIRE_COURSE_EMAIL_AUTH': False}) @patch.dict(settings.FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True, 'REQUIRE_COURSE_EMAIL_AUTH': False})
def setUp(self): def setUp(self):
self.course = CourseFactory.create() course_title = u"ẗëṡẗ title イ乇丂イ ᄊ乇丂丂ムg乇 キo尺 ムレレ тэѕт мэѕѕаБэ"
self.course = CourseFactory.create(display_name=course_title)
self.instructor = InstructorFactory(course=self.course.location) self.instructor = InstructorFactory(course=self.course.location)
......
# -*- coding: utf-8 -*-
""" """
Unit tests for handling email sending errors Unit tests for handling email sending errors
""" """
...@@ -43,7 +44,8 @@ class TestEmailErrors(ModuleStoreTestCase): ...@@ -43,7 +44,8 @@ class TestEmailErrors(ModuleStoreTestCase):
""" """
def setUp(self): def setUp(self):
self.course = CourseFactory.create() course_title = u"ẗëṡẗ title イ乇丂イ ᄊ乇丂丂ムg乇 キo尺 ムレレ тэѕт мэѕѕаБэ"
self.course = CourseFactory.create(display_name=course_title)
self.instructor = AdminFactory.create() self.instructor = AdminFactory.create()
self.client.login(username=self.instructor.username, password="test") self.client.login(username=self.instructor.username, password="test")
......
# -*- coding: utf-8 -*-
""" """
Unit tests for bulk-email-related forms. Unit tests for bulk-email-related forms.
""" """
...@@ -23,8 +24,8 @@ class CourseAuthorizationFormTest(ModuleStoreTestCase): ...@@ -23,8 +24,8 @@ class CourseAuthorizationFormTest(ModuleStoreTestCase):
"""Test the CourseAuthorizationAdminForm form for Mongo-backed courses.""" """Test the CourseAuthorizationAdminForm form for Mongo-backed courses."""
def setUp(self): def setUp(self):
# Make a mongo course course_title = u"ẗëṡẗ title イ乇丂イ ᄊ乇丂丂ムg乇 キo尺 ムレレ тэѕт мэѕѕаБэ"
self.course = CourseFactory.create() self.course = CourseFactory.create(display_name=course_title)
def tearDown(self): def tearDown(self):
""" """
......
...@@ -25,5 +25,6 @@ MAPPINGS = { ...@@ -25,5 +25,6 @@ MAPPINGS = {
'edX/open_ended/2012_Fall': 'xml', 'edX/open_ended/2012_Fall': 'xml',
'edX/due_date/2013_fall': 'xml', 'edX/due_date/2013_fall': 'xml',
'edX/open_ended_nopath/2012_Fall': 'xml', 'edX/open_ended_nopath/2012_Fall': 'xml',
'edX/detached_pages/2014': 'xml',
} }
TEST_DATA_MIXED_MODULESTORE = mixed_store_config(TEST_DATA_DIR, MAPPINGS) TEST_DATA_MIXED_MODULESTORE = mixed_store_config(TEST_DATA_DIR, MAPPINGS)
""" """
Test the about xblock Test the about xblock
""" """
import mock
from django.test.utils import override_settings from django.test.utils import override_settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
...@@ -18,6 +19,10 @@ class AboutTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase): ...@@ -18,6 +19,10 @@ class AboutTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
category="about", parent_location=self.course.location, category="about", parent_location=self.course.location,
data="OOGIE BLOOGIE", display_name="overview" data="OOGIE BLOOGIE", display_name="overview"
) )
# The following XML course is closed; we're testing that
# an about page still appears when the course is already closed
self.xml_course_id = 'edX/detached_pages/2014'
self.xml_data = "about page 463139"
def test_logged_in(self): def test_logged_in(self):
self.setup_user() self.setup_user()
...@@ -31,3 +36,18 @@ class AboutTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase): ...@@ -31,3 +36,18 @@ class AboutTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
resp = self.client.get(url) resp = self.client.get(url)
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
self.assertIn("OOGIE BLOOGIE", resp.content) self.assertIn("OOGIE BLOOGIE", resp.content)
@mock.patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
def test_logged_in_xml(self):
self.setup_user()
url = reverse('about_course', args=[self.xml_course_id])
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
self.assertIn(self.xml_data, resp.content)
@mock.patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
def test_anonymous_user_xml(self):
url = reverse('about_course', args=[self.xml_course_id])
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
self.assertIn(self.xml_data, resp.content)
""" """
Test the course_info xblock Test the course_info xblock
""" """
import mock
from django.test.utils import override_settings from django.test.utils import override_settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
...@@ -18,6 +19,10 @@ class CourseInfoTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase): ...@@ -18,6 +19,10 @@ class CourseInfoTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
category="course_info", parent_location=self.course.location, category="course_info", parent_location=self.course.location,
data="OOGIE BLOOGIE", display_name="updates" data="OOGIE BLOOGIE", display_name="updates"
) )
# The following XML course is closed; we're testing that
# a course info page still appears when the course is already closed
self.xml_data = "course info 463139"
self.xml_course_id = "edX/detached_pages/2014"
def test_logged_in(self): def test_logged_in(self):
self.setup_user() self.setup_user()
...@@ -31,3 +36,18 @@ class CourseInfoTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase): ...@@ -31,3 +36,18 @@ class CourseInfoTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
resp = self.client.get(url) resp = self.client.get(url)
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
self.assertNotIn("OOGIE BLOOGIE", resp.content) self.assertNotIn("OOGIE BLOOGIE", resp.content)
@mock.patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
def test_logged_in_xml(self):
self.setup_user()
url = reverse('info', args=[self.xml_course_id])
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
self.assertIn(self.xml_data, resp.content)
@mock.patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
def test_anonymous_user_xml(self):
url = reverse('info', args=[self.xml_course_id])
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
self.assertNotIn(self.xml_data, resp.content)
...@@ -155,6 +155,11 @@ class StaticTabDateTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase): ...@@ -155,6 +155,11 @@ class StaticTabDateTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
category="static_tab", parent_location=self.course.location, category="static_tab", parent_location=self.course.location,
data="OOGIE BLOOGIE", display_name="new_tab" data="OOGIE BLOOGIE", display_name="new_tab"
) )
# The following XML course is closed; we're testing that
# static tabs still appear when the course is already closed
self.xml_data = "static 463139"
self.xml_url = "8e4cce2b4aaf4ba28b1220804619e41f"
self.xml_course_id = 'edX/detached_pages/2014'
def test_logged_in(self): def test_logged_in(self):
self.setup_user() self.setup_user()
...@@ -169,6 +174,21 @@ class StaticTabDateTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase): ...@@ -169,6 +174,21 @@ class StaticTabDateTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
self.assertIn("OOGIE BLOOGIE", resp.content) self.assertIn("OOGIE BLOOGIE", resp.content)
@patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
def test_logged_in_xml(self):
self.setup_user()
url = reverse('static_tab', args=[self.xml_course_id, self.xml_url])
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
self.assertIn(self.xml_data, resp.content)
@patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
def test_anonymous_user_xml(self):
url = reverse('static_tab', args=[self.xml_course_id, self.xml_url])
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
self.assertIn(self.xml_data, resp.content)
class TextbooksTestCase(TestCase): class TextbooksTestCase(TestCase):
......
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