Commit b719c074 by brianhw

Merge pull request #5517 from edx/brian/fix_export_course

Make sure temporary course directory uses safe characters.
parents fc514843 61c2b96a
...@@ -6,6 +6,7 @@ If <filename> is '-', it pipes the file to stdout ...@@ -6,6 +6,7 @@ If <filename> is '-', it pipes the file to stdout
""" """
import os import os
import re
import shutil import shutil
import tarfile import tarfile
from tempfile import mktemp, mkdtemp from tempfile import mktemp, mkdtemp
...@@ -18,7 +19,7 @@ from django.core.management.base import BaseCommand, CommandError ...@@ -18,7 +19,7 @@ from django.core.management.base import BaseCommand, CommandError
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from xmodule.modulestore.xml_exporter import export_to_xml from xmodule.modulestore.xml_exporter import export_to_xml
from opaque_keys import InvalidKeyError from opaque_keys import InvalidKeyError
from opaque_keys.edx.locations import SlashSeparatedCourseKey from opaque_keys.edx.keys import CourseKey
class Command(BaseCommand): class Command(BaseCommand):
...@@ -30,9 +31,9 @@ class Command(BaseCommand): ...@@ -30,9 +31,9 @@ class Command(BaseCommand):
help = dedent(__doc__).strip() help = dedent(__doc__).strip()
def handle(self, *args, **options): def handle(self, *args, **options):
course_id, filename, pipe_results = self._parse_arguments(args) course_key, filename, pipe_results = self._parse_arguments(args)
export_course_to_tarfile(course_id, filename) export_course_to_tarfile(course_key, filename)
results = self._get_results(filename) if pipe_results else None results = self._get_results(filename) if pipe_results else None
...@@ -41,7 +42,7 @@ class Command(BaseCommand): ...@@ -41,7 +42,7 @@ class Command(BaseCommand):
def _parse_arguments(self, args): def _parse_arguments(self, args):
"""Parse command line arguments""" """Parse command line arguments"""
try: try:
course_id = SlashSeparatedCourseKey.from_deprecated_string(args[0]) course_key = CourseKey.from_string(args[0])
filename = args[1] filename = args[1]
except InvalidKeyError: except InvalidKeyError:
raise CommandError("Unparsable course_id") raise CommandError("Unparsable course_id")
...@@ -54,7 +55,7 @@ class Command(BaseCommand): ...@@ -54,7 +55,7 @@ class Command(BaseCommand):
filename = mktemp() filename = mktemp()
pipe_results = True pipe_results = True
return course_id, filename, pipe_results return course_key, filename, pipe_results
def _get_results(self, filename): def _get_results(self, filename):
"""Load results from file""" """Load results from file"""
...@@ -64,32 +65,38 @@ class Command(BaseCommand): ...@@ -64,32 +65,38 @@ class Command(BaseCommand):
return results return results
def export_course_to_tarfile(course_id, filename): def export_course_to_tarfile(course_key, filename):
"""Exports a course into a tar.gz file""" """Exports a course into a tar.gz file"""
tmp_dir = mkdtemp() tmp_dir = mkdtemp()
try: try:
course_dir = export_course_to_directory(course_id, tmp_dir) course_dir = export_course_to_directory(course_key, tmp_dir)
compress_directory(course_dir, filename) compress_directory(course_dir, filename)
finally: finally:
shutil.rmtree(tmp_dir) shutil.rmtree(tmp_dir)
def export_course_to_directory(course_id, root_dir): def export_course_to_directory(course_key, root_dir):
"""Export course into a directory""" """Export course into a directory"""
store = modulestore() store = modulestore()
course = store.get_course(course_id) course = store.get_course(course_key)
if course is None: if course is None:
raise CommandError("Invalid course_id") raise CommandError("Invalid course_id")
course_name = course.id.to_deprecated_string().replace('/', '-') # The safest characters are A-Z, a-z, 0-9, <underscore>, <period> and <hyphen>.
export_to_xml(store, None, course.id, root_dir, course_name) # We represent the first four with \w.
# TODO: Once we support courses with unicode characters, we will need to revisit this.
replacement_char = u'-'
course_dir = replacement_char.join([course.id.org, course.id.course, course.id.run])
course_dir = re.sub(r'[^\w\.\-]', replacement_char, course_dir)
course_dir = path(root_dir) / course_name export_to_xml(store, None, course.id, root_dir, course_dir)
return course_dir
export_dir = path(root_dir) / course_dir
return export_dir
def compress_directory(directory, filename): def compress_directory(directory, filename):
"""Compress a directrory into a tar.gz file""" """Compress a directory into a tar.gz file"""
mode = 'w:gz' mode = 'w:gz'
name = path(directory).name name = path(directory).name
with tarfile.open(filename, mode) as tar_file: with tarfile.open(filename, mode) as tar_file:
......
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