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
6d67d0c6
Commit
6d67d0c6
authored
Feb 05, 2014
by
Calen Pennington
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1949 from cpennington/xblock-studio-js-and-css
Enable XBlock js and css in Studio
parents
7339f568
569c5def
Hide whitespace changes
Inline
Side-by-side
Showing
28 changed files
with
318 additions
and
190 deletions
+318
-190
cms/djangoapps/contentstore/tests/test_contentstore.py
+6
-3
cms/djangoapps/contentstore/tests/utils.py
+7
-0
cms/djangoapps/contentstore/views/item.py
+61
-22
cms/djangoapps/contentstore/views/preview.py
+13
-21
cms/lib/xblock/runtime.py
+1
-3
cms/static/coffee/spec/main.coffee
+1
-0
cms/static/coffee/spec/main_squire.coffee
+1
-0
cms/static/coffee/spec/views/module_edit_spec.coffee
+53
-4
cms/static/coffee/src/views/module_edit.coffee
+32
-4
cms/static/coffee/src/xblock/cms.runtime.v1.coffee
+22
-0
cms/static/js_test.yml
+2
-1
cms/static/js_test_squire.yml
+2
-0
cms/static/sass/elements/_xmodules.scss
+1
-1
cms/static/sass/views/_static-pages.scss
+3
-3
cms/static/sass/views/_unit.scss
+1
-1
common/djangoapps/xmodule_modifiers.py
+10
-6
common/static/coffee/spec/xblock/core_spec.coffee
+11
-8
common/static/coffee/spec/xblock/runtime.v1_spec.coffee
+2
-4
common/static/coffee/src/xblock/core.coffee
+5
-4
common/static/coffee/src/xblock/runtime.v1.coffee
+5
-24
lms/djangoapps/courseware/module_render.py
+4
-3
lms/djangoapps/instructor/views/instructor_dashboard.py
+1
-2
lms/djangoapps/instructor/views/legacy.py
+1
-2
lms/envs/common.py
+7
-5
lms/lib/xblock/runtime.py
+28
-58
lms/lib/xblock/test/test_runtime.py
+19
-11
lms/static/coffee/src/xblock/lms.runtime.v1.coffee
+18
-0
lms/static/js_test.yml
+1
-0
No files found.
cms/djangoapps/contentstore/tests/test_contentstore.py
View file @
6d67d0c6
#pylint: disable=E1101
#pylint: disable=E1101
import
shutil
import
json
import
mock
import
mock
import
shutil
from
textwrap
import
dedent
from
textwrap
import
dedent
...
@@ -503,7 +504,9 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -503,7 +504,9 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
This verifies that a video caption url is as we expect it to be
This verifies that a video caption url is as we expect it to be
"""
"""
resp
=
self
.
_test_preview
(
Location
(
'i4x'
,
'edX'
,
'toy'
,
'video'
,
'sample_video'
,
None
))
resp
=
self
.
_test_preview
(
Location
(
'i4x'
,
'edX'
,
'toy'
,
'video'
,
'sample_video'
,
None
))
self
.
assertContains
(
resp
,
'data-caption-asset-path="/c4x/edX/toy/asset/subs_"'
)
self
.
assertEquals
(
resp
.
status_code
,
200
)
content
=
json
.
loads
(
resp
.
content
)
self
.
assertIn
(
'data-caption-asset-path="/c4x/edX/toy/asset/subs_"'
,
content
[
'html'
])
def
_test_preview
(
self
,
location
):
def
_test_preview
(
self
,
location
):
""" Preview test case. """
""" Preview test case. """
...
@@ -514,7 +517,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -514,7 +517,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
locator
=
loc_mapper
()
.
translate_location
(
locator
=
loc_mapper
()
.
translate_location
(
course_items
[
0
]
.
location
.
course_id
,
location
,
True
,
True
course_items
[
0
]
.
location
.
course_id
,
location
,
True
,
True
)
)
resp
=
self
.
client
.
get_
html
(
locator
.
url_reverse
(
'xblock'
))
resp
=
self
.
client
.
get_
fragment
(
locator
.
url_reverse
(
'xblock'
))
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
# TODO: uncomment when preview no longer has locations being returned.
# TODO: uncomment when preview no longer has locations being returned.
# _test_no_locations(self, resp)
# _test_no_locations(self, resp)
...
...
cms/djangoapps/contentstore/tests/utils.py
View file @
6d67d0c6
...
@@ -57,6 +57,13 @@ class AjaxEnabledTestClient(Client):
...
@@ -57,6 +57,13 @@ class AjaxEnabledTestClient(Client):
"""
"""
return
self
.
get
(
path
,
data
or
{},
follow
,
HTTP_ACCEPT
=
"application/json"
,
**
extra
)
return
self
.
get
(
path
,
data
or
{},
follow
,
HTTP_ACCEPT
=
"application/json"
,
**
extra
)
def
get_fragment
(
self
,
path
,
data
=
None
,
follow
=
False
,
**
extra
):
"""
Convenience method for client.get which sets the accept type to application/x-fragment+json
"""
return
self
.
get
(
path
,
data
or
{},
follow
,
HTTP_ACCEPT
=
"application/x-fragment+json"
,
**
extra
)
@override_settings
(
MODULESTORE
=
TEST_MODULESTORE
)
@override_settings
(
MODULESTORE
=
TEST_MODULESTORE
)
class
CourseTestCase
(
ModuleStoreTestCase
):
class
CourseTestCase
(
ModuleStoreTestCase
):
...
...
cms/djangoapps/contentstore/views/item.py
View file @
6d67d0c6
"""Views for items (modules)."""
"""Views for items (modules)."""
import
hashlib
import
logging
import
logging
from
uuid
import
uuid4
from
uuid
import
uuid4
from
collections
import
OrderedDict
from
functools
import
partial
from
functools
import
partial
from
static_replace
import
replace_static_urls
from
static_replace
import
replace_static_urls
from
xmodule_modifiers
import
wrap_xblock
from
xmodule_modifiers
import
wrap_xblock
from
django.conf
import
settings
from
django.core.exceptions
import
PermissionDenied
from
django.core.exceptions
import
PermissionDenied
from
django.contrib.auth.decorators
import
login_required
from
django.contrib.auth.decorators
import
login_required
from
django.http
import
HttpResponseBadRequest
from
django.http
import
HttpResponseBadRequest
,
HttpResponse
from
django.utils.translation
import
ugettext
as
_
from
django.views.decorators.http
import
require_http_methods
from
django.views.decorators.http
import
require_http_methods
from
xblock.fields
import
Scope
from
xblock.fields
import
Scope
from
xblock.fragment
import
Fragment
from
xblock.core
import
XBlock
from
xblock.core
import
XBlock
import
xmodule.x_module
from
xmodule.modulestore.django
import
modulestore
,
loc_mapper
from
xmodule.modulestore.django
import
modulestore
,
loc_mapper
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
,
InvalidLocationError
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
,
InvalidLocationError
from
xmodule.modulestore.inheritance
import
own_metadata
from
xmodule.modulestore.inheritance
import
own_metadata
from
xmodule.modulestore.locator
import
BlockUsageLocator
from
xmodule.modulestore.locator
import
BlockUsageLocator
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.x_module
import
prefer_xmodules
from
util.json_request
import
expect_json
,
JsonResponse
from
util.json_request
import
expect_json
,
JsonResponse
from
util.string_utils
import
str_to_bool
from
util.string_utils
import
str_to_bool
...
@@ -31,10 +36,10 @@ from ..utils import get_modulestore
...
@@ -31,10 +36,10 @@ from ..utils import get_modulestore
from
.access
import
has_course_access
from
.access
import
has_course_access
from
.helpers
import
_xmodule_recurse
from
.helpers
import
_xmodule_recurse
from
preview
import
handler_prefix
,
get_preview_html
from
contentstore.views.preview
import
get_preview_fragment
from
edxmako.shortcuts
import
render_to_
response
,
render_to_
string
from
edxmako.shortcuts
import
render_to_string
from
models.settings.course_grading
import
CourseGradingModel
from
models.settings.course_grading
import
CourseGradingModel
from
django.utils.translation
import
ugettext
as
_
from
cms.lib.xblock.runtime
import
handler_url
__all__
=
[
'orphan_handler'
,
'xblock_handler'
]
__all__
=
[
'orphan_handler'
,
'xblock_handler'
]
...
@@ -43,6 +48,22 @@ log = logging.getLogger(__name__)
...
@@ -43,6 +48,22 @@ log = logging.getLogger(__name__)
CREATE_IF_NOT_FOUND
=
[
'course_info'
]
CREATE_IF_NOT_FOUND
=
[
'course_info'
]
# In order to allow descriptors to use a handler url, we need to
# monkey-patch the x_module library.
# TODO: Remove this code when Runtimes are no longer created by modulestores
xmodule
.
x_module
.
descriptor_global_handler_url
=
handler_url
def
hash_resource
(
resource
):
"""
Hash a :class:`xblock.fragment.FragmentResource
"""
md5
=
hashlib
.
md5
()
for
data
in
resource
:
md5
.
update
(
data
)
return
md5
.
hexdigest
()
# pylint: disable=unused-argument
# pylint: disable=unused-argument
@require_http_methods
((
"DELETE"
,
"GET"
,
"PUT"
,
"POST"
))
@require_http_methods
((
"DELETE"
,
"GET"
,
"PUT"
,
"POST"
))
@login_required
@login_required
...
@@ -88,34 +109,52 @@ def xblock_handler(request, tag=None, package_id=None, branch=None, version_guid
...
@@ -88,34 +109,52 @@ def xblock_handler(request, tag=None, package_id=None, branch=None, version_guid
old_location
=
loc_mapper
()
.
translate_locator_to_location
(
locator
)
old_location
=
loc_mapper
()
.
translate_locator_to_location
(
locator
)
if
request
.
method
==
'GET'
:
if
request
.
method
==
'GET'
:
if
'application/json'
in
request
.
META
.
get
(
'HTTP_ACCEPT'
,
'application/json'
):
accept_header
=
request
.
META
.
get
(
'HTTP_ACCEPT'
,
'application/json'
)
fields
=
request
.
REQUEST
.
get
(
'fields'
,
''
)
.
split
(
','
)
if
'graderType'
in
fields
:
if
'application/x-fragment+json'
in
accept_header
:
# right now can't combine output of this w/ output of _get_module_info, but worthy goal
return
JsonResponse
(
CourseGradingModel
.
get_section_grader_type
(
locator
))
# TODO: pass fields to _get_module_info and only return those
rsp
=
_get_module_info
(
locator
)
return
JsonResponse
(
rsp
)
else
:
component
=
modulestore
()
.
get_item
(
old_location
)
component
=
modulestore
()
.
get_item
(
old_location
)
# Wrap the generated fragment in the xmodule_editor div so that the javascript
# Wrap the generated fragment in the xmodule_editor div so that the javascript
# can bind to it correctly
# can bind to it correctly
component
.
runtime
.
wrappers
.
append
(
partial
(
wrap_xblock
,
handler_prefix
))
component
.
runtime
.
wrappers
.
append
(
partial
(
wrap_xblock
,
'StudioRuntime'
))
try
:
try
:
content
=
component
.
render
(
'studio_view'
)
.
content
editor_fragment
=
component
.
render
(
'studio_view'
)
# catch exceptions indiscriminately, since after this point they escape the
# catch exceptions indiscriminately, since after this point they escape the
# dungeon and surface as uneditable, unsaveable, and undeletable
# dungeon and surface as uneditable, unsaveable, and undeletable
# component-goblins.
# component-goblins.
except
Exception
as
exc
:
# pylint: disable=W0703
except
Exception
as
exc
:
# pylint: disable=W0703
log
.
debug
(
"Unable to render studio_view for
%
r"
,
component
,
exc_info
=
True
)
log
.
debug
(
"Unable to render studio_view for
%
r"
,
component
,
exc_info
=
True
)
content
=
render_to_string
(
'html_error.html'
,
{
'message'
:
str
(
exc
)})
editor_fragment
=
Fragment
(
render_to_string
(
'html_error.html'
,
{
'message'
:
str
(
exc
)}))
modulestore
()
.
save_xmodule
(
component
)
preview_fragment
=
get_preview_fragment
(
request
,
component
)
return
render_to_response
(
'component.html'
,
{
hashed_resources
=
OrderedDict
()
'preview'
:
get_preview_html
(
request
,
component
),
for
resource
in
editor_fragment
.
resources
+
preview_fragment
.
resources
:
'editor'
:
content
,
hashed_resources
[
hash_resource
(
resource
)]
=
resource
'label'
:
component
.
display_name
or
component
.
category
,
return
JsonResponse
({
'html'
:
render_to_string
(
'component.html'
,
{
'preview'
:
preview_fragment
.
content
,
'editor'
:
editor_fragment
.
content
,
'label'
:
component
.
display_name
or
component
.
scope_ids
.
block_type
,
}),
'resources'
:
hashed_resources
.
items
()
})
})
elif
'application/json'
in
accept_header
:
fields
=
request
.
REQUEST
.
get
(
'fields'
,
''
)
.
split
(
','
)
if
'graderType'
in
fields
:
# right now can't combine output of this w/ output of _get_module_info, but worthy goal
return
JsonResponse
(
CourseGradingModel
.
get_section_grader_type
(
locator
))
# TODO: pass fields to _get_module_info and only return those
rsp
=
_get_module_info
(
locator
)
return
JsonResponse
(
rsp
)
else
:
return
HttpResponse
(
status
=
406
)
elif
request
.
method
==
'DELETE'
:
elif
request
.
method
==
'DELETE'
:
delete_children
=
str_to_bool
(
request
.
REQUEST
.
get
(
'recurse'
,
'False'
))
delete_children
=
str_to_bool
(
request
.
REQUEST
.
get
(
'recurse'
,
'False'
))
delete_all_versions
=
str_to_bool
(
request
.
REQUEST
.
get
(
'all_versions'
,
'False'
))
delete_all_versions
=
str_to_bool
(
request
.
REQUEST
.
get
(
'all_versions'
,
'False'
))
...
@@ -281,7 +320,7 @@ def _create_item(request):
...
@@ -281,7 +320,7 @@ def _create_item(request):
data
=
None
data
=
None
template_id
=
request
.
json
.
get
(
'boilerplate'
)
template_id
=
request
.
json
.
get
(
'boilerplate'
)
if
template_id
is
not
None
:
if
template_id
is
not
None
:
clz
=
XBlock
.
load_class
(
category
,
select
=
prefer_xmodules
)
clz
=
parent
.
runtime
.
load_block_type
(
category
)
if
clz
is
not
None
:
if
clz
is
not
None
:
template
=
clz
.
get_template
(
template_id
)
template
=
clz
.
get_template
(
template_id
)
if
template
is
not
None
:
if
template
is
not
None
:
...
...
cms/djangoapps/contentstore/views/preview.py
View file @
6d67d0c6
import
logging
import
logging
import
hashlib
from
functools
import
partial
from
functools
import
partial
from
django.conf
import
settings
from
django.conf
import
settings
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
django.http
import
Http404
,
HttpResponseBadRequest
from
django.http
import
Http404
,
HttpResponseBadRequest
from
django.contrib.auth.decorators
import
login_required
from
django.contrib.auth.decorators
import
login_required
from
edxmako.shortcuts
import
render_to_
response
,
render_to_
string
from
edxmako.shortcuts
import
render_to_string
from
xmodule_modifiers
import
replace_static_urls
,
wrap_xblock
from
xmodule_modifiers
import
replace_static_urls
,
wrap_xblock
from
xmodule.error_module
import
ErrorDescriptor
from
xmodule.error_module
import
ErrorDescriptor
...
@@ -15,6 +16,7 @@ from xmodule.x_module import ModuleSystem
...
@@ -15,6 +16,7 @@ from xmodule.x_module import ModuleSystem
from
xblock.runtime
import
KvsFieldData
from
xblock.runtime
import
KvsFieldData
from
xblock.django.request
import
webob_to_django_response
,
django_to_webob_request
from
xblock.django.request
import
webob_to_django_response
,
django_to_webob_request
from
xblock.exceptions
import
NoSuchHandlerError
from
xblock.exceptions
import
NoSuchHandlerError
from
xblock.fragment
import
Fragment
from
lms.lib.xblock.field_data
import
LmsFieldData
from
lms.lib.xblock.field_data
import
LmsFieldData
from
lms.lib.xblock.runtime
import
quote_slashes
,
unquote_slashes
from
lms.lib.xblock.runtime
import
quote_slashes
,
unquote_slashes
...
@@ -33,20 +35,6 @@ __all__ = ['preview_handler']
...
@@ -33,20 +35,6 @@ __all__ = ['preview_handler']
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
def
handler_prefix
(
block
,
handler
=
''
,
suffix
=
''
):
"""
Return a url prefix for XBlock handler_url. The full handler_url
should be '{prefix}/{handler}/{suffix}?{query}'.
Trailing `/`s are removed from the returned url.
"""
return
reverse
(
'preview_handler'
,
kwargs
=
{
'usage_id'
:
quote_slashes
(
unicode
(
block
.
scope_ids
.
usage_id
)
.
encode
(
'utf-8'
)),
'handler'
:
handler
,
'suffix'
:
suffix
,
})
.
rstrip
(
'/?'
)
@login_required
@login_required
def
preview_handler
(
request
,
usage_id
,
handler
,
suffix
=
''
):
def
preview_handler
(
request
,
usage_id
,
handler
,
suffix
=
''
):
"""
"""
...
@@ -91,7 +79,11 @@ class PreviewModuleSystem(ModuleSystem): # pylint: disable=abstract-method
...
@@ -91,7 +79,11 @@ class PreviewModuleSystem(ModuleSystem): # pylint: disable=abstract-method
An XModule ModuleSystem for use in Studio previews
An XModule ModuleSystem for use in Studio previews
"""
"""
def
handler_url
(
self
,
block
,
handler_name
,
suffix
=
''
,
query
=
''
,
thirdparty
=
False
):
def
handler_url
(
self
,
block
,
handler_name
,
suffix
=
''
,
query
=
''
,
thirdparty
=
False
):
return
handler_prefix
(
block
,
handler_name
,
suffix
)
+
'?'
+
query
return
reverse
(
'preview_handler'
,
kwargs
=
{
'usage_id'
:
quote_slashes
(
unicode
(
block
.
scope_ids
.
usage_id
)
.
encode
(
'utf-8'
)),
'handler'
:
handler_name
,
'suffix'
:
suffix
,
})
+
'?'
+
query
def
_preview_module_system
(
request
,
descriptor
):
def
_preview_module_system
(
request
,
descriptor
):
...
@@ -123,7 +115,7 @@ def _preview_module_system(request, descriptor):
...
@@ -123,7 +115,7 @@ def _preview_module_system(request, descriptor):
# Set up functions to modify the fragment produced by student_view
# Set up functions to modify the fragment produced by student_view
wrappers
=
(
wrappers
=
(
# This wrapper wraps the module in the template specified above
# This wrapper wraps the module in the template specified above
partial
(
wrap_xblock
,
handler_prefix
,
display_name_only
=
descriptor
.
location
.
category
==
'static_tab'
),
partial
(
wrap_xblock
,
'PreviewRuntime'
,
display_name_only
=
descriptor
.
location
.
category
==
'static_tab'
),
# This wrapper replaces urls in the output that start with /static
# This wrapper replaces urls in the output that start with /static
# with the correct course-specific url for the static content
# with the correct course-specific url for the static content
...
@@ -153,15 +145,15 @@ def _load_preview_module(request, descriptor):
...
@@ -153,15 +145,15 @@ def _load_preview_module(request, descriptor):
return
descriptor
return
descriptor
def
get_preview_
html
(
request
,
descriptor
):
def
get_preview_
fragment
(
request
,
descriptor
):
"""
"""
Returns the HTML returned by the XModule's student_view,
Returns the HTML returned by the XModule's student_view,
specified by the descriptor and idx.
specified by the descriptor and idx.
"""
"""
module
=
_load_preview_module
(
request
,
descriptor
)
module
=
_load_preview_module
(
request
,
descriptor
)
try
:
try
:
content
=
module
.
render
(
"student_view"
)
.
content
fragment
=
module
.
render
(
"student_view"
)
except
Exception
as
exc
:
# pylint: disable=W0703
except
Exception
as
exc
:
# pylint: disable=W0703
log
.
debug
(
"Unable to render student_view for
%
r"
,
module
,
exc_info
=
True
)
log
.
debug
(
"Unable to render student_view for
%
r"
,
module
,
exc_info
=
True
)
content
=
render_to_string
(
'html_error.html'
,
{
'message'
:
str
(
exc
)}
)
fragment
=
Fragment
(
render_to_string
(
'html_error.html'
,
{
'message'
:
str
(
exc
)})
)
return
cont
ent
return
fragm
ent
cms/lib/xblock/runtime.py
View file @
6d67d0c6
...
@@ -4,7 +4,6 @@ XBlock runtime implementations for edX Studio
...
@@ -4,7 +4,6 @@ XBlock runtime implementations for edX Studio
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
import
xmodule.x_module
from
lms.lib.xblock.runtime
import
quote_slashes
from
lms.lib.xblock.runtime
import
quote_slashes
...
@@ -17,7 +16,7 @@ def handler_url(block, handler_name, suffix='', query='', thirdparty=False):
...
@@ -17,7 +16,7 @@ def handler_url(block, handler_name, suffix='', query='', thirdparty=False):
raise
NotImplementedError
(
"edX Studio doesn't support third-party xblock handler urls"
)
raise
NotImplementedError
(
"edX Studio doesn't support third-party xblock handler urls"
)
url
=
reverse
(
'component_handler'
,
kwargs
=
{
url
=
reverse
(
'component_handler'
,
kwargs
=
{
'usage_id'
:
quote_slashes
(
str
(
block
.
scope_ids
.
usage_id
)),
'usage_id'
:
quote_slashes
(
unicode
(
block
.
scope_ids
.
usage_id
)
.
encode
(
'utf-8'
)),
'handler'
:
handler_name
,
'handler'
:
handler_name
,
'suffix'
:
suffix
,
'suffix'
:
suffix
,
})
.
rstrip
(
'/'
)
})
.
rstrip
(
'/'
)
...
@@ -27,4 +26,3 @@ def handler_url(block, handler_name, suffix='', query='', thirdparty=False):
...
@@ -27,4 +26,3 @@ def handler_url(block, handler_name, suffix='', query='', thirdparty=False):
return
url
return
url
xmodule
.
x_module
.
descriptor_global_handler_url
=
handler_url
cms/static/coffee/spec/main.coffee
View file @
6d67d0c6
...
@@ -28,6 +28,7 @@ requirejs.config({
...
@@ -28,6 +28,7 @@ requirejs.config({
"tinymce"
:
"xmodule_js/common_static/js/vendor/tiny_mce/tiny_mce"
,
"tinymce"
:
"xmodule_js/common_static/js/vendor/tiny_mce/tiny_mce"
,
"jquery.tinymce"
:
"xmodule_js/common_static/js/vendor/tiny_mce/jquery.tinymce"
,
"jquery.tinymce"
:
"xmodule_js/common_static/js/vendor/tiny_mce/jquery.tinymce"
,
"xmodule"
:
"xmodule_js/src/xmodule"
,
"xmodule"
:
"xmodule_js/src/xmodule"
,
"xblock/cms.runtime.v1"
:
"coffee/src/xblock/cms.runtime.v1"
,
"xblock"
:
"xmodule_js/common_static/coffee/src/xblock"
,
"xblock"
:
"xmodule_js/common_static/coffee/src/xblock"
,
"utility"
:
"xmodule_js/common_static/js/src/utility"
,
"utility"
:
"xmodule_js/common_static/js/src/utility"
,
"accessibility"
:
"xmodule_js/common_static/js/src/accessibility_tools"
,
"accessibility"
:
"xmodule_js/common_static/js/src/accessibility_tools"
,
...
...
cms/static/coffee/spec/main_squire.coffee
View file @
6d67d0c6
...
@@ -27,6 +27,7 @@ requirejs.config({
...
@@ -27,6 +27,7 @@ requirejs.config({
"tinymce"
:
"xmodule_js/common_static/js/vendor/tiny_mce/tiny_mce"
,
"tinymce"
:
"xmodule_js/common_static/js/vendor/tiny_mce/tiny_mce"
,
"jquery.tinymce"
:
"xmodule_js/common_static/js/vendor/tiny_mce/jquery.tinymce"
,
"jquery.tinymce"
:
"xmodule_js/common_static/js/vendor/tiny_mce/jquery.tinymce"
,
"xmodule"
:
"xmodule_js/src/xmodule"
,
"xmodule"
:
"xmodule_js/src/xmodule"
,
"xblock/cms.runtime.v1"
:
"coffee/src/xblock/cms.runtime.v1"
,
"xblock"
:
"xmodule_js/common_static/coffee/src/xblock"
,
"xblock"
:
"xmodule_js/common_static/coffee/src/xblock"
,
"utility"
:
"xmodule_js/common_static/js/src/utility"
,
"utility"
:
"xmodule_js/common_static/js/src/utility"
,
"sinon"
:
"xmodule_js/common_static/js/vendor/sinon-1.7.1"
,
"sinon"
:
"xmodule_js/common_static/js/vendor/sinon-1.7.1"
,
...
...
cms/static/coffee/spec/views/module_edit_spec.coffee
View file @
6d67d0c6
define
[
"
coffee/src/views/module_edit"
,
"js/models/module_info"
,
"xmodule"
],
(
ModuleEdit
,
ModuleModel
)
->
define
[
"
jquery"
,
"coffee/src/views/module_edit"
,
"js/models/module_info"
,
"xmodule"
],
(
$
,
ModuleEdit
,
ModuleModel
)
->
describe
"ModuleEdit"
,
->
describe
"ModuleEdit"
,
->
beforeEach
->
beforeEach
->
...
@@ -24,7 +24,7 @@ define ["coffee/src/views/module_edit", "js/models/module_info", "xmodule"], (Mo
...
@@ -24,7 +24,7 @@ define ["coffee/src/views/module_edit", "js/models/module_info", "xmodule"], (Mo
</section>
</section>
</li>
</li>
"""
"""
spyOn
(
$
.
fn
,
'load
'
).
andReturn
(
@
moduleData
)
spyOn
(
$
,
'ajax
'
).
andReturn
(
@
moduleData
)
@
moduleEdit
=
new
ModuleEdit
(
@
moduleEdit
=
new
ModuleEdit
(
el
:
$
(
".component"
)
el
:
$
(
".component"
)
...
@@ -56,14 +56,63 @@ define ["coffee/src/views/module_edit", "js/models/module_info", "xmodule"], (Mo
...
@@ -56,14 +56,63 @@ define ["coffee/src/views/module_edit", "js/models/module_info", "xmodule"], (Mo
beforeEach
->
beforeEach
->
spyOn
(
@
moduleEdit
,
'loadDisplay'
)
spyOn
(
@
moduleEdit
,
'loadDisplay'
)
spyOn
(
@
moduleEdit
,
'delegateEvents'
)
spyOn
(
@
moduleEdit
,
'delegateEvents'
)
spyOn
(
$
.
fn
,
'append'
)
spyOn
(
$
,
'getScript'
)
window
.
loadedXBlockResources
=
undefined
@
moduleEdit
.
render
()
@
moduleEdit
.
render
()
$
.
ajax
.
mostRecentCall
.
args
[
0
].
success
(
html
:
'<div>Response html</div>'
resources
:
[
[
'hash1'
,
{
kind
:
'text'
,
mimetype
:
'text/css'
,
data
:
'inline-css'
}],
[
'hash2'
,
{
kind
:
'url'
,
mimetype
:
'text/css'
,
data
:
'css-url'
}],
[
'hash3'
,
{
kind
:
'text'
,
mimetype
:
'application/javascript'
,
data
:
'inline-js'
}],
[
'hash4'
,
{
kind
:
'url'
,
mimetype
:
'application/javascript'
,
data
:
'js-url'
}],
[
'hash5'
,
{
placement
:
'head'
,
mimetype
:
'text/html'
,
data
:
'head-html'
}],
[
'hash6'
,
{
placement
:
'not-head'
,
mimetype
:
'text/html'
,
data
:
'not-head-html'
}],
]
)
it
"loads the module preview and editor via ajax on the view element"
,
->
it
"loads the module preview and editor via ajax on the view element"
,
->
expect
(
@
moduleEdit
.
$el
.
load
).
toHaveBeenCalledWith
(
"/xblock/
#{
@
moduleEdit
.
model
.
id
}
"
,
jasmine
.
any
(
Function
))
expect
(
$
.
ajax
).
toHaveBeenCalledWith
(
@
moduleEdit
.
$el
.
load
.
mostRecentCall
.
args
[
1
]()
url
:
"/xblock/
#{
@
moduleEdit
.
model
.
id
}
"
type
:
"GET"
headers
:
Accept
:
'application/x-fragment+json'
success
:
jasmine
.
any
(
Function
)
)
expect
(
@
moduleEdit
.
loadDisplay
).
toHaveBeenCalled
()
expect
(
@
moduleEdit
.
loadDisplay
).
toHaveBeenCalled
()
expect
(
@
moduleEdit
.
delegateEvents
).
toHaveBeenCalled
()
expect
(
@
moduleEdit
.
delegateEvents
).
toHaveBeenCalled
()
it
"loads inline css from fragments"
,
->
expect
(
$
(
'head'
).
append
).
toHaveBeenCalledWith
(
"<style type='text/css'>inline-css</style>"
)
it
"loads css urls from fragments"
,
->
expect
(
$
(
'head'
).
append
).
toHaveBeenCalledWith
(
"<link rel='stylesheet' href='css-url' type='text/css'>"
)
it
"loads inline js from fragments"
,
->
expect
(
$
(
'head'
).
append
).
toHaveBeenCalledWith
(
"<script>inline-js</script>"
)
it
"loads js urls from fragments"
,
->
expect
(
$
.
getScript
).
toHaveBeenCalledWith
(
"js-url"
)
it
"loads head html"
,
->
expect
(
$
(
'head'
).
append
).
toHaveBeenCalledWith
(
"head-html"
)
it
"doesn't load body html"
,
->
expect
(
$
.
fn
.
append
).
not
.
toHaveBeenCalledWith
(
'not-head-html'
)
it
"doesn't reload resources"
,
->
count
=
$
(
'head'
).
append
.
callCount
$
.
ajax
.
mostRecentCall
.
args
[
0
].
success
(
html
:
'<div>Response html 2</div>'
resources
:
[
[
'hash1'
,
{
kind
:
'text'
,
mimetype
:
'text/css'
,
data
:
'inline-css'
}],
]
)
expect
(
$
(
'head'
).
append
.
callCount
).
toBe
(
count
)
describe
"loadDisplay"
,
->
describe
"loadDisplay"
,
->
beforeEach
->
beforeEach
->
spyOn
(
XBlock
,
'initializeBlock'
)
spyOn
(
XBlock
,
'initializeBlock'
)
...
...
cms/static/coffee/src/views/module_edit.coffee
View file @
6d67d0c6
define
[
"backbone"
,
"jquery"
,
"underscore"
,
"gettext"
,
"xblock/runtime.v1"
,
define
[
"backbone"
,
"jquery"
,
"underscore"
,
"gettext"
,
"xblock/runtime.v1"
,
"js/views/feedback_notification"
,
"js/views/metadata"
,
"js/collections/metadata"
"js/views/feedback_notification"
,
"js/views/metadata"
,
"js/collections/metadata"
"js/utils/modal"
,
"jquery.inputnumber"
,
"xmodule"
,
"coffee/src/main"
],
"js/utils/modal"
,
"jquery.inputnumber"
,
"xmodule"
,
"coffee/src/main"
,
"xblock/cms.runtime.v1"
],
(
Backbone
,
$
,
_
,
gettext
,
XBlock
,
NotificationView
,
MetadataView
,
MetadataCollection
,
ModalUtils
)
->
(
Backbone
,
$
,
_
,
gettext
,
XBlock
,
NotificationView
,
MetadataView
,
MetadataCollection
,
ModalUtils
)
->
class
ModuleEdit
extends
Backbone
.
View
class
ModuleEdit
extends
Backbone
.
View
tagName
:
'li'
tagName
:
'li'
...
@@ -75,9 +75,37 @@ define ["backbone", "jquery", "underscore", "gettext", "xblock/runtime.v1",
...
@@ -75,9 +75,37 @@ define ["backbone", "jquery", "underscore", "gettext", "xblock/runtime.v1",
render
:
->
render
:
->
if
@
model
.
id
if
@
model
.
id
@
$el
.
load
(
@
model
.
url
(),
=>
$
.
ajax
(
@
loadDisplay
()
url
:
@
model
.
url
()
@
delegateEvents
()
type
:
'GET'
headers
:
Accept
:
'application/x-fragment+json'
success
:
(
data
)
=>
@
$el
.
html
(
data
.
html
)
for
value
in
data
.
resources
do
(
value
)
=>
hash
=
value
[
0
]
if
not
window
.
loadedXBlockResources
?
window
.
loadedXBlockResources
=
[]
if
hash
not
in
window
.
loadedXBlockResources
resource
=
value
[
1
]
switch
resource
.
mimetype
when
"text/css"
switch
resource
.
kind
when
"text"
then
$
(
'head'
).
append
(
"<style type='text/css'>
#{
resource
.
data
}
</style>"
)
when
"url"
then
$
(
'head'
).
append
(
"<link rel='stylesheet' href='
#{
resource
.
data
}
' type='text/css'>"
)
when
"application/javascript"
switch
resource
.
kind
when
"text"
then
$
(
'head'
).
append
(
"<script>
#{
resource
.
data
}
</script>"
)
when
"url"
then
$
.
getScript
(
resource
.
data
)
when
"text/html"
switch
resource
.
placement
when
"head"
then
$
(
'head'
).
append
(
resource
.
data
)
window
.
loadedXBlockResources
.
push
(
hash
)
@
loadDisplay
()
@
delegateEvents
()
)
)
clickSaveButton
:
(
event
)
=>
clickSaveButton
:
(
event
)
=>
...
...
cms/static/coffee/src/xblock/cms.runtime.v1.coffee
0 → 100644
View file @
6d67d0c6
define
[
"jquery"
,
"xblock/runtime.v1"
,
"URI"
],
(
$
,
XBlock
,
URI
)
->
@
PreviewRuntime
=
{}
class
PreviewRuntime
.
v1
extends
XBlock
.
Runtime
.
v1
handlerUrl
:
(
element
,
handlerName
,
suffix
,
query
,
thirdparty
)
->
uri
=
URI
(
"/preview/xblock"
).
segment
(
$
(
@
element
).
data
(
'usage-id'
))
.
segment
(
'handler'
)
.
segment
(
handlerName
)
if
suffix
?
then
uri
.
segment
(
suffix
)
if
query
?
then
uri
.
search
(
query
)
uri
.
toString
()
@
StudioRuntime
=
{}
class
StudioRuntime
.
v1
extends
XBlock
.
Runtime
.
v1
handlerUrl
:
(
element
,
handlerName
,
suffix
,
query
,
thirdparty
)
->
uri
=
URI
(
"/xblock"
).
segment
(
$
(
@
element
).
data
(
'usage-id'
))
.
segment
(
'handler'
)
.
segment
(
handlerName
)
if
suffix
?
then
uri
.
segment
(
suffix
)
if
query
?
then
uri
.
search
(
query
)
uri
.
toString
()
cms/static/js_test.yml
View file @
6d67d0c6
...
@@ -59,7 +59,8 @@ lib_paths:
...
@@ -59,7 +59,8 @@ lib_paths:
-
xmodule_js/common_static/js/vendor/URI.min.js
-
xmodule_js/common_static/js/vendor/URI.min.js
-
xmodule_js/common_static/js/vendor/jquery.smooth-scroll.min.js
-
xmodule_js/common_static/js/vendor/jquery.smooth-scroll.min.js
-
xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js
-
xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js
-
xmodule_js/common_static/coffee/src/xblock
-
xmodule_js/common_static/coffee/src/xblock/
-
xmodule_js/common_static/js/vendor/URI.min.js
# Paths to source JavaScript files
# Paths to source JavaScript files
src_paths
:
src_paths
:
...
...
cms/static/js_test_squire.yml
View file @
6d67d0c6
...
@@ -54,6 +54,8 @@ lib_paths:
...
@@ -54,6 +54,8 @@ lib_paths:
-
xmodule_js/src/xmodule.js
-
xmodule_js/src/xmodule.js
-
xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js
-
xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js
-
xmodule_js/common_static/js/test/i18n.js
-
xmodule_js/common_static/js/test/i18n.js
-
xmodule_js/common_static/coffee/src/xblock/
-
xmodule_js/common_static/js/vendor/URI.min.js
# Paths to source JavaScript files
# Paths to source JavaScript files
src_paths
:
src_paths
:
...
...
cms/static/sass/elements/_xmodules.scss
View file @
6d67d0c6
...
@@ -16,7 +16,7 @@
...
@@ -16,7 +16,7 @@
.xmodule_VideoModule
{
.xmodule_VideoModule
{
// display mode
// display mode
&
.x
module_display
{
&
.x
block-student_view
{
// full screen
// full screen
.video-controls
.add-fullscreen
{
.video-controls
.add-fullscreen
{
...
...
cms/static/sass/views/_static-pages.scss
View file @
6d67d0c6
...
@@ -183,16 +183,16 @@
...
@@ -183,16 +183,16 @@
border-left
:
1px
solid
$mediumGrey
;
border-left
:
1px
solid
$mediumGrey
;
border-right
:
1px
solid
$mediumGrey
;
border-right
:
1px
solid
$mediumGrey
;
.x
module_display
{
.x
block-student_view
{
display
:
none
;
display
:
none
;
}
}
}
}
.new
.x
module_display
{
.new
.x
block-student_view
{
background
:
$yellow
;
background
:
$yellow
;
}
}
.x
module_display
{
.x
block-student_view
{
@include
transition
(
background-color
$tmg-s3
linear
0s
);
@include
transition
(
background-color
$tmg-s3
linear
0s
);
padding
:
20px
20px
22px
;
padding
:
20px
20px
22px
;
font-size
:
24px
;
font-size
:
24px
;
...
...
cms/static/sass/views/_unit.scss
View file @
6d67d0c6
...
@@ -420,7 +420,7 @@ body.course.unit,.view-unit {
...
@@ -420,7 +420,7 @@ body.course.unit,.view-unit {
}
}
}
}
.x
module_display
{
.x
block-student_view
{
padding
:
2
*
$baseline
$baseline
$baseline
;
padding
:
2
*
$baseline
$baseline
$baseline
;
overflow-x
:
auto
;
overflow-x
:
auto
;
...
...
common/djangoapps/xmodule_modifiers.py
View file @
6d67d0c6
...
@@ -15,6 +15,7 @@ from xblock.fragment import Fragment
...
@@ -15,6 +15,7 @@ from xblock.fragment import Fragment
from
xmodule.seq_module
import
SequenceModule
from
xmodule.seq_module
import
SequenceModule
from
xmodule.vertical_module
import
VerticalModule
from
xmodule.vertical_module
import
VerticalModule
from
xmodule.x_module
import
shim_xmodule_js
,
XModuleDescriptor
,
XModule
from
xmodule.x_module
import
shim_xmodule_js
,
XModuleDescriptor
,
XModule
from
lms.lib.xblock.runtime
import
quote_slashes
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -29,26 +30,28 @@ def wrap_fragment(fragment, new_content):
...
@@ -29,26 +30,28 @@ def wrap_fragment(fragment, new_content):
return
wrapper_frag
return
wrapper_frag
def
wrap_xblock
(
handler_prefix
,
block
,
view
,
frag
,
context
,
display_name_only
=
Fals
e
):
# pylint: disable=unused-argument
def
wrap_xblock
(
runtime_class
,
block
,
view
,
frag
,
context
,
display_name_only
=
False
,
extra_data
=
Non
e
):
# pylint: disable=unused-argument
"""
"""
Wraps the results of rendering an XBlock view in a standard <section> with identifying
Wraps the results of rendering an XBlock view in a standard <section> with identifying
data so that the appropriate javascript module can be loaded onto it.
data so that the appropriate javascript module can be loaded onto it.
:param handler_prefix: A function that takes a block and returns the url prefix for
:param runtime_class: The name of the javascript runtime class to use to load this block
the javascript handler_url. This prefix should be able to have {handler_name}/{suffix}?{query}
appended to it to return a valid handler_url
:param block: An XBlock (that may be an XModule or XModuleDescriptor)
:param block: An XBlock (that may be an XModule or XModuleDescriptor)
:param view: The name of the view that rendered the fragment being wrapped
:param view: The name of the view that rendered the fragment being wrapped
:param frag: The :class:`Fragment` to be wrapped
:param frag: The :class:`Fragment` to be wrapped
:param context: The context passed to the view being rendered
:param context: The context passed to the view being rendered
:param display_name_only: If true, don't render the fragment content at all.
:param display_name_only: If true, don't render the fragment content at all.
Instead, just render the `display_name` of `block`
Instead, just render the `display_name` of `block`
:param extra_data: A dictionary with extra data values to be set on the wrapper
"""
"""
if
extra_data
is
None
:
extra_data
=
{}
# If any mixins have been applied, then use the unmixed class
# If any mixins have been applied, then use the unmixed class
class_name
=
getattr
(
block
,
'unmixed_class'
,
block
.
__class__
)
.
__name__
class_name
=
getattr
(
block
,
'unmixed_class'
,
block
.
__class__
)
.
__name__
data
=
{}
data
=
{}
data
.
update
(
extra_data
)
css_classes
=
[
'xblock'
,
'xblock-'
+
view
]
css_classes
=
[
'xblock'
,
'xblock-'
+
view
]
if
isinstance
(
block
,
(
XModule
,
XModuleDescriptor
)):
if
isinstance
(
block
,
(
XModule
,
XModuleDescriptor
)):
...
@@ -65,14 +68,15 @@ def wrap_xblock(handler_prefix, block, view, frag, context, display_name_only=Fa
...
@@ -65,14 +68,15 @@ def wrap_xblock(handler_prefix, block, view, frag, context, display_name_only=Fa
if
frag
.
js_init_fn
:
if
frag
.
js_init_fn
:
data
[
'init'
]
=
frag
.
js_init_fn
data
[
'init'
]
=
frag
.
js_init_fn
data
[
'runtime-class'
]
=
runtime_class
data
[
'runtime-version'
]
=
frag
.
js_init_version
data
[
'runtime-version'
]
=
frag
.
js_init_version
data
[
'handler-prefix'
]
=
handler_prefix
(
block
)
data
[
'block-type'
]
=
block
.
scope_ids
.
block_type
data
[
'block-type'
]
=
block
.
scope_ids
.
block_type
data
[
'usage-id'
]
=
quote_slashes
(
unicode
(
block
.
scope_ids
.
usage_id
)
.
encode
(
'utf-8'
))
template_context
=
{
template_context
=
{
'content'
:
block
.
display_name
if
display_name_only
else
frag
.
content
,
'content'
:
block
.
display_name
if
display_name_only
else
frag
.
content
,
'classes'
:
css_classes
,
'classes'
:
css_classes
,
'data_attributes'
:
' '
.
join
(
'data-{}="{}"'
.
format
(
key
,
value
)
for
key
,
value
in
data
.
items
()),
'data_attributes'
:
' '
.
join
(
u
'data-{}="{}"'
.
format
(
key
,
value
)
for
key
,
value
in
data
.
items
()),
}
}
return
wrap_fragment
(
frag
,
render_to_string
(
'xblock_wrapper.html'
,
template_context
))
return
wrap_fragment
(
frag
,
render_to_string
(
'xblock_wrapper.html'
,
template_context
))
...
...
common/static/coffee/spec/xblock/core_spec.coffee
View file @
6d67d0c6
...
@@ -2,9 +2,9 @@ describe "XBlock", ->
...
@@ -2,9 +2,9 @@ describe "XBlock", ->
beforeEach
->
beforeEach
->
setFixtures
"""
setFixtures
"""
<div>
<div>
<div class='xblock' id='vA' data-runtime-version="A" data-init="initFnA" data-name="a-name"/>
<div class='xblock' id='vA' data-runtime-version="A" data-
runtime-class="TestRuntime" data-
init="initFnA" data-name="a-name"/>
<div>
<div>
<div class='xblock' id='vZ' data-runtime-version="Z" data-init="initFnZ"/>
<div class='xblock' id='vZ' data-runtime-version="Z" data-
runtime-class="TestRuntime" data-
init="initFnZ"/>
</div>
</div>
<div class='xblock' id='missing-version' data-init='initFnA' data-name='no-version'/>
<div class='xblock' id='missing-version' data-init='initFnA' data-name='no-version'/>
<div class='xblock' id='missing-init' data-runtime-version="A" data-name='no-init'/>
<div class='xblock' id='missing-init' data-runtime-version="A" data-name='no-init'/>
...
@@ -13,8 +13,11 @@ describe "XBlock", ->
...
@@ -13,8 +13,11 @@ describe "XBlock", ->
describe
"initializeBlock"
,
->
describe
"initializeBlock"
,
->
beforeEach
->
beforeEach
->
XBlock
.
runtime
.
vA
=
jasmine
.
createSpy
().
andReturn
(
'runtimeA'
)
window
.
TestRuntime
=
{}
XBlock
.
runtime
.
vZ
=
jasmine
.
createSpy
().
andReturn
(
'runtimeZ'
)
@
runtimeA
=
{
name
:
'runtimeA'
}
@
runtimeZ
=
{
name
:
'runtimeZ'
}
TestRuntime
.
vA
=
jasmine
.
createSpy
().
andReturn
(
@
runtimeA
)
TestRuntime
.
vZ
=
jasmine
.
createSpy
().
andReturn
(
@
runtimeZ
)
window
.
initFnA
=
jasmine
.
createSpy
()
window
.
initFnA
=
jasmine
.
createSpy
()
window
.
initFnZ
=
jasmine
.
createSpy
()
window
.
initFnZ
=
jasmine
.
createSpy
()
...
@@ -28,12 +31,12 @@ describe "XBlock", ->
...
@@ -28,12 +31,12 @@ describe "XBlock", ->
@
missingInitBlock
=
XBlock
.
initializeBlock
(
$
(
'#missing-init'
)[
0
])
@
missingInitBlock
=
XBlock
.
initializeBlock
(
$
(
'#missing-init'
)[
0
])
it
"loads the right runtime version"
,
->
it
"loads the right runtime version"
,
->
expect
(
XBlock
.
r
untime
.
vA
).
toHaveBeenCalledWith
(
$
(
'#vA'
)[
0
],
@
fakeChildren
)
expect
(
TestR
untime
.
vA
).
toHaveBeenCalledWith
(
$
(
'#vA'
)[
0
],
@
fakeChildren
)
expect
(
XBlock
.
r
untime
.
vZ
).
toHaveBeenCalledWith
(
$
(
'#vZ'
)[
0
],
@
fakeChildren
)
expect
(
TestR
untime
.
vZ
).
toHaveBeenCalledWith
(
$
(
'#vZ'
)[
0
],
@
fakeChildren
)
it
"loads the right init function"
,
->
it
"loads the right init function"
,
->
expect
(
window
.
initFnA
).
toHaveBeenCalledWith
(
'runtimeA'
,
$
(
'#vA'
)[
0
])
expect
(
window
.
initFnA
).
toHaveBeenCalledWith
(
@
runtimeA
,
$
(
'#vA'
)[
0
])
expect
(
window
.
initFnZ
).
toHaveBeenCalledWith
(
'runtimeZ'
,
$
(
'#vZ'
)[
0
])
expect
(
window
.
initFnZ
).
toHaveBeenCalledWith
(
@
runtimeZ
,
$
(
'#vZ'
)[
0
])
it
"loads when missing versions"
,
->
it
"loads when missing versions"
,
->
expect
(
@
missingVersionBlock
.
element
).
toBe
(
$
(
'#missing-version'
))
expect
(
@
missingVersionBlock
.
element
).
toBe
(
$
(
'#missing-version'
))
...
...
common/static/coffee/spec/xblock/runtime.v1_spec.coffee
View file @
6d67d0c6
describe
"XBlock.
r
untime.v1"
,
->
describe
"XBlock.
R
untime.v1"
,
->
beforeEach
->
beforeEach
->
setFixtures
"""
setFixtures
"""
<div class='xblock' data-handler-prefix='/xblock/fake-usage-id/handler'/>
<div class='xblock' data-handler-prefix='/xblock/fake-usage-id/handler'/>
...
@@ -10,9 +10,7 @@ describe "XBlock.runtime.v1", ->
...
@@ -10,9 +10,7 @@ describe "XBlock.runtime.v1", ->
@
element
=
$
(
'.xblock'
)[
0
]
@
element
=
$
(
'.xblock'
)[
0
]
@
runtime
=
XBlock
.
runtime
.
v1
(
@
element
,
@
children
)
@
runtime
=
new
XBlock
.
Runtime
.
v1
(
@
element
,
@
children
)
it
"provides a handler url"
,
->
expect
(
@
runtime
.
handlerUrl
(
@
element
,
'foo'
)).
toBe
(
'/xblock/fake-usage-id/handler/foo'
)
it
"provides a list of children"
,
->
it
"provides a list of children"
,
->
expect
(
@
runtime
.
children
).
toBe
(
@
children
)
expect
(
@
runtime
.
children
).
toBe
(
@
children
)
...
...
common/static/coffee/src/xblock/core.coffee
View file @
6d67d0c6
@
XBlock
=
@
XBlock
=
r
untime
:
{}
R
untime
:
{}
initializeBlock
:
(
element
)
->
initializeBlock
:
(
element
)
->
$element
=
$
(
element
)
$element
=
$
(
element
)
children
=
@
initializeBlocks
(
$element
)
children
=
@
initializeBlocks
(
$element
)
runtime
=
$element
.
data
(
"runtime-class"
)
version
=
$element
.
data
(
"runtime-version"
)
version
=
$element
.
data
(
"runtime-version"
)
initFnName
=
$element
.
data
(
"init"
)
initFnName
=
$element
.
data
(
"init"
)
if
version
?
and
initFnName
?
if
runtime
?
and
version
?
and
initFnName
?
runtime
=
@
runtime
[
"v
#{
version
}
"
](
element
,
children
)
runtime
=
new
window
[
runtime
]
[
"v
#{
version
}
"
](
element
,
children
)
initFn
=
window
[
initFnName
]
initFn
=
window
[
initFnName
]
block
=
initFn
(
runtime
,
element
)
?
{}
block
=
initFn
(
runtime
,
element
)
?
{}
else
else
elementTag
=
$
(
'<div>'
).
append
(
$element
.
clone
()).
html
();
elementTag
=
$
(
'<div>'
).
append
(
$element
.
clone
()).
html
();
console
.
log
(
"Block
#{
elementTag
}
is missing data-runtime-version or data-init, and can't be initialized"
)
console
.
log
(
"Block
#{
elementTag
}
is missing data-runtime
, data-runtime
-version or data-init, and can't be initialized"
)
block
=
{}
block
=
{}
block
.
element
=
element
block
.
element
=
element
...
...
common/static/coffee/src/xblock/runtime.v1.coffee
View file @
6d67d0c6
@
XBlock
.
runtime
.
v1
=
(
element
,
children
)
->
class
XBlock
.
Runtime
.
v1
childMap
=
{}
constructor
:
(
@
element
,
@
children
)
->
$
.
each
children
,
(
idx
,
child
)
->
@
childMap
=
{}
childMap
[
child
.
name
]
=
child
$
.
each
@
children
,
(
idx
,
child
)
=>
@
childMap
[
child
.
name
]
=
child
return
{
# Generate the handler url for the specified handler.
#
# element is the html element containing the xblock requesting the url
# handlerName is the name of the handler
# suffix is the optional url suffix to include in the handler url
# query is an optional query-string (note, this should not include a preceding ? or &)
handlerUrl
:
(
element
,
handlerName
,
suffix
,
query
)
->
handlerPrefix
=
$
(
element
).
data
(
"handler-prefix"
)
suffix
=
if
suffix
?
then
"/
#{
suffix
}
"
else
''
query
=
if
query
?
then
"?
#{
query
}
"
else
''
"
#{
handlerPrefix
}
/
#{
handlerName
}#{
suffix
}#{
query
}
"
# A list of xblock children of this element
children
:
children
# A map of name -> child for the xblock children of this element
childMap
:
childMap
}
lms/djangoapps/courseware/module_render.py
View file @
6d67d0c6
...
@@ -21,7 +21,7 @@ from courseware.access import has_access, get_user_role
...
@@ -21,7 +21,7 @@ from courseware.access import has_access, get_user_role
from
courseware.masquerade
import
setup_masquerade
from
courseware.masquerade
import
setup_masquerade
from
courseware.model_data
import
FieldDataCache
,
DjangoKeyValueStore
from
courseware.model_data
import
FieldDataCache
,
DjangoKeyValueStore
from
lms.lib.xblock.field_data
import
LmsFieldData
from
lms.lib.xblock.field_data
import
LmsFieldData
from
lms.lib.xblock.runtime
import
LmsModuleSystem
,
handler_prefix
,
unquote_slashes
from
lms.lib.xblock.runtime
import
LmsModuleSystem
,
unquote_slashes
from
edxmako.shortcuts
import
render_to_string
from
edxmako.shortcuts
import
render_to_string
from
psychometrics.psychoanalyze
import
make_psychometrics_data_update_handler
from
psychometrics.psychoanalyze
import
make_psychometrics_data_update_handler
from
student.models
import
anonymous_id_for_user
,
user_by_anonymous_id
from
student.models
import
anonymous_id_for_user
,
user_by_anonymous_id
...
@@ -339,7 +339,7 @@ def get_module_for_descriptor_internal(user, descriptor, field_data_cache, cours
...
@@ -339,7 +339,7 @@ def get_module_for_descriptor_internal(user, descriptor, field_data_cache, cours
# Wrap the output display in a single div to allow for the XModule
# Wrap the output display in a single div to allow for the XModule
# javascript to be bound correctly
# javascript to be bound correctly
if
wrap_xmodule_display
is
True
:
if
wrap_xmodule_display
is
True
:
block_wrappers
.
append
(
partial
(
wrap_xblock
,
partial
(
handler_prefix
,
course_id
)
))
block_wrappers
.
append
(
partial
(
wrap_xblock
,
'LmsRuntime'
,
extra_data
=
{
'course-id'
:
course_id
}
))
# TODO (cpennington): When modules are shared between courses, the static
# TODO (cpennington): When modules are shared between courses, the static
# prefix is going to have to be specific to the module, not the directory
# prefix is going to have to be specific to the module, not the directory
...
@@ -379,7 +379,8 @@ def get_module_for_descriptor_internal(user, descriptor, field_data_cache, cours
...
@@ -379,7 +379,8 @@ def get_module_for_descriptor_internal(user, descriptor, field_data_cache, cours
# As we have the time to manually test more modules, we can add to the list
# As we have the time to manually test more modules, we can add to the list
# of modules that get the per-course anonymized id.
# of modules that get the per-course anonymized id.
is_pure_xblock
=
isinstance
(
descriptor
,
XBlock
)
and
not
isinstance
(
descriptor
,
XModuleDescriptor
)
is_pure_xblock
=
isinstance
(
descriptor
,
XBlock
)
and
not
isinstance
(
descriptor
,
XModuleDescriptor
)
is_lti_module
=
not
is_pure_xblock
and
issubclass
(
descriptor
.
module_class
,
LTIModule
)
module_class
=
getattr
(
descriptor
,
'module_class'
,
None
)
is_lti_module
=
not
is_pure_xblock
and
issubclass
(
module_class
,
LTIModule
)
if
is_pure_xblock
or
is_lti_module
:
if
is_pure_xblock
or
is_lti_module
:
anonymous_student_id
=
anonymous_id_for_user
(
user
,
course_id
)
anonymous_student_id
=
anonymous_id_for_user
(
user
,
course_id
)
else
:
else
:
...
...
lms/djangoapps/instructor/views/instructor_dashboard.py
View file @
6d67d0c6
...
@@ -24,7 +24,6 @@ from django_comment_client.utils import has_forum_access
...
@@ -24,7 +24,6 @@ from django_comment_client.utils import has_forum_access
from
django_comment_common.models
import
FORUM_ROLE_ADMINISTRATOR
from
django_comment_common.models
import
FORUM_ROLE_ADMINISTRATOR
from
student.models
import
CourseEnrollment
from
student.models
import
CourseEnrollment
from
bulk_email.models
import
CourseAuthorization
from
bulk_email.models
import
CourseAuthorization
from
lms.lib.xblock.runtime
import
handler_prefix
from
.tools
import
get_units_with_due_date
,
title_or_url
from
.tools
import
get_units_with_due_date
,
title_or_url
...
@@ -206,7 +205,7 @@ def _section_send_email(course_id, access, course):
...
@@ -206,7 +205,7 @@ def _section_send_email(course_id, access, course):
ScopeIds
(
None
,
None
,
None
,
'i4x://dummy_org/dummy_course/html/dummy_name'
)
ScopeIds
(
None
,
None
,
None
,
'i4x://dummy_org/dummy_course/html/dummy_name'
)
)
)
fragment
=
course
.
system
.
render
(
html_module
,
'studio_view'
)
fragment
=
course
.
system
.
render
(
html_module
,
'studio_view'
)
fragment
=
wrap_xblock
(
partial
(
handler_prefix
,
course_id
),
html_module
,
'studio_view'
,
fragment
,
None
)
fragment
=
wrap_xblock
(
'LmsRuntime'
,
html_module
,
'studio_view'
,
fragment
,
None
,
extra_data
=
{
"course-id"
:
course_id
}
)
email_editor
=
fragment
.
content
email_editor
=
fragment
.
content
section_data
=
{
section_data
=
{
'section_key'
:
'send_email'
,
'section_key'
:
'send_email'
,
...
...
lms/djangoapps/instructor/views/legacy.py
View file @
6d67d0c6
...
@@ -61,7 +61,6 @@ import track.views
...
@@ -61,7 +61,6 @@ import track.views
from
xblock.field_data
import
DictFieldData
from
xblock.field_data
import
DictFieldData
from
xblock.fields
import
ScopeIds
from
xblock.fields
import
ScopeIds
from
django.utils.translation
import
ugettext
as
_u
from
django.utils.translation
import
ugettext
as
_u
from
lms.lib.xblock.runtime
import
handler_prefix
from
microsite_configuration.middleware
import
MicrositeConfiguration
from
microsite_configuration.middleware
import
MicrositeConfiguration
...
@@ -848,7 +847,7 @@ def instructor_dashboard(request, course_id):
...
@@ -848,7 +847,7 @@ def instructor_dashboard(request, course_id):
ScopeIds
(
None
,
None
,
None
,
'i4x://dummy_org/dummy_course/html/dummy_name'
)
ScopeIds
(
None
,
None
,
None
,
'i4x://dummy_org/dummy_course/html/dummy_name'
)
)
)
fragment
=
html_module
.
render
(
'studio_view'
)
fragment
=
html_module
.
render
(
'studio_view'
)
fragment
=
wrap_xblock
(
partial
(
handler_prefix
,
course_id
),
html_module
,
'studio_view'
,
fragment
,
None
)
fragment
=
wrap_xblock
(
'LmsRuntime'
,
html_module
,
'studio_view'
,
fragment
,
None
,
extra_data
=
{
"course-id"
:
course_id
}
)
email_editor
=
fragment
.
content
email_editor
=
fragment
.
content
# Enable instructor email only if the following conditions are met:
# Enable instructor email only if the following conditions are met:
...
...
lms/envs/common.py
View file @
6d67d0c6
...
@@ -750,6 +750,7 @@ main_vendor_js = [
...
@@ -750,6 +750,7 @@ main_vendor_js = [
'js/vendor/ova/ova.js'
,
'js/vendor/ova/ova.js'
,
'js/vendor/ova/catch/js/catch.js'
,
'js/vendor/ova/catch/js/catch.js'
,
'js/vendor/ova/catch/js/handlebars-1.1.2.js'
'js/vendor/ova/catch/js/handlebars-1.1.2.js'
'js/vendor/URI.min.js'
]
]
discussion_js
=
sorted
(
rooted_glob
(
COMMON_ROOT
/
'static'
,
'coffee/src/discussion/**/*.js'
))
discussion_js
=
sorted
(
rooted_glob
(
COMMON_ROOT
/
'static'
,
'coffee/src/discussion/**/*.js'
))
...
@@ -815,17 +816,18 @@ PIPELINE_CSS = {
...
@@ -815,17 +816,18 @@ PIPELINE_CSS = {
}
}
common_js
=
set
(
rooted_glob
(
COMMON_ROOT
/
'static'
,
'coffee/src/**/*.js'
))
-
set
(
courseware_js
+
discussion_js
+
staff_grading_js
+
open_ended_js
+
notes_js
+
instructor_dash_js
)
project_js
=
set
(
rooted_glob
(
PROJECT_ROOT
/
'static'
,
'coffee/src/**/*.js'
))
-
set
(
courseware_js
+
discussion_js
+
staff_grading_js
+
open_ended_js
+
notes_js
+
instructor_dash_js
)
# test_order: Determines the position of this chunk of javascript on
# test_order: Determines the position of this chunk of javascript on
# the jasmine test page
# the jasmine test page
PIPELINE_JS
=
{
PIPELINE_JS
=
{
'application'
:
{
'application'
:
{
# Application will contain all paths not in courseware_only_js
# Application will contain all paths not in courseware_only_js
'source_filenames'
:
sorted
(
'source_filenames'
:
sorted
(
common_js
)
+
sorted
(
project_js
)
+
[
set
(
rooted_glob
(
COMMON_ROOT
/
'static'
,
'coffee/src/**/*.js'
)
+
rooted_glob
(
PROJECT_ROOT
/
'static'
,
'coffee/src/**/*.js'
))
-
set
(
courseware_js
+
discussion_js
+
staff_grading_js
+
open_ended_js
+
notes_js
+
instructor_dash_js
)
)
+
[
'js/form.ext.js'
,
'js/form.ext.js'
,
'js/my_courses_dropdown.js'
,
'js/my_courses_dropdown.js'
,
'js/toggle_login_modal.js'
,
'js/toggle_login_modal.js'
,
...
...
lms/lib/xblock/runtime.py
View file @
6d67d0c6
...
@@ -58,63 +58,6 @@ def unquote_slashes(text):
...
@@ -58,63 +58,6 @@ def unquote_slashes(text):
return
re
.
sub
(
r'(;;|;_)'
,
_unquote_slashes
,
text
)
return
re
.
sub
(
r'(;;|;_)'
,
_unquote_slashes
,
text
)
def
handler_url
(
course_id
,
block
,
handler
,
suffix
=
''
,
query
=
''
,
thirdparty
=
False
):
"""
Return an XBlock handler url for the specified course, block and handler.
If handler is an empty string, this function is being used to create a
prefix of the general URL, which is assumed to be followed by handler name
and suffix.
If handler is specified, then it is checked for being a valid handler
function, and ValueError is raised if not.
"""
view_name
=
'xblock_handler'
if
handler
:
# Be sure this is really a handler.
func
=
getattr
(
block
,
handler
,
None
)
if
not
func
:
raise
ValueError
(
"{!r} is not a function name"
.
format
(
handler
))
if
not
getattr
(
func
,
"_is_xblock_handler"
,
False
):
raise
ValueError
(
"{!r} is not a handler name"
.
format
(
handler
))
if
thirdparty
:
view_name
=
'xblock_handler_noauth'
url
=
reverse
(
view_name
,
kwargs
=
{
'course_id'
:
course_id
,
'usage_id'
:
quote_slashes
(
unicode
(
block
.
scope_ids
.
usage_id
)
.
encode
(
'utf-8'
)),
'handler'
:
handler
,
'suffix'
:
suffix
,
})
# If suffix is an empty string, remove the trailing '/'
if
not
suffix
:
url
=
url
.
rstrip
(
'/'
)
# If there is a query string, append it
if
query
:
url
+=
'?'
+
query
return
url
def
handler_prefix
(
course_id
,
block
):
"""
Returns a prefix for use by the Javascript handler_url function.
The prefix is a valid handler url after the handler name is slash-appended
to it.
"""
# This depends on handler url having the handler_name as the final piece of the url
# so that leaving an empty handler_name really does leave the opportunity to append
# the handler_name on the frontend
# This is relied on by the xblock/runtime.v1.coffee frontend handlerUrl function
return
handler_url
(
course_id
,
block
,
''
)
.
rstrip
(
'/?'
)
class
LmsHandlerUrls
(
object
):
class
LmsHandlerUrls
(
object
):
"""
"""
A runtime mixin that provides a handler_url function that routes
A runtime mixin that provides a handler_url function that routes
...
@@ -127,7 +70,34 @@ class LmsHandlerUrls(object):
...
@@ -127,7 +70,34 @@ class LmsHandlerUrls(object):
# pylint: disable=no-member
# pylint: disable=no-member
def
handler_url
(
self
,
block
,
handler_name
,
suffix
=
''
,
query
=
''
,
thirdparty
=
False
):
def
handler_url
(
self
,
block
,
handler_name
,
suffix
=
''
,
query
=
''
,
thirdparty
=
False
):
"""See :method:`xblock.runtime:Runtime.handler_url`"""
"""See :method:`xblock.runtime:Runtime.handler_url`"""
return
handler_url
(
self
.
course_id
,
block
,
handler_name
,
suffix
=
''
,
query
=
''
,
thirdparty
=
thirdparty
)
view_name
=
'xblock_handler'
if
handler_name
:
# Be sure this is really a handler.
func
=
getattr
(
block
,
handler_name
,
None
)
if
not
func
:
raise
ValueError
(
"{!r} is not a function name"
.
format
(
handler_name
))
if
not
getattr
(
func
,
"_is_xblock_handler"
,
False
):
raise
ValueError
(
"{!r} is not a handler name"
.
format
(
handler_name
))
if
thirdparty
:
view_name
=
'xblock_handler_noauth'
url
=
reverse
(
view_name
,
kwargs
=
{
'course_id'
:
self
.
course_id
,
'usage_id'
:
quote_slashes
(
unicode
(
block
.
scope_ids
.
usage_id
)
.
encode
(
'utf-8'
)),
'handler'
:
handler_name
,
'suffix'
:
suffix
,
})
# If suffix is an empty string, remove the trailing '/'
if
not
suffix
:
url
=
url
.
rstrip
(
'/'
)
# If there is a query string, append it
if
query
:
url
+=
'?'
+
query
return
url
class
LmsModuleSystem
(
LmsHandlerUrls
,
ModuleSystem
):
# pylint: disable=abstract-method
class
LmsModuleSystem
(
LmsHandlerUrls
,
ModuleSystem
):
# pylint: disable=abstract-method
...
...
lms/lib/xblock/test/test_runtime.py
View file @
6d67d0c6
...
@@ -6,7 +6,7 @@ from ddt import ddt, data
...
@@ -6,7 +6,7 @@ from ddt import ddt, data
from
mock
import
Mock
from
mock
import
Mock
from
unittest
import
TestCase
from
unittest
import
TestCase
from
urlparse
import
urlparse
from
urlparse
import
urlparse
from
lms.lib.xblock.runtime
import
quote_slashes
,
unquote_slashes
,
handler_url
from
lms.lib.xblock.runtime
import
quote_slashes
,
unquote_slashes
,
LmsModuleSystem
TEST_STRINGS
=
[
TEST_STRINGS
=
[
''
,
''
,
...
@@ -41,23 +41,31 @@ class TestHandlerUrl(TestCase):
...
@@ -41,23 +41,31 @@ class TestHandlerUrl(TestCase):
def
setUp
(
self
):
def
setUp
(
self
):
self
.
block
=
Mock
()
self
.
block
=
Mock
()
self
.
course_id
=
"org/course/run"
self
.
course_id
=
"org/course/run"
self
.
runtime
=
LmsModuleSystem
(
static_url
=
'/static'
,
track_function
=
Mock
(),
get_module
=
Mock
(),
render_template
=
Mock
(),
replace_urls
=
str
,
course_id
=
self
.
course_id
,
)
def
test_trailing_characters
(
self
):
def
test_trailing_characters
(
self
):
self
.
assertFalse
(
handler_url
(
self
.
course_id
,
self
.
block
,
'handler'
)
.
endswith
(
'?'
))
self
.
assertFalse
(
self
.
runtime
.
handler_url
(
self
.
block
,
'handler'
)
.
endswith
(
'?'
))
self
.
assertFalse
(
handler_url
(
self
.
course_id
,
self
.
block
,
'handler'
)
.
endswith
(
'/'
))
self
.
assertFalse
(
self
.
runtime
.
handler_url
(
self
.
block
,
'handler'
)
.
endswith
(
'/'
))
self
.
assertFalse
(
handler_url
(
self
.
course_id
,
self
.
block
,
'handler'
,
'suffix'
)
.
endswith
(
'?'
))
self
.
assertFalse
(
self
.
runtime
.
handler_url
(
self
.
block
,
'handler'
,
'suffix'
)
.
endswith
(
'?'
))
self
.
assertFalse
(
handler_url
(
self
.
course_id
,
self
.
block
,
'handler'
,
'suffix'
)
.
endswith
(
'/'
))
self
.
assertFalse
(
self
.
runtime
.
handler_url
(
self
.
block
,
'handler'
,
'suffix'
)
.
endswith
(
'/'
))
self
.
assertFalse
(
handler_url
(
self
.
course_id
,
self
.
block
,
'handler'
,
'suffix'
,
'query'
)
.
endswith
(
'?'
))
self
.
assertFalse
(
self
.
runtime
.
handler_url
(
self
.
block
,
'handler'
,
'suffix'
,
'query'
)
.
endswith
(
'?'
))
self
.
assertFalse
(
handler_url
(
self
.
course_id
,
self
.
block
,
'handler'
,
'suffix'
,
'query'
)
.
endswith
(
'/'
))
self
.
assertFalse
(
self
.
runtime
.
handler_url
(
self
.
block
,
'handler'
,
'suffix'
,
'query'
)
.
endswith
(
'/'
))
self
.
assertFalse
(
handler_url
(
self
.
course_id
,
self
.
block
,
'handler'
,
query
=
'query'
)
.
endswith
(
'?'
))
self
.
assertFalse
(
self
.
runtime
.
handler_url
(
self
.
block
,
'handler'
,
query
=
'query'
)
.
endswith
(
'?'
))
self
.
assertFalse
(
handler_url
(
self
.
course_id
,
self
.
block
,
'handler'
,
query
=
'query'
)
.
endswith
(
'/'
))
self
.
assertFalse
(
self
.
runtime
.
handler_url
(
self
.
block
,
'handler'
,
query
=
'query'
)
.
endswith
(
'/'
))
def
_parsed_query
(
self
,
query_string
):
def
_parsed_query
(
self
,
query_string
):
"""Return the parsed query string from a handler_url generated with the supplied query_string"""
"""Return the parsed query string from a handler_url generated with the supplied query_string"""
return
urlparse
(
handler_url
(
self
.
course_id
,
self
.
block
,
'handler'
,
query
=
query_string
))
.
query
return
urlparse
(
self
.
runtime
.
handler_url
(
self
.
block
,
'handler'
,
query
=
query_string
))
.
query
def
test_query_string
(
self
):
def
test_query_string
(
self
):
self
.
assertIn
(
'foo=bar'
,
self
.
_parsed_query
(
'foo=bar'
))
self
.
assertIn
(
'foo=bar'
,
self
.
_parsed_query
(
'foo=bar'
))
...
@@ -66,7 +74,7 @@ class TestHandlerUrl(TestCase):
...
@@ -66,7 +74,7 @@ class TestHandlerUrl(TestCase):
def
_parsed_path
(
self
,
handler_name
=
'handler'
,
suffix
=
''
):
def
_parsed_path
(
self
,
handler_name
=
'handler'
,
suffix
=
''
):
"""Return the parsed path from a handler_url with the supplied handler_name and suffix"""
"""Return the parsed path from a handler_url with the supplied handler_name and suffix"""
return
urlparse
(
handler_url
(
self
.
course_id
,
self
.
block
,
handler_name
,
suffix
=
suffix
))
.
path
return
urlparse
(
self
.
runtime
.
handler_url
(
self
.
block
,
handler_name
,
suffix
=
suffix
))
.
path
def
test_suffix
(
self
):
def
test_suffix
(
self
):
self
.
assertTrue
(
self
.
_parsed_path
(
suffix
=
"foo"
)
.
endswith
(
'foo'
))
self
.
assertTrue
(
self
.
_parsed_path
(
suffix
=
"foo"
)
.
endswith
(
'foo'
))
...
...
lms/static/coffee/src/xblock/lms.runtime.v1.coffee
0 → 100644
View file @
6d67d0c6
@
LmsRuntime
=
{}
class
LmsRuntime
.
v1
extends
XBlock
.
Runtime
.
v1
handlerUrl
:
(
element
,
handlerName
,
suffix
,
query
,
thirdparty
)
->
courseId
=
$
(
@
element
).
data
(
"course-id"
)
usageId
=
$
(
@
element
).
data
(
"usage-id"
)
handlerAuth
=
if
thirdparty
then
"handler_noauth"
else
"handler"
uri
=
URI
(
'/courses'
).
segment
(
courseId
)
.
segment
(
'xblock'
)
.
segment
(
usageId
)
.
segment
(
handlerAuth
)
.
segment
(
handlerName
)
if
suffix
?
then
uri
.
segment
(
suffix
)
if
query
?
then
uri
.
search
(
query
)
uri
.
toString
()
lms/static/js_test.yml
View file @
6d67d0c6
...
@@ -40,6 +40,7 @@ lib_paths:
...
@@ -40,6 +40,7 @@ lib_paths:
-
xmodule_js/common_static/js/vendor/jquery.cookie.js
-
xmodule_js/common_static/js/vendor/jquery.cookie.js
-
xmodule_js/common_static/js/vendor/flot/jquery.flot.js
-
xmodule_js/common_static/js/vendor/flot/jquery.flot.js
-
xmodule_js/common_static/js/vendor/CodeMirror/codemirror.js
-
xmodule_js/common_static/js/vendor/CodeMirror/codemirror.js
-
xmodule_js/common_static/js/vendor/URI.min.js
-
xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js
-
xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js
-
xmodule_js/common_static/coffee/src/xblock
-
xmodule_js/common_static/coffee/src/xblock
-
xmodule_js/src/capa/
-
xmodule_js/src/capa/
...
...
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