diff --git a/cms/djangoapps/contentstore/tests/test_import_nostatic.py b/cms/djangoapps/contentstore/tests/test_import_nostatic.py new file mode 100644 index 0000000000..6b43d3f7c8 --- /dev/null +++ b/cms/djangoapps/contentstore/tests/test_import_nostatic.py @@ -0,0 +1,164 @@ +#pylint: disable=E1101 + +import json +import shutil +import sys +import mock +from django.test.client import Client +from django.test.utils import override_settings +from django.conf import settings +from django.core.urlresolvers import reverse +from path import path +from tempdir import mkdtemp_clean +from fs.osfs import OSFS +import copy +from json import loads +from datetime import timedelta + +from django.contrib.auth.models import User +from django.dispatch import Signal +from contentstore.utils import get_modulestore +from contentstore.tests.utils import parse_json + +from auth.authz import add_user_to_creator_group + +from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase +from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory + +from xmodule.modulestore import Location, mongo +from xmodule.modulestore.store_utilities import clone_course +from xmodule.modulestore.store_utilities import delete_course +from xmodule.modulestore.django import modulestore +from xmodule.contentstore.django import contentstore, _CONTENTSTORE +from xmodule.modulestore.xml_exporter import export_to_xml +from xmodule.modulestore.xml_importer import import_from_xml, perform_xlint +from xmodule.modulestore.inheritance import own_metadata +from xmodule.contentstore.content import StaticContent +from xmodule.contentstore.utils import restore_asset_from_trashcan, empty_asset_trashcan + +from xmodule.capa_module import CapaDescriptor +from xmodule.course_module import CourseDescriptor +from xmodule.seq_module import SequenceDescriptor +from xmodule.modulestore.exceptions import ItemNotFoundError + +from contentstore.views.component import ADVANCED_COMPONENT_TYPES +from xmodule.exceptions import NotFoundError + +from django_comment_common.utils import are_permissions_roles_seeded +from xmodule.exceptions import InvalidVersionError +import datetime +from pytz import UTC +from uuid import uuid4 +from pymongo import MongoClient +from student.models import CourseEnrollment + +TEST_DATA_CONTENTSTORE = copy.deepcopy(settings.CONTENTSTORE) +TEST_DATA_CONTENTSTORE['OPTIONS']['db'] = 'test_xcontent_%s' % uuid4().hex + + +class MongoCollectionFindWrapper(object): + def __init__(self, original): + self.original = original + self.counter = 0 + + def find(self, query, *args, **kwargs): + self.counter = self.counter + 1 + return self.original(query, *args, **kwargs) + + +@override_settings(CONTENTSTORE=TEST_DATA_CONTENTSTORE) +class ContentStoreImportNoStaticTest(ModuleStoreTestCase): + """ + Tests that rely on the toy and test_import_course courses. + TODO: refactor using CourseFactory so they do not. + """ + def setUp(self): + + settings.MODULESTORE['default']['OPTIONS']['fs_root'] = path('common/test/data') + settings.MODULESTORE['direct']['OPTIONS']['fs_root'] = path('common/test/data') + uname = 'testuser' + email = 'test+courses@edx.org' + password = 'foo' + + # Create the use so we can log them in. + self.user = User.objects.create_user(uname, email, password) + + # Note that we do not actually need to do anything + # for registration if we directly mark them active. + self.user.is_active = True + # Staff has access to view all courses + self.user.is_staff = True + + # Save the data that we've just changed to the db. + self.user.save() + + self.client = Client() + self.client.login(username=uname, password=password) + + def load_test_import_course(self): + ''' + Load the standard course used to test imports (for do_import_static=False behavior). + ''' + content_store = contentstore() + module_store = modulestore('direct') + import_from_xml(module_store, 'common/test/data/', ['test_import_course'], static_content_store=content_store, do_import_static=False, verbose=True) + course_location = CourseDescriptor.id_to_location('edX/test_import_course/2012_Fall') + course = module_store.get_item(course_location) + self.assertIsNotNone(course) + + return module_store, content_store, course, course_location + + + def test_static_import(self): + ''' + Stuff in static_import should always be imported into contentstore + ''' + module_store, content_store, course, course_location = self.load_test_import_course() + + # make sure we have ONE asset in our contentstore ("should_be_imported.html") + all_assets = content_store.get_all_content_for_course(course_location) + print "len(all_assets)=%d" % len(all_assets) + self.assertEqual(len(all_assets), 1) + + content = None + try: + location = StaticContent.get_location_from_path('/c4x/edX/test_import_course/asset/should_be_imported.html') + content = content_store.find(location) + except NotFoundError: + pass + + self.assertIsNotNone(content) + + # make sure course.lms.static_asset_path is correct + print "static_asset_path = {0}".format(course.lms.static_asset_path) + self.assertEqual(course.lms.static_asset_path, 'test_import_course') + + + def test_asset_import_nostatic(self): + ''' + This test validates that an image asset is NOT imported when do_import_static=False + ''' + content_store = contentstore() + + module_store = modulestore('direct') + import_from_xml(module_store, 'common/test/data/', ['toy'], static_content_store=content_store, do_import_static=False, verbose=True) + + course_location = CourseDescriptor.id_to_location('edX/toy/2012_Fall') + course = module_store.get_item(course_location) + + # make sure we have NO assets in our contentstore + all_assets = content_store.get_all_content_for_course(course_location) + print "len(all_assets)=%d" % len(all_assets) + self.assertEqual(len(all_assets), 0) + + + def test_no_static_link_rewrites_on_import(self): + module_store = modulestore('direct') + import_from_xml(module_store, 'common/test/data/', ['toy'], do_import_static=False, verbose=True) + + handouts = module_store.get_item(Location(['i4x', 'edX', 'toy', 'course_info', 'handouts', None])) + self.assertIn('/static/', handouts.data) + + handouts = module_store.get_item(Location(['i4x', 'edX', 'toy', 'html', 'toyhtml', None])) + self.assertIn('/static/', handouts.data) + diff --git a/common/djangoapps/static_replace/__init__.py b/common/djangoapps/static_replace/__init__.py index 2ee56799a8..4762edf0b6 100644 --- a/common/djangoapps/static_replace/__init__.py +++ b/common/djangoapps/static_replace/__init__.py @@ -117,7 +117,7 @@ def replace_static_urls(text, data_directory, course_id=None, static_asset_path= if settings.DEBUG and finders.find(rest, True): return original # if we're running with a MongoBacked store course_namespace is not None, then use studio style urls - elif course_id and modulestore().get_modulestore_type(course_id) != XML_MODULESTORE_TYPE: + elif (not static_asset_path) and course_id and modulestore().get_modulestore_type(course_id) != XML_MODULESTORE_TYPE: # first look in the static file pipeline and see if we are trying to reference # a piece of static content which is in the mitx repo (e.g. JS associated with an xmodule) if staticfiles_storage.exists(rest): diff --git a/common/test/data/test_import_course/about/end_date.html b/common/test/data/test_import_course/about/end_date.html new file mode 100644 index 0000000000..2fd9f95700 --- /dev/null +++ b/common/test/data/test_import_course/about/end_date.html @@ -0,0 +1 @@ +TBD \ No newline at end of file diff --git a/common/test/data/test_import_course/chapter/vertical_container.xml b/common/test/data/test_import_course/chapter/vertical_container.xml new file mode 100644 index 0000000000..886346704c --- /dev/null +++ b/common/test/data/test_import_course/chapter/vertical_container.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/common/test/data/test_import_course/course.xml b/common/test/data/test_import_course/course.xml new file mode 100644 index 0000000000..da108f7233 --- /dev/null +++ b/common/test/data/test_import_course/course.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/common/test/data/test_import_course/course/2012_Fall.xml b/common/test/data/test_import_course/course/2012_Fall.xml new file mode 100644 index 0000000000..9b14d49dcd --- /dev/null +++ b/common/test/data/test_import_course/course/2012_Fall.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/common/test/data/test_import_course/info/handouts.html b/common/test/data/test_import_course/info/handouts.html new file mode 100644 index 0000000000..85fa34d71d --- /dev/null +++ b/common/test/data/test_import_course/info/handouts.html @@ -0,0 +1 @@ +Sample \ No newline at end of file diff --git a/common/test/data/test_import_course/policies/2012_Fall.json b/common/test/data/test_import_course/policies/2012_Fall.json new file mode 100644 index 0000000000..464184fac8 --- /dev/null +++ b/common/test/data/test_import_course/policies/2012_Fall.json @@ -0,0 +1,33 @@ +{ + "course/2012_Fall": { + "graceperiod": "2 days 5 hours 59 minutes 59 seconds", + "start": "2015-07-17T12:00", + "display_name": "Toy Course", + "graded": "true", + "tabs": [ + {"type": "courseware"}, + {"type": "course_info", "name": "Course Info"}, + {"type": "static_tab", "url_slug": "syllabus", "name": "Syllabus"}, + {"type": "static_tab", "url_slug": "resources", "name": "Resources"}, + {"type": "discussion", "name": "Discussion"}, + {"type": "wiki", "name": "Wiki"}, + {"type": "progress", "name": "Progress"} + ] + }, + "chapter/Overview": { + "display_name": "Overview" + }, + "videosequence/Toy_Videos": { + "display_name": "Toy Videos", + "format": "Lecture Sequence" + }, + "html/secret:toylab": { + "display_name": "Toy lab" + }, + "video/Video_Resources": { + "display_name": "Video Resources" + }, + "video/Welcome": { + "display_name": "Welcome" + } +} diff --git a/common/test/data/test_import_course/sequential/vertical_sequential.xml b/common/test/data/test_import_course/sequential/vertical_sequential.xml new file mode 100644 index 0000000000..695e640243 --- /dev/null +++ b/common/test/data/test_import_course/sequential/vertical_sequential.xml @@ -0,0 +1,4 @@ + + + … + \ No newline at end of file diff --git a/common/test/data/test_import_course/static/handouts/sample_handout.txt b/common/test/data/test_import_course/static/handouts/sample_handout.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/test/data/test_import_course/static/images/amlevine.png b/common/test/data/test_import_course/static/images/amlevine.png new file mode 100644 index 0000000000..a5dae11145 Binary files /dev/null and b/common/test/data/test_import_course/static/images/amlevine.png differ diff --git a/common/test/data/test_import_course/static/images/course_image.jpg b/common/test/data/test_import_course/static/images/course_image.jpg new file mode 100644 index 0000000000..9d63c003f3 Binary files /dev/null and b/common/test/data/test_import_course/static/images/course_image.jpg differ diff --git a/common/test/data/test_import_course/static/sample_static.txt b/common/test/data/test_import_course/static/sample_static.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/test/data/test_import_course/static_import/should_be_imported.html b/common/test/data/test_import_course/static_import/should_be_imported.html new file mode 100644 index 0000000000..967175bcf7 --- /dev/null +++ b/common/test/data/test_import_course/static_import/should_be_imported.html @@ -0,0 +1 @@ +

this file should be in the contentstore

diff --git a/common/test/data/test_import_course/vertical/vertical_test.xml b/common/test/data/test_import_course/vertical/vertical_test.xml new file mode 100644 index 0000000000..68c5745f37 --- /dev/null +++ b/common/test/data/test_import_course/vertical/vertical_test.xml @@ -0,0 +1,9 @@ + + diff --git a/common/test/data/test_import_course/video/separate_file_video.xml b/common/test/data/test_import_course/video/separate_file_video.xml new file mode 100644 index 0000000000..b90ea9d8c4 --- /dev/null +++ b/common/test/data/test_import_course/video/separate_file_video.xml @@ -0,0 +1 @@ +