Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
f4822c23
Commit
f4822c23
authored
Oct 30, 2012
by
Chris Dodge
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
lots of tweeks to better support importing of existing courseware
parent
5a968209
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
186 additions
and
84 deletions
+186
-84
cms/djangoapps/contentstore/views.py
+7
-2
common/djangoapps/mitxmako/shortcuts.py
+1
-1
common/djangoapps/static_replace.py
+19
-5
common/djangoapps/xmodule_modifiers.py
+2
-2
common/lib/xmodule/setup.py
+2
-0
common/lib/xmodule/xmodule/contentstore/content.py
+7
-0
common/lib/xmodule/xmodule/course_module.py
+2
-1
common/lib/xmodule/xmodule/modulestore/mongo.py
+2
-0
common/lib/xmodule/xmodule/modulestore/xml.py
+24
-28
common/lib/xmodule/xmodule/modulestore/xml_importer.py
+81
-22
common/lib/xmodule/xmodule/template_module.py
+14
-6
lms/djangoapps/courseware/courses.py
+14
-12
lms/djangoapps/courseware/module_render.py
+2
-1
lms/djangoapps/courseware/tests/tests.py
+8
-3
lms/djangoapps/courseware/views.py
+1
-1
No files found.
cms/djangoapps/contentstore/views.py
View file @
f4822c23
...
@@ -287,9 +287,13 @@ def edit_unit(request, location):
...
@@ -287,9 +287,13 @@ def edit_unit(request, location):
# TODO (cpennington): If we share units between courses,
# TODO (cpennington): If we share units between courses,
# this will need to change to check permissions correctly so as
# this will need to change to check permissions correctly so as
# to pick the correct parent subsection
# to pick the correct parent subsection
logging
.
debug
(
'looking for parent of {0}'
.
format
(
location
))
containing_subsection_locs
=
modulestore
()
.
get_parent_locations
(
location
)
containing_subsection_locs
=
modulestore
()
.
get_parent_locations
(
location
)
containing_subsection
=
modulestore
()
.
get_item
(
containing_subsection_locs
[
0
])
containing_subsection
=
modulestore
()
.
get_item
(
containing_subsection_locs
[
0
])
logging
.
debug
(
'looking for parent of {0}'
.
format
(
containing_subsection
.
location
))
containing_section_locs
=
modulestore
()
.
get_parent_locations
(
containing_subsection
.
location
)
containing_section_locs
=
modulestore
()
.
get_parent_locations
(
containing_subsection
.
location
)
containing_section
=
modulestore
()
.
get_item
(
containing_section_locs
[
0
])
containing_section
=
modulestore
()
.
get_item
(
containing_section_locs
[
0
])
...
@@ -997,7 +1001,8 @@ def import_course(request, org, course, name):
...
@@ -997,7 +1001,8 @@ def import_course(request, org, course, name):
data_root
=
path
(
settings
.
GITHUB_REPO_ROOT
)
data_root
=
path
(
settings
.
GITHUB_REPO_ROOT
)
course_dir
=
data_root
/
"{0}-{1}-{2}"
.
format
(
org
,
course
,
name
)
course_subdir
=
"{0}-{1}-{2}"
.
format
(
org
,
course
,
name
)
course_dir
=
data_root
/
course_subdir
if
not
course_dir
.
isdir
():
if
not
course_dir
.
isdir
():
os
.
mkdir
(
course_dir
)
os
.
mkdir
(
course_dir
)
...
@@ -1033,7 +1038,7 @@ def import_course(request, org, course, name):
...
@@ -1033,7 +1038,7 @@ def import_course(request, org, course, name):
shutil
.
move
(
r
/
fname
,
course_dir
)
shutil
.
move
(
r
/
fname
,
course_dir
)
module_store
,
course_items
=
import_from_xml
(
modulestore
(
'direct'
),
settings
.
GITHUB_REPO_ROOT
,
module_store
,
course_items
=
import_from_xml
(
modulestore
(
'direct'
),
settings
.
GITHUB_REPO_ROOT
,
[
course_dir
],
load_error_modules
=
False
,
static_content_store
=
contentstore
(),
target_location_namespace
=
Location
(
location
))
[
course_
sub
dir
],
load_error_modules
=
False
,
static_content_store
=
contentstore
(),
target_location_namespace
=
Location
(
location
))
# we can blow this away when we're done importing.
# we can blow this away when we're done importing.
shutil
.
rmtree
(
course_dir
)
shutil
.
rmtree
(
course_dir
)
...
...
common/djangoapps/mitxmako/shortcuts.py
View file @
f4822c23
...
@@ -42,7 +42,7 @@ def render_to_string(template_name, dictionary, context=None, namespace='main'):
...
@@ -42,7 +42,7 @@ def render_to_string(template_name, dictionary, context=None, namespace='main'):
context_dictionary
.
update
(
context
)
context_dictionary
.
update
(
context
)
# fetch and render template
# fetch and render template
template
=
middleware
.
lookup
[
namespace
]
.
get_template
(
template_name
)
template
=
middleware
.
lookup
[
namespace
]
.
get_template
(
template_name
)
return
template
.
render
(
**
context_dictionary
)
return
template
.
render
_unicode
(
**
context_dictionary
)
def
render_to_response
(
template_name
,
dictionary
,
context_instance
=
None
,
namespace
=
'main'
,
**
kwargs
):
def
render_to_response
(
template_name
,
dictionary
,
context_instance
=
None
,
namespace
=
'main'
,
**
kwargs
):
...
...
common/djangoapps/static_replace.py
View file @
f4822c23
...
@@ -5,6 +5,10 @@ from staticfiles.storage import staticfiles_storage
...
@@ -5,6 +5,10 @@ from staticfiles.storage import staticfiles_storage
from
staticfiles
import
finders
from
staticfiles
import
finders
from
django.conf
import
settings
from
django.conf
import
settings
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.xml
import
XMLModuleStore
from
xmodule.contentstore.content
import
StaticContent
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
def
try_staticfiles_lookup
(
path
):
def
try_staticfiles_lookup
(
path
):
...
@@ -22,7 +26,7 @@ def try_staticfiles_lookup(path):
...
@@ -22,7 +26,7 @@ def try_staticfiles_lookup(path):
return
url
return
url
def
replace
(
static_url
,
prefix
=
None
):
def
replace
(
static_url
,
prefix
=
None
,
course_namespace
=
None
):
if
prefix
is
None
:
if
prefix
is
None
:
prefix
=
''
prefix
=
''
else
:
else
:
...
@@ -41,13 +45,23 @@ def replace(static_url, prefix=None):
...
@@ -41,13 +45,23 @@ def replace(static_url, prefix=None):
return
static_url
.
group
(
0
)
return
static_url
.
group
(
0
)
else
:
else
:
# don't error if file can't be found
# don't error if file can't be found
url
=
try_staticfiles_lookup
(
prefix
+
static_url
.
group
(
'rest'
))
# cdodge: to support the change over to Mongo backed content stores, lets
return
""
.
join
([
quote
,
url
,
quote
])
# use the utility functions in StaticContent.py
if
static_url
.
group
(
'prefix'
)
==
'/static/'
and
not
isinstance
(
modulestore
(),
XMLModuleStore
):
if
course_namespace
is
None
:
raise
BaseException
(
'You must pass in course_namespace when remapping static content urls with MongoDB stores'
)
url
=
StaticContent
.
convert_legacy_static_url
(
static_url
.
group
(
'rest'
),
course_namespace
)
else
:
url
=
try_staticfiles_lookup
(
prefix
+
static_url
.
group
(
'rest'
))
new_link
=
""
.
join
([
quote
,
url
,
quote
])
return
new_link
def
replace_urls
(
text
,
staticfiles_prefix
=
None
,
replace_prefix
=
'/static/'
):
def
replace_urls
(
text
,
staticfiles_prefix
=
None
,
replace_prefix
=
'/static/'
,
course_namespace
=
None
):
def
replace_url
(
static_url
):
def
replace_url
(
static_url
):
return
replace
(
static_url
,
staticfiles_prefix
)
return
replace
(
static_url
,
staticfiles_prefix
,
course_namespace
=
course_namespace
)
return
re
.
sub
(
r"""
return
re
.
sub
(
r"""
(?x) # flags=re.VERBOSE
(?x) # flags=re.VERBOSE
...
...
common/djangoapps/xmodule_modifiers.py
View file @
f4822c23
...
@@ -50,7 +50,7 @@ def replace_course_urls(get_html, course_id):
...
@@ -50,7 +50,7 @@ def replace_course_urls(get_html, course_id):
return
replace_urls
(
get_html
(),
staticfiles_prefix
=
'/courses/'
+
course_id
,
replace_prefix
=
'/course/'
)
return
replace_urls
(
get_html
(),
staticfiles_prefix
=
'/courses/'
+
course_id
,
replace_prefix
=
'/course/'
)
return
_get_html
return
_get_html
def
replace_static_urls
(
get_html
,
prefix
):
def
replace_static_urls
(
get_html
,
prefix
,
course_namespace
=
None
):
"""
"""
Updates the supplied module with a new get_html function that wraps
Updates the supplied module with a new get_html function that wraps
the old get_html function and substitutes urls of the form /static/...
the old get_html function and substitutes urls of the form /static/...
...
@@ -59,7 +59,7 @@ def replace_static_urls(get_html, prefix):
...
@@ -59,7 +59,7 @@ def replace_static_urls(get_html, prefix):
@wraps
(
get_html
)
@wraps
(
get_html
)
def
_get_html
():
def
_get_html
():
return
replace_urls
(
get_html
(),
staticfiles_prefix
=
prefix
)
return
replace_urls
(
get_html
(),
staticfiles_prefix
=
prefix
,
course_namespace
=
course_namespace
)
return
_get_html
return
_get_html
...
...
common/lib/xmodule/setup.py
View file @
f4822c23
...
@@ -37,6 +37,8 @@ setup(
...
@@ -37,6 +37,8 @@ setup(
"discussion = xmodule.discussion_module:DiscussionDescriptor"
,
"discussion = xmodule.discussion_module:DiscussionDescriptor"
,
"course_info = xmodule.html_module:HtmlDescriptor"
,
"course_info = xmodule.html_module:HtmlDescriptor"
,
"static_tab = xmodule.html_module:HtmlDescriptor"
,
"static_tab = xmodule.html_module:HtmlDescriptor"
,
"custom_tag_template = xmodule.raw_module:RawDescriptor"
,
"about = xmodule.html_module:HtmlDescriptor"
]
]
}
}
)
)
common/lib/xmodule/xmodule/contentstore/content.py
View file @
f4822c23
...
@@ -62,6 +62,13 @@ class StaticContent(object):
...
@@ -62,6 +62,13 @@ class StaticContent(object):
@staticmethod
@staticmethod
def
get_id_from_path
(
path
):
def
get_id_from_path
(
path
):
return
get_id_from_location
(
get_location_from_path
(
path
))
return
get_id_from_location
(
get_location_from_path
(
path
))
@staticmethod
def
convert_legacy_static_url
(
path
,
course_namespace
):
loc
=
StaticContent
.
compute_location
(
course_namespace
.
org
,
course_namespace
.
course
,
path
)
return
StaticContent
.
get_url_path_from_location
(
loc
)
class
ContentStore
(
object
):
class
ContentStore
(
object
):
...
...
common/lib/xmodule/xmodule/course_module.py
View file @
f4822c23
...
@@ -202,7 +202,7 @@ class CourseDescriptor(SequenceDescriptor):
...
@@ -202,7 +202,7 @@ class CourseDescriptor(SequenceDescriptor):
# Try to load grading policy
# Try to load grading policy
paths
=
[
'grading_policy.json'
]
paths
=
[
'grading_policy.json'
]
if
policy_dir
:
if
policy_dir
:
paths
=
[
policy_dir
+
'grading_policy.json'
]
+
paths
paths
=
[
policy_dir
+
'
/
grading_policy.json'
]
+
paths
policy
=
json
.
loads
(
cls
.
read_grading_policy
(
paths
,
system
))
policy
=
json
.
loads
(
cls
.
read_grading_policy
(
paths
,
system
))
...
@@ -394,3 +394,4 @@ class CourseDescriptor(SequenceDescriptor):
...
@@ -394,3 +394,4 @@ class CourseDescriptor(SequenceDescriptor):
return
self
.
location
.
org
return
self
.
location
.
org
common/lib/xmodule/xmodule/modulestore/mongo.py
View file @
f4822c23
import
pymongo
import
pymongo
import
sys
import
sys
import
logging
from
bson.son
import
SON
from
bson.son
import
SON
from
fs.osfs
import
OSFS
from
fs.osfs
import
OSFS
...
@@ -49,6 +50,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
...
@@ -49,6 +50,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
self
.
load_item
,
resources_fs
,
error_tracker
,
render_template
)
self
.
load_item
,
resources_fs
,
error_tracker
,
render_template
)
self
.
modulestore
=
modulestore
self
.
modulestore
=
modulestore
self
.
module_data
=
module_data
self
.
module_data
=
module_data
self
.
default_class
=
default_class
self
.
default_class
=
default_class
def
load_item
(
self
,
location
):
def
load_item
(
self
,
location
):
...
...
common/lib/xmodule/xmodule/modulestore/xml.py
View file @
f4822c23
...
@@ -427,42 +427,38 @@ class XMLModuleStore(ModuleStoreBase):
...
@@ -427,42 +427,38 @@ class XMLModuleStore(ModuleStoreBase):
# now import all pieces of course_info which is expected to be stored
# now import all pieces of course_info which is expected to be stored
# in <content_dir>/info or <content_dir>/info/<url_name>
# in <content_dir>/info or <content_dir>/info/<url_name>
if
url_name
:
self
.
load_extra_content
(
system
,
course_descriptor
,
'course_info'
,
self
.
data_dir
/
course_dir
/
'info'
,
course_dir
,
url_name
)
info_path
=
self
.
data_dir
/
course_dir
/
'info'
/
url_name
if
not
os
.
path
.
exists
(
info_path
):
info_path
=
self
.
data_dir
/
course_dir
/
'info'
# we have a fixed number of .html info files that we expect there
for
info_filename
in
[
'handouts'
,
'guest_handouts'
,
'updates'
,
'guest_updates'
]:
filepath
=
info_path
/
info_filename
+
'.html'
if
os
.
path
.
exists
(
filepath
):
with
open
(
filepath
)
as
info_file
:
html
=
info_file
.
read
()
.
decode
(
'utf-8'
)
loc
=
Location
(
'i4x'
,
course_descriptor
.
location
.
org
,
course_descriptor
.
location
.
course
,
'course_info'
,
info_filename
)
info_module
=
HtmlDescriptor
(
system
,
definition
=
{
'data'
:
html
},
**
{
'location'
:
loc
})
self
.
modules
[
course_id
][
info_module
.
location
]
=
info_module
# now import all static tabs which are expected to be stored in
# now import all static tabs which are expected to be stored in
# in <content_dir>/tabs or <content_dir>/tabs/<url_name>
# in <content_dir>/tabs or <content_dir>/tabs/<url_name>
if
url_name
:
self
.
load_extra_content
(
system
,
course_descriptor
,
'static_tab'
,
self
.
data_dir
/
course_dir
/
'tabs'
,
course_dir
,
url_name
)
tabs_path
=
self
.
data_dir
/
course_dir
/
'tabs'
/
url_name
if
not
os
.
path
.
exists
(
tabs_path
):
self
.
load_extra_content
(
system
,
course_descriptor
,
'custom_tag_template'
,
self
.
data_dir
/
course_dir
/
'custom_tags'
,
course_dir
,
url_name
)
tabs_path
=
self
.
data_dir
/
course_dir
/
'tabs'
for
tab_filepath
in
glob
.
glob
(
tabs_path
/
'*.html'
):
self
.
load_extra_content
(
system
,
course_descriptor
,
'about'
,
self
.
data_dir
/
course_dir
/
'about'
,
course_dir
,
url_name
)
with
open
(
tab_filepath
)
as
tab_file
:
html
=
tab_file
.
read
()
.
decode
(
'utf-8'
)
# 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
(
tab_filepath
))[
0
]
loc
=
Location
(
'i4x'
,
course_descriptor
.
location
.
org
,
course_descriptor
.
location
.
course
,
'static_tab'
,
slug
)
tab_module
=
HtmlDescriptor
(
system
,
definition
=
{
'data'
:
html
},
**
{
'location'
:
loc
})
self
.
modules
[
course_id
][
tab_module
.
location
]
=
tab_module
log
.
debug
(
'========> Done with course import from {0}'
.
format
(
course_dir
))
log
.
debug
(
'========> Done with course import from {0}'
.
format
(
course_dir
))
return
course_descriptor
return
course_descriptor
def
load_extra_content
(
self
,
system
,
course_descriptor
,
category
,
base_dir
,
course_dir
,
url_name
):
if
url_name
:
path
=
base_dir
/
url_name
if
not
os
.
path
.
exists
(
path
):
path
=
base_dir
for
filepath
in
glob
.
glob
(
path
/
'*'
):
with
open
(
filepath
)
as
f
:
try
:
html
=
f
.
read
()
.
decode
(
'utf-8'
)
# 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
]
loc
=
Location
(
'i4x'
,
course_descriptor
.
location
.
org
,
course_descriptor
.
location
.
course
,
category
,
slug
)
module
=
HtmlDescriptor
(
system
,
definition
=
{
'data'
:
html
},
**
{
'location'
:
loc
})
module
.
metadata
[
'data_dir'
]
=
course_dir
self
.
modules
[
course_descriptor
.
id
][
module
.
location
]
=
module
except
Exception
,
e
:
logging
.
exception
(
"Failed to load {0}. Skipping... Exception: {1}"
.
format
(
filepath
,
str
(
e
)))
def
get_instance
(
self
,
course_id
,
location
,
depth
=
0
):
def
get_instance
(
self
,
course_id
,
location
,
depth
=
0
):
"""
"""
...
...
common/lib/xmodule/xmodule/modulestore/xml_importer.py
View file @
f4822c23
import
logging
import
logging
import
os
import
os
import
mimetypes
import
mimetypes
from
lxml.html
import
rewrite_links
as
lxml_rewrite_links
from
.xml
import
XMLModuleStore
from
.xml
import
XMLModuleStore
from
.exceptions
import
DuplicateItemError
from
.exceptions
import
DuplicateItemError
...
@@ -9,7 +10,7 @@ from xmodule.contentstore.content import StaticContent, XASSET_SRCREF_PREFIX
...
@@ -9,7 +10,7 @@ from xmodule.contentstore.content import StaticContent, XASSET_SRCREF_PREFIX
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
def
import_static_content
(
modules
,
data_dir
,
static_content_store
):
def
import_static_content
(
modules
,
data_dir
,
static_content_store
,
target_location_namespace
):
remap_dict
=
{}
remap_dict
=
{}
...
@@ -31,15 +32,13 @@ def import_static_content(modules, data_dir, static_content_store):
...
@@ -31,15 +32,13 @@ def import_static_content(modules, data_dir, static_content_store):
# now import all static assets
# now import all static assets
static_dir
=
'{0}/static/'
.
format
(
course_data_dir
)
static_dir
=
'{0}/static/'
.
format
(
course_data_dir
)
logging
.
debug
(
"Importing static assets in {0}"
.
format
(
static_dir
))
for
dirname
,
dirnames
,
filenames
in
os
.
walk
(
static_dir
):
for
dirname
,
dirnames
,
filenames
in
os
.
walk
(
static_dir
):
for
filename
in
filenames
:
for
filename
in
filenames
:
try
:
try
:
content_path
=
os
.
path
.
join
(
dirname
,
filename
)
content_path
=
os
.
path
.
join
(
dirname
,
filename
)
fullname_with_subpath
=
content_path
.
replace
(
static_dir
,
''
)
# strip away leading path from the name
fullname_with_subpath
=
content_path
.
replace
(
static_dir
,
''
)
# strip away leading path from the name
content_loc
=
StaticContent
.
compute_location
(
course_loc
.
org
,
course_loc
.
course
,
fullname_with_subpath
)
content_loc
=
StaticContent
.
compute_location
(
target_location_namespace
.
org
,
target_location_namespace
.
course
,
fullname_with_subpath
)
mime_type
=
mimetypes
.
guess_type
(
filename
)[
0
]
mime_type
=
mimetypes
.
guess_type
(
filename
)[
0
]
f
=
open
(
content_path
,
'rb'
)
f
=
open
(
content_path
,
'rb'
)
...
@@ -59,12 +58,50 @@ def import_static_content(modules, data_dir, static_content_store):
...
@@ -59,12 +58,50 @@ def import_static_content(modules, data_dir, static_content_store):
#store the remapping information which will be needed to subsitute in the module data
#store the remapping information which will be needed to subsitute in the module data
remap_dict
[
fullname_with_subpath
]
=
content_loc
.
name
remap_dict
[
fullname_with_subpath
]
=
content_loc
.
name
except
:
except
:
raise
raise
return
remap_dict
return
remap_dict
def
verify_content_links
(
module
,
base_dir
,
static_content_store
,
link
,
remap_dict
=
None
):
if
link
.
startswith
(
'/static/'
):
# yes, then parse out the name
path
=
link
[
len
(
'/static/'
):]
static_pathname
=
base_dir
/
path
if
os
.
path
.
exists
(
static_pathname
):
try
:
content_loc
=
StaticContent
.
compute_location
(
module
.
location
.
org
,
module
.
location
.
course
,
path
)
filename
=
os
.
path
.
basename
(
path
)
mime_type
=
mimetypes
.
guess_type
(
filename
)[
0
]
f
=
open
(
static_pathname
,
'rb'
)
data
=
f
.
read
()
f
.
close
()
content
=
StaticContent
(
content_loc
,
filename
,
mime_type
,
data
)
# first let's save a thumbnail so we can get back a thumbnail location
thumbnail_content
=
static_content_store
.
generate_thumbnail
(
content
)
if
thumbnail_content
is
not
None
:
content
.
thumbnail_location
=
thumbnail_content
.
location
#then commit the content
static_content_store
.
save
(
content
)
new_link
=
StaticContent
.
get_url_path_from_location
(
content_loc
)
if
remap_dict
is
not
None
:
remap_dict
[
link
]
=
new_link
return
new_link
except
Exception
,
e
:
logging
.
exception
(
'Skipping failed content load from {0}. Exception: {1}'
.
format
(
path
,
e
))
return
link
def
import_from_xml
(
store
,
data_dir
,
course_dirs
=
None
,
def
import_from_xml
(
store
,
data_dir
,
course_dirs
=
None
,
default_class
=
'xmodule.raw_module.RawDescriptor'
,
default_class
=
'xmodule.raw_module.RawDescriptor'
,
load_error_modules
=
True
,
static_content_store
=
None
,
target_location_namespace
=
None
):
load_error_modules
=
True
,
static_content_store
=
None
,
target_location_namespace
=
None
):
...
@@ -94,15 +131,20 @@ def import_from_xml(store, data_dir, course_dirs=None,
...
@@ -94,15 +131,20 @@ def import_from_xml(store, data_dir, course_dirs=None,
# method on XmlModuleStore.
# method on XmlModuleStore.
course_items
=
[]
course_items
=
[]
for
course_id
in
module_store
.
modules
.
keys
():
for
course_id
in
module_store
.
modules
.
keys
():
remap_dict
=
{}
course_data_dir
=
None
for
module
in
module_store
.
modules
[
course_id
]
.
itervalues
():
if
module
.
category
==
'course'
:
course_data_dir
=
module
.
metadata
[
'data_dir'
]
if
static_content_store
is
not
None
:
if
static_content_store
is
not
None
:
remap_dict
=
import_static_content
(
module_store
.
modules
[
course_id
],
data_dir
,
static_content_store
)
import_static_content
(
module_store
.
modules
[
course_id
],
data_dir
,
static_content_store
,
target_location_namespace
if
target_location_namespace
is
not
None
else
module_store
.
modules
[
course_id
]
.
location
)
for
module
in
module_store
.
modules
[
course_id
]
.
itervalues
():
for
module
in
module_store
.
modules
[
course_id
]
.
itervalues
():
# remap module to the new namespace
# remap module to the new namespace
if
target_location_namespace
is
not
None
:
if
target_location_namespace
is
not
None
:
# This looks a bit wonky as we need to also change the 'name' of the imported course to be what
# This looks a bit wonky as we need to also change the 'name' of the imported course to be what
# the caller passed in
# the caller passed in
if
module
.
location
.
category
!=
'course'
:
if
module
.
location
.
category
!=
'course'
:
...
@@ -120,8 +162,8 @@ def import_from_xml(store, data_dir, course_dirs=None,
...
@@ -120,8 +162,8 @@ def import_from_xml(store, data_dir, course_dirs=None,
child_loc
=
Location
(
child
)
child_loc
=
Location
(
child
)
new_child_loc
=
child_loc
.
_replace
(
tag
=
target_location_namespace
.
tag
,
org
=
target_location_namespace
.
org
,
new_child_loc
=
child_loc
.
_replace
(
tag
=
target_location_namespace
.
tag
,
org
=
target_location_namespace
.
org
,
course
=
target_location_namespace
.
course
)
course
=
target_location_namespace
.
course
)
new_locs
.
append
(
new_child_loc
)
new_locs
.
append
(
new_child_loc
.
url
()
)
module
.
definition
[
'children'
]
=
new_locs
module
.
definition
[
'children'
]
=
new_locs
...
@@ -129,22 +171,37 @@ def import_from_xml(store, data_dir, course_dirs=None,
...
@@ -129,22 +171,37 @@ def import_from_xml(store, data_dir, course_dirs=None,
if
module
.
category
==
'course'
:
if
module
.
category
==
'course'
:
# HACK: for now we don't support progress tabs. There's a special metadata configuration setting for this.
# HACK: for now we don't support progress tabs. There's a special metadata configuration setting for this.
module
.
metadata
[
'hide_progress_tab'
]
=
True
module
.
metadata
[
'hide_progress_tab'
]
=
True
# a bit of a hack, but typically the "course image" which is shown on marketing pages is hard coded to /images/course_image.jpg
# so let's make sure we import in case there are no other references to it in the modules
verify_content_links
(
module
,
data_dir
/
course_data_dir
,
static_content_store
,
'/static/images/course_image.jpg'
)
course_items
.
append
(
module
)
course_items
.
append
(
module
)
if
'data'
in
module
.
definition
:
if
'data'
in
module
.
definition
:
module_data
=
module
.
definition
[
'data'
]
module_data
=
module
.
definition
[
'data'
]
# cdodge: update any references to the static content paths
# cdodge: now go through any link references to '/static/' and make sure we've imported
# This is a bit brute force - simple search/replace - but it's unlikely that such references to '/static/....'
# it as a StaticContent asset
# would occur naturally (in the wild)
try
:
# @TODO, sorry a bit of technical debt here. There are some helper methods in xmodule_modifiers.py and static_replace.py which could
remap_dict
=
{}
# better do the url replace on the html rendering side rather than on the ingest side
try
:
# use the rewrite_links as a utility means to enumerate through all links
if
'/static/'
in
module_data
:
# in the module data. We use that to load that reference into our asset store
for
subkey
in
remap_dict
.
keys
():
# IMPORTANT: There appears to be a bug in lxml.rewrite_link which makes us not be able to
module_data
=
module_data
.
replace
(
'/static/'
+
subkey
,
'xasset:'
+
remap_dict
[
subkey
])
# do the rewrites natively in that code.
except
:
# For example, what I'm seeing is <img src='foo.jpg' /> -> <img src='bar.jpg'>
pass
# part of the techincal debt is that module_data might not be a string (e.g. ABTest)
# Note the dropped element closing tag. This causes the LMS to fail when rendering modules - that's
# no good, so we have to do this kludge
if
isinstance
(
module_data
,
str
)
or
isinstance
(
module_data
,
unicode
):
# some module 'data' fields are non strings which blows up the link traversal code
lxml_rewrite_links
(
module_data
,
lambda
link
:
verify_content_links
(
module
,
data_dir
/
course_data_dir
,
static_content_store
,
link
,
remap_dict
))
for
key
in
remap_dict
.
keys
():
module_data
=
module_data
.
replace
(
key
,
remap_dict
[
key
])
except
Exception
,
e
:
logging
.
exception
(
"failed to rewrite links on {0}. Continuing..."
.
format
(
module
.
location
))
store
.
update_item
(
module
.
location
,
module_data
)
store
.
update_item
(
module
.
location
,
module_data
)
...
@@ -156,4 +213,6 @@ def import_from_xml(store, data_dir, course_dirs=None,
...
@@ -156,4 +213,6 @@ def import_from_xml(store, data_dir, course_dirs=None,
# inherited metadata everywhere.
# inherited metadata everywhere.
store
.
update_metadata
(
module
.
location
,
dict
(
module
.
own_metadata
))
store
.
update_metadata
(
module
.
location
,
dict
(
module
.
own_metadata
))
return
module_store
,
course_items
return
module_store
,
course_items
common/lib/xmodule/xmodule/template_module.py
View file @
f4822c23
...
@@ -2,6 +2,7 @@ from xmodule.x_module import XModule
...
@@ -2,6 +2,7 @@ from xmodule.x_module import XModule
from
xmodule.raw_module
import
RawDescriptor
from
xmodule.raw_module
import
RawDescriptor
from
lxml
import
etree
from
lxml
import
etree
from
mako.template
import
Template
from
mako.template
import
Template
from
xmodule.modulestore.django
import
modulestore
class
CustomTagModule
(
XModule
):
class
CustomTagModule
(
XModule
):
...
@@ -40,8 +41,7 @@ class CustomTagDescriptor(RawDescriptor):
...
@@ -40,8 +41,7 @@ class CustomTagDescriptor(RawDescriptor):
module_class
=
CustomTagModule
module_class
=
CustomTagModule
template_dir_name
=
'customtag'
template_dir_name
=
'customtag'
@staticmethod
def
render_template
(
self
,
system
,
xml_data
):
def
render_template
(
system
,
xml_data
):
'''Render the template, given the definition xml_data'''
'''Render the template, given the definition xml_data'''
xmltree
=
etree
.
fromstring
(
xml_data
)
xmltree
=
etree
.
fromstring
(
xml_data
)
if
'impl'
in
xmltree
.
attrib
:
if
'impl'
in
xmltree
.
attrib
:
...
@@ -57,15 +57,23 @@ class CustomTagDescriptor(RawDescriptor):
...
@@ -57,15 +57,23 @@ class CustomTagDescriptor(RawDescriptor):
.
format
(
location
))
.
format
(
location
))
params
=
dict
(
xmltree
.
items
())
params
=
dict
(
xmltree
.
items
())
with
system
.
resources_fs
.
open
(
'custom_tags/{name}'
.
format
(
name
=
template_name
))
as
template
:
# cdodge: look up the template as a module
return
Template
(
template
.
read
())
.
render
(
**
params
)
template_loc
=
self
.
location
.
_replace
(
category
=
'custom_tag_template'
,
name
=
template_name
)
template_module
=
modulestore
()
.
get_item
(
template_loc
)
template_module_data
=
template_module
.
definition
[
'data'
]
template
=
Template
(
template_module_data
)
return
template
.
render
(
**
params
)
def
__init__
(
self
,
system
,
definition
,
**
kwargs
):
def
__init__
(
self
,
system
,
definition
,
**
kwargs
):
'''Render and save the template for this descriptor instance'''
'''Render and save the template for this descriptor instance'''
super
(
CustomTagDescriptor
,
self
)
.
__init__
(
system
,
definition
,
**
kwargs
)
super
(
CustomTagDescriptor
,
self
)
.
__init__
(
system
,
definition
,
**
kwargs
)
self
.
rendered_html
=
self
.
render_template
(
system
,
definition
[
'data'
])
@property
def
rendered_html
(
self
):
return
self
.
render_template
(
self
.
system
,
self
.
definition
[
'data'
])
def
export_to_file
(
self
):
def
export_to_file
(
self
):
"""
"""
...
...
lms/djangoapps/courseware/courses.py
View file @
f4822c23
...
@@ -14,6 +14,7 @@ from module_render import get_module
...
@@ -14,6 +14,7 @@ from module_render import get_module
from
xmodule.course_module
import
CourseDescriptor
from
xmodule.course_module
import
CourseDescriptor
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
from
xmodule.contentstore.content
import
StaticContent
from
xmodule.modulestore.xml
import
XMLModuleStore
from
xmodule.modulestore.xml
import
XMLModuleStore
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.x_module
import
XModule
from
xmodule.x_module
import
XModule
...
@@ -68,8 +69,13 @@ def get_opt_course_with_access(user, course_id, action):
...
@@ -68,8 +69,13 @@ def get_opt_course_with_access(user, course_id, action):
def
course_image_url
(
course
):
def
course_image_url
(
course
):
"""Try to look up the image url for the course. If it's not found,
"""Try to look up the image url for the course. If it's not found,
log an error and return the dead link"""
log an error and return the dead link"""
path
=
course
.
metadata
[
'data_dir'
]
+
"/images/course_image.jpg"
if
isinstance
(
modulestore
(),
XMLModuleStore
):
return
try_staticfiles_lookup
(
path
)
path
=
course
.
metadata
[
'data_dir'
]
+
"/images/course_image.jpg"
return
try_staticfiles_lookup
(
path
)
else
:
loc
=
course
.
location
.
_replace
(
tag
=
'c4x'
,
category
=
'asset'
,
name
=
'images_course_image.jpg'
)
path
=
StaticContent
.
get_url_path_from_location
(
loc
)
return
path
def
find_file
(
fs
,
dirs
,
filename
):
def
find_file
(
fs
,
dirs
,
filename
):
"""
"""
...
@@ -123,14 +129,12 @@ def get_course_about_section(course, section_key):
...
@@ -123,14 +129,12 @@ def get_course_about_section(course, section_key):
'effort'
,
'end_date'
,
'prerequisites'
,
'ocw_links'
]:
'effort'
,
'end_date'
,
'prerequisites'
,
'ocw_links'
]:
try
:
try
:
fs
=
course
.
system
.
resources_fs
loc
=
course
.
location
.
_replace
(
category
=
'about'
,
name
=
section_key
)
# first look for a run-specific version
item
=
modulestore
()
.
get_instance
(
course
.
id
,
loc
)
dirs
=
[
path
(
"about"
)
/
course
.
url_name
,
path
(
"about"
)]
filepath
=
find_file
(
fs
,
dirs
,
section_key
+
".html"
)
return
item
.
definition
[
'data'
]
with
fs
.
open
(
filepath
)
as
htmlFile
:
return
replace_urls
(
htmlFile
.
read
()
.
decode
(
'utf-8'
),
except
ItemNotFoundError
:
course
.
metadata
[
'data_dir'
])
except
ResourceNotFoundError
:
log
.
warning
(
"Missing about section {key} in course {url}"
.
format
(
log
.
warning
(
"Missing about section {key} in course {url}"
.
format
(
key
=
section_key
,
url
=
course
.
location
.
url
()))
key
=
section_key
,
url
=
course
.
location
.
url
()))
return
None
return
None
...
@@ -160,8 +164,6 @@ def get_course_info_section(request, cache, course, section_key):
...
@@ -160,8 +164,6 @@ def get_course_info_section(request, cache, course, section_key):
loc
=
Location
(
course
.
location
.
tag
,
course
.
location
.
org
,
course
.
location
.
course
,
'course_info'
,
section_key
)
loc
=
Location
(
course
.
location
.
tag
,
course
.
location
.
org
,
course
.
location
.
course
,
'course_info'
,
section_key
)
course_module
=
get_module
(
request
.
user
,
request
,
loc
,
cache
,
course
.
id
)
course_module
=
get_module
(
request
.
user
,
request
,
loc
,
cache
,
course
.
id
)
logging
.
debug
(
'course_module = {0}'
.
format
(
course_module
))
html
=
''
html
=
''
if
course_module
is
not
None
:
if
course_module
is
not
None
:
...
...
lms/djangoapps/courseware/module_render.py
View file @
f4822c23
...
@@ -256,7 +256,8 @@ def _get_module(user, request, location, student_module_cache, course_id, positi
...
@@ -256,7 +256,8 @@ def _get_module(user, request, location, student_module_cache, course_id, positi
module
.
get_html
=
replace_static_urls
(
module
.
get_html
=
replace_static_urls
(
wrap_xmodule
(
module
.
get_html
,
module
,
'xmodule_display.html'
),
wrap_xmodule
(
module
.
get_html
,
module
,
'xmodule_display.html'
),
module
.
metadata
[
'data_dir'
]
if
'data_dir'
in
module
.
metadata
else
''
)
module
.
metadata
[
'data_dir'
]
if
'data_dir'
in
module
.
metadata
else
''
,
course_namespace
=
module
.
location
.
_replace
(
category
=
None
,
name
=
None
))
# Allow URLs of the form '/course/' refer to the root of multicourse directory
# Allow URLs of the form '/course/' refer to the root of multicourse directory
# hierarchy of this course
# hierarchy of this course
...
...
lms/djangoapps/courseware/tests/tests.py
View file @
f4822c23
...
@@ -247,6 +247,7 @@ class PageLoader(ActivateLoginTestCase):
...
@@ -247,6 +247,7 @@ class PageLoader(ActivateLoginTestCase):
all_ok
=
True
all_ok
=
True
for
descriptor
in
modstore
.
get_items
(
for
descriptor
in
modstore
.
get_items
(
Location
(
None
,
None
,
None
,
None
,
None
)):
Location
(
None
,
None
,
None
,
None
,
None
)):
n
+=
1
n
+=
1
print
"Checking "
,
descriptor
.
location
.
url
()
print
"Checking "
,
descriptor
.
location
.
url
()
#print descriptor.__class__, descriptor.location
#print descriptor.__class__, descriptor.location
...
@@ -256,9 +257,13 @@ class PageLoader(ActivateLoginTestCase):
...
@@ -256,9 +257,13 @@ class PageLoader(ActivateLoginTestCase):
msg
=
str
(
resp
.
status_code
)
msg
=
str
(
resp
.
status_code
)
if
resp
.
status_code
!=
302
:
if
resp
.
status_code
!=
302
:
msg
=
"ERROR "
+
msg
# cdodge: we're adding new module category as part of the Studio work
all_ok
=
False
# such as 'course_info', etc, which are not supposed to be jump_to'able
num_bad
+=
1
# so a successful return value here should be a 404
if
descriptor
.
location
.
category
not
in
[
'about'
,
'static_tab'
,
'course_info'
,
'custom_tag_template'
]
or
resp
.
status_code
!=
404
:
msg
=
"ERROR "
+
msg
all_ok
=
False
num_bad
+=
1
print
msg
print
msg
self
.
assertTrue
(
all_ok
)
# fail fast
self
.
assertTrue
(
all_ok
)
# fail fast
...
...
lms/djangoapps/courseware/views.py
View file @
f4822c23
...
@@ -421,7 +421,7 @@ def course_about(request, course_id):
...
@@ -421,7 +421,7 @@ def course_about(request, course_id):
settings
.
MITX_FEATURES
.
get
(
'ENABLE_LMS_MIGRATION'
))
settings
.
MITX_FEATURES
.
get
(
'ENABLE_LMS_MIGRATION'
))
return
render_to_response
(
'portal/course_about.html'
,
return
render_to_response
(
'portal/course_about.html'
,
{
'course'
:
course
,
{
'course'
:
course
,
'registered'
:
registered
,
'registered'
:
registered
,
'course_target'
:
course_target
,
'course_target'
:
course_target
,
'show_courseware_link'
:
show_courseware_link
})
'show_courseware_link'
:
show_courseware_link
})
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment