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
39e97225
Commit
39e97225
authored
Dec 09, 2013
by
polesye
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix tests.
parent
2e87b1a6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
27 additions
and
429 deletions
+27
-429
common/lib/xmodule/xmodule/js/spec/lti/lti_spec.js
+0
-370
common/lib/xmodule/xmodule/lti_module.py
+0
-15
common/lib/xmodule/xmodule/tests/test_lti_unit.py
+6
-22
lms/djangoapps/courseware/features/lti.py
+18
-17
lms/djangoapps/courseware/tests/test_lti_integration.py
+1
-4
lms/templates/lti.html
+2
-1
No files found.
common/lib/xmodule/xmodule/js/spec/lti/lti_spec.js
deleted
100644 → 0
View file @
2e87b1a6
/**
* File: constructor.js
*
* Purpose: Jasmine tests for LTI module (front-end part).
*
*
* Because LTI module is constructed so that all methods are available via the
* prototype chain, many times we can test methods without having to
* instantiate a new LTI object.
*/
/*
* "Hence that general is skillful in attack whose opponent does not know what
* to defend; and he is skillful in defense whose opponent does not know what
* to attack."
*
* ~ Sun Tzu
*/
(
function
()
{
var
IN_NEW_WINDOW
=
'true'
,
IN_IFRAME
=
'false'
,
EMPTY_URL
=
''
,
DEFAULT_URL
=
'http://www.example.com'
,
NEW_URL
=
'http://www.example.com/some_book'
;
describe
(
'LTI XModule'
,
function
()
{
describe
(
'LTIConstructor method'
,
function
()
{
describe
(
'[in iframe, new url]'
,
function
()
{
var
lti
;
beforeEach
(
function
()
{
loadFixtures
(
'lti.html'
);
setUpLtiElement
(
$
(
'.lti-wrapper'
),
IN_IFRAME
,
NEW_URL
);
spyOnEvent
(
$
(
'.lti-wrapper'
).
find
(
'.ltiLaunchForm'
),
'submit'
);
lti
=
new
window
.
LTI
(
'.lti-wrapper'
);
});
it
(
'new LTI object contains all properties'
,
function
()
{
expect
(
lti
.
el
).
toBeDefined
();
expect
(
lti
.
el
).
toExist
();
expect
(
lti
.
formEl
).
toBeDefined
();
expect
(
lti
.
formEl
).
toExist
();
expect
(
lti
.
formEl
).
toHaveAttr
(
'action'
);
expect
(
lti
.
ltiEl
).
toBeDefined
();
expect
(
lti
.
ltiEl
).
toExist
();
expect
(
lti
.
formAction
).
toEqual
(
NEW_URL
);
expect
(
lti
.
openInANewPage
).
toEqual
(
false
);
expect
(
lti
.
ajaxUrl
).
toEqual
(
jasmine
.
any
(
String
));
expect
(
'submit'
).
toHaveBeenTriggeredOn
(
lti
.
formEl
);
});
afterEach
(
function
()
{
lti
=
undefined
;
});
});
describe
(
'[in new window, new url]'
,
function
()
{
var
lti
;
beforeEach
(
function
()
{
loadFixtures
(
'lti.html'
);
setUpLtiElement
(
$
(
'.lti-wrapper'
),
IN_NEW_WINDOW
,
NEW_URL
);
lti
=
new
window
.
LTI
(
'.lti-wrapper'
);
});
it
(
'check extra properties and values'
,
function
()
{
expect
(
lti
.
openInANewPage
).
toEqual
(
true
);
expect
(
lti
.
signatureIsNew
).
toBeTruthy
();
expect
(
lti
.
newWindowBtnEl
).
toBeDefined
();
expect
(
lti
.
newWindowBtnEl
).
toExist
();
expect
(
lti
.
disableOpenNewWindowBtn
).
toBe
(
false
);
});
afterEach
(
function
()
{
lti
=
undefined
;
});
});
describe
(
'[in iframe, NO new url]'
,
function
()
{
var
testCases
=
[{
itDescription
:
'URL is blank'
,
action
:
EMPTY_URL
},
{
itDescription
:
'URL is default'
,
action
:
DEFAULT_URL
}];
$
.
each
(
testCases
,
function
(
index
,
test
)
{
it
(
test
.
itDescription
,
function
()
{
var
lti
;
loadFixtures
(
'lti.html'
);
setUpLtiElement
(
$
(
'.lti-wrapper'
),
IN_IFRAME
,
test
.
action
);
lti
=
new
window
.
LTI
(
'.lti-wrapper'
);
expect
(
lti
.
openInANewPage
).
not
.
toBeDefined
();
});
});
});
});
describe
(
'submitFormHandler method'
,
function
()
{
var
thisObj
;
beforeEach
(
function
()
{
thisObj
=
{
signatureIsNew
:
undefined
,
getNewSignature
:
jasmine
.
createSpy
(
'getNewSignature'
),
formEl
:
{
submit
:
jasmine
.
createSpy
(
'submit'
)
}
};
});
it
(
'signature is new'
,
function
()
{
thisObj
.
signatureIsNew
=
true
;
window
.
LTI
.
prototype
.
submitFormHandler
.
call
(
thisObj
);
expect
(
thisObj
.
formEl
.
submit
).
toHaveBeenCalled
();
expect
(
thisObj
.
signatureIsNew
).
toBe
(
false
);
});
it
(
'signature is old'
,
function
()
{
thisObj
.
signatureIsNew
=
false
;
window
.
LTI
.
prototype
.
submitFormHandler
.
call
(
thisObj
);
expect
(
thisObj
.
formEl
.
submit
).
not
.
toHaveBeenCalled
();
expect
(
thisObj
.
signatureIsNew
).
toBe
(
false
);
expect
(
thisObj
.
getNewSignature
).
toHaveBeenCalled
();
});
afterEach
(
function
()
{
thisObj
=
undefined
;
});
});
describe
(
'getNewSignature method'
,
function
()
{
var
lti
;
beforeEach
(
function
()
{
loadFixtures
(
'lti.html'
);
setUpLtiElement
(
$
(
'.lti-wrapper'
),
IN_NEW_WINDOW
,
NEW_URL
);
spyOn
(
$
,
'postWithPrefix'
).
andCallFake
(
function
(
url
,
data
,
callback
)
{
callback
({
input_fields
:
{}
});
}
);
lti
=
new
window
.
LTI
(
'.lti-wrapper'
);
spyOn
(
lti
,
'submitFormHandler'
).
andCallThrough
();
lti
.
submitFormHandler
.
reset
();
spyOn
(
lti
,
'handleAjaxUpdateSignature'
);
});
it
(
'"Open in new page" clicked twice, signature requested once'
,
function
()
{
lti
.
newWindowBtnEl
.
click
();
lti
.
newWindowBtnEl
.
click
();
expect
(
lti
.
submitFormHandler
).
toHaveBeenCalled
();
expect
(
lti
.
submitFormHandler
.
callCount
).
toBe
(
2
);
expect
(
$
.
postWithPrefix
).
toHaveBeenCalledWith
(
lti
.
ajaxUrl
+
'/regenerate_signature'
,
{},
jasmine
.
any
(
Function
)
);
expect
(
lti
.
disableOpenNewWindowBtn
).
toBe
(
true
);
expect
(
lti
.
handleAjaxUpdateSignature
)
.
toHaveBeenCalledWith
({
input_fields
:
{}
});
}
);
afterEach
(
function
()
{
lti
=
undefined
;
});
});
describe
(
'handleAjaxUpdateSignature method'
,
function
()
{
var
lti
,
oldInputFields
,
newInputFields
,
AjaxCallbackData
=
{};
function
fakePostWithPrefix
(
url
,
data
,
callback
)
{
return
callback
(
AjaxCallbackData
);
}
beforeEach
(
function
()
{
oldInputFields
=
{
oauth_nonce
:
'28347958723982798572'
,
oauth_timestamp
:
'2389479832'
,
oauth_signature
:
'89ru3289r3ry283y3r82ryr38yr'
};
newInputFields
=
{
oauth_nonce
:
'ru3902ru239ru'
,
oauth_timestamp
:
'24ru309rur39r8u'
,
oauth_signature
:
'08923ru3082u2rur'
};
AjaxCallbackData
.
error
=
0
;
AjaxCallbackData
.
input_fields
=
newInputFields
;
loadFixtures
(
'lti.html'
);
setUpLtiElement
(
$
(
'.lti-wrapper'
),
IN_NEW_WINDOW
,
NEW_URL
);
spyOn
(
$
,
'postWithPrefix'
).
andCallFake
(
fakePostWithPrefix
);
lti
=
new
window
.
LTI
(
'.lti-wrapper'
);
spyOn
(
lti
,
'submitFormHandler'
).
andCallThrough
();
spyOn
(
lti
,
'handleAjaxUpdateSignature'
).
andCallThrough
();
spyOn
(
lti
.
formEl
,
'submit'
);
spyOn
(
window
.
console
,
'log'
).
andCallThrough
();
lti
.
submitFormHandler
.
reset
();
lti
.
handleAjaxUpdateSignature
.
reset
();
lti
.
formEl
.
submit
.
reset
();
window
.
console
.
log
.
reset
();
});
it
(
'On second click form is updated, and submitted'
,
function
()
{
// Setup initial OAuth values in the form.
lti
.
formEl
.
find
(
"input[name='oauth_nonce']"
)
.
val
(
oldInputFields
.
oauth_nonce
);
lti
.
formEl
.
find
(
"input[name='oauth_timestamp']"
)
.
val
(
oldInputFields
.
oauth_timestamp
);
lti
.
formEl
.
find
(
"input[name='oauth_signature']"
)
.
val
(
oldInputFields
.
oauth_signature
);
// First click. Signature is new. Should just submit the form.
lti
.
newWindowBtnEl
.
click
();
// Initial OAuth values should not have changed.
expect
(
lti
.
formEl
.
find
(
"input[name='oauth_nonce']"
).
val
())
.
toBe
(
oldInputFields
.
oauth_nonce
);
expect
(
lti
.
formEl
.
find
(
"input[name='oauth_timestamp']"
).
val
())
.
toBe
(
oldInputFields
.
oauth_timestamp
);
expect
(
lti
.
formEl
.
find
(
"input[name='oauth_signature']"
).
val
())
.
toBe
(
oldInputFields
.
oauth_signature
);
expect
(
lti
.
submitFormHandler
).
toHaveBeenCalled
();
expect
(
lti
.
submitFormHandler
.
callCount
).
toBe
(
1
);
expect
(
lti
.
handleAjaxUpdateSignature
).
not
.
toHaveBeenCalled
();
expect
(
lti
.
handleAjaxUpdateSignature
.
callCount
).
toBe
(
0
);
expect
(
lti
.
formEl
.
submit
).
toHaveBeenCalled
();
expect
(
lti
.
formEl
.
submit
.
callCount
).
toBe
(
1
);
lti
.
submitFormHandler
.
reset
();
lti
.
handleAjaxUpdateSignature
.
reset
();
lti
.
formEl
.
submit
.
reset
();
// Second click. Signature is old. Should request for a new
// signature, and then submit the form.
lti
.
newWindowBtnEl
.
click
();
expect
(
lti
.
submitFormHandler
).
toHaveBeenCalled
();
expect
(
lti
.
submitFormHandler
.
callCount
).
toBe
(
2
);
expect
(
lti
.
handleAjaxUpdateSignature
).
toHaveBeenCalled
();
expect
(
lti
.
handleAjaxUpdateSignature
.
callCount
).
toBe
(
1
);
expect
(
lti
.
formEl
.
submit
).
toHaveBeenCalled
();
expect
(
lti
.
formEl
.
submit
.
callCount
).
toBe
(
1
);
expect
(
lti
.
disableOpenNewWindowBtn
).
toBe
(
false
);
// The new OAuth values should be in the form.
expect
(
lti
.
formEl
.
find
(
"input[name='oauth_nonce']"
).
val
())
.
toBe
(
newInputFields
.
oauth_nonce
);
expect
(
lti
.
formEl
.
find
(
"input[name='oauth_timestamp']"
).
val
())
.
toBe
(
newInputFields
.
oauth_timestamp
);
expect
(
lti
.
formEl
.
find
(
"input[name='oauth_signature']"
).
val
())
.
toBe
(
newInputFields
.
oauth_signature
);
});
it
(
'invalid response for new OAuth signature'
,
function
()
{
AjaxCallbackData
.
input_fields
=
0
;
AjaxCallbackData
.
error
=
'error'
;
lti
.
newWindowBtnEl
.
click
();
lti
.
submitFormHandler
.
reset
();
lti
.
handleAjaxUpdateSignature
.
reset
();
window
.
console
.
log
.
reset
();
lti
.
formEl
.
submit
.
reset
();
lti
.
newWindowBtnEl
.
click
();
expect
(
lti
.
submitFormHandler
).
toHaveBeenCalled
();
expect
(
lti
.
submitFormHandler
.
callCount
).
toBe
(
1
);
expect
(
lti
.
handleAjaxUpdateSignature
).
toHaveBeenCalled
();
expect
(
lti
.
handleAjaxUpdateSignature
.
callCount
).
toBe
(
1
);
expect
(
window
.
console
.
log
).
toHaveBeenCalledWith
(
jasmine
.
any
(
String
)
);
expect
(
lti
.
formEl
.
submit
).
not
.
toHaveBeenCalled
();
});
afterEach
(
function
()
{
lti
=
undefined
;
oldInputFields
=
undefined
;
newInputFields
=
undefined
;
});
});
});
function
setUpLtiElement
(
element
,
target
,
action
)
{
var
container
,
form
;
container
=
element
.
find
(
'.lti'
);
form
=
container
.
find
(
'.ltiLaunchForm'
);
if
(
target
===
IN_IFRAME
)
{
container
.
data
(
'open_in_a_new_page'
,
'false'
);
form
.
attr
(
'target'
,
'ltiLaunchFrame'
);
}
form
.
attr
(
'action'
,
action
);
// If we have a new proper action (non-default), we create either
// a link that will submit the form, or an iframe that will contain
// the answer of auto submitted form.
if
(
action
!==
EMPTY_URL
&&
action
!==
DEFAULT_URL
)
{
if
(
target
===
IN_NEW_WINDOW
)
{
$
(
'<a />'
,
{
href
:
'#'
,
class
:
'link_lti_new_window'
}).
appendTo
(
container
);
}
else
{
$
(
'<iframe />'
,
{
name
:
'ltiLaunchFrame'
,
class
:
'ltiLaunchFrame'
,
src
:
''
}).
appendTo
(
container
);
}
}
}
}());
common/lib/xmodule/xmodule/lti_module.py
View file @
39e97225
...
...
@@ -292,21 +292,6 @@ class LTIModule(LTIFields, XModule):
"""
return
Response
(
self
.
get_form
(),
content_type
=
'text/html'
)
def
handle_ajax
(
self
,
dispatch
,
__
):
"""
Ajax handler.
Args:
dispatch: string request slug
Returns:
json string
"""
if
dispatch
==
'regenerate_signature'
:
return
json
.
dumps
({
'input_fields'
:
self
.
get_input_fields
()
})
else
:
# return error message
return
json
.
dumps
({
'error'
:
'[handle_ajax]: Unknown Command!'
})
def
get_user_id
(
self
):
user_id
=
self
.
runtime
.
anonymous_student_id
assert
user_id
is
not
None
...
...
common/lib/xmodule/xmodule/tests/test_lti_unit.py
View file @
39e97225
...
...
@@ -229,6 +229,12 @@ class LTIModuleTest(LogicTest):
real_outcome_service_url
=
self
.
xmodule
.
get_outcome_service_url
()
self
.
assertEqual
(
real_outcome_service_url
,
expected_outcome_service_url
)
def
test_get_form_path
(
self
):
expected_form_path
=
self
.
xmodule
.
runtime
.
handler_url
(
self
.
xmodule
,
'preview_handler'
)
.
rstrip
(
'/?'
)
real_form_path
=
self
.
xmodule
.
get_form_path
()
self
.
assertEqual
(
real_form_path
,
expected_form_path
)
def
test_resource_link_id
(
self
):
with
patch
(
'xmodule.lti_module.LTIModule.id'
,
new_callable
=
PropertyMock
)
as
mock_id
:
mock_id
.
return_value
=
self
.
module_id
...
...
@@ -251,28 +257,6 @@ class LTIModuleTest(LogicTest):
def
test_client_key_secret
(
self
):
pass
def
test_handle_ajax
(
self
):
dispatch
=
'regenerate_signature'
data
=
''
self
.
xmodule
.
get_input_fields
=
Mock
(
return_value
=
{
'test_input_field_key'
:
'test_input_field_value'
})
json_dump
=
self
.
xmodule
.
handle_ajax
(
dispatch
,
data
)
expected_json_dump
=
'{"input_fields": {"test_input_field_key": "test_input_field_value"}}'
self
.
assertEqual
(
json
.
loads
(
json_dump
),
json
.
loads
(
expected_json_dump
)
)
def
test_handle_ajax_bad_dispatch
(
self
):
dispatch
=
'bad_dispatch'
data
=
''
self
.
xmodule
.
get_input_fields
=
Mock
(
return_value
=
{
'test_input_field_key'
:
'test_input_field_value'
})
json_dump
=
self
.
xmodule
.
handle_ajax
(
dispatch
,
data
)
expected_json_dump
=
'{"error": "[handle_ajax]: Unknown Command!"}'
self
.
assertEqual
(
json
.
loads
(
json_dump
),
json
.
loads
(
expected_json_dump
)
)
def
test_max_score
(
self
):
self
.
xmodule
.
weight
=
100.0
...
...
lms/djangoapps/courseware/features/lti.py
View file @
39e97225
...
...
@@ -13,22 +13,22 @@ from courseware.tests.factories import InstructorFactory
@step
(
'I view the LTI and error is shown$'
)
def
lti_is_not_rendered
(
_step
):
# error is shown
assert
world
.
is_css_present
(
'.error_message'
)
assert
world
.
is_css_present
(
'.error_message'
,
wait_time
=
0
)
# iframe is not presented
assert
not
world
.
is_css_present
(
'iframe'
)
assert
not
world
.
is_css_present
(
'iframe'
,
wait_time
=
0
)
# link is not presented
assert
not
world
.
is_css_present
(
'.link_lti_new_window'
)
assert
not
world
.
is_css_present
(
'.link_lti_new_window'
,
wait_time
=
0
)
def
check_lti_iframe_content
(
text
):
#inside iframe test content is presented
location
=
world
.
scenario_dict
[
'LTI'
]
.
location
.
html_id
()
iframe_name
=
'lti
Launch
Frame-'
+
location
iframe_name
=
'ltiFrame-'
+
location
with
world
.
browser
.
get_iframe
(
iframe_name
)
as
iframe
:
# iframe does not contain functions from terrain/ui_helpers.py
assert
iframe
.
is_element_present_by_css
(
'.result'
,
wait_time
=
5
)
assert
iframe
.
is_element_present_by_css
(
'.result'
,
wait_time
=
0
)
assert
(
text
==
world
.
retry_on_exception
(
lambda
:
iframe
.
find_by_css
(
'.result'
)[
0
]
.
text
,
max_attempts
=
5
...
...
@@ -38,18 +38,18 @@ def check_lti_iframe_content(text):
@step
(
'I view the LTI and it is rendered in (.*)$'
)
def
lti_is_rendered
(
_step
,
rendered_in
):
if
rendered_in
.
strip
()
==
'iframe'
:
assert
world
.
is_css_present
(
'iframe'
)
assert
not
world
.
is_css_present
(
'.link_lti_new_window'
)
assert
not
world
.
is_css_present
(
'.error_message'
)
assert
world
.
is_css_present
(
'iframe'
,
wait_time
=
2
)
assert
not
world
.
is_css_present
(
'.link_lti_new_window'
,
wait_time
=
0
)
assert
not
world
.
is_css_present
(
'.error_message'
,
wait_time
=
0
)
# iframe is visible
assert
world
.
css_visible
(
'iframe'
)
check_lti_iframe_content
(
"This is LTI tool. Success."
)
elif
rendered_in
.
strip
()
==
'new page'
:
assert
not
world
.
is_css_present
(
'iframe'
)
assert
world
.
is_css_present
(
'.link_lti_new_window'
)
assert
not
world
.
is_css_present
(
'.error_message'
)
assert
not
world
.
is_css_present
(
'iframe'
,
wait_time
=
2
)
assert
world
.
is_css_present
(
'.link_lti_new_window'
,
wait_time
=
0
)
assert
not
world
.
is_css_present
(
'.error_message'
,
wait_time
=
0
)
check_lti_popup
()
else
:
# incorrent rendered_in parameter
assert
False
...
...
@@ -57,9 +57,9 @@ def lti_is_rendered(_step, rendered_in):
@step
(
'I view the LTI but incorrect_signature warning is rendered$'
)
def
incorrect_lti_is_rendered
(
_step
):
assert
world
.
is_css_present
(
'iframe'
)
assert
not
world
.
is_css_present
(
'.link_lti_new_window'
)
assert
not
world
.
is_css_present
(
'.error_message'
)
assert
world
.
is_css_present
(
'iframe'
,
wait_time
=
2
)
assert
not
world
.
is_css_present
(
'.link_lti_new_window'
,
wait_time
=
0
)
assert
not
world
.
is_css_present
(
'.error_message'
,
wait_time
=
0
)
#inside iframe test content is presented
check_lti_iframe_content
(
"Wrong LTI signature"
)
...
...
@@ -234,10 +234,11 @@ def check_progress(_step, text):
@step
(
'I see graph with total progress "([^"]*)"$'
)
def
see_graph
(
_step
,
progress
):
SELECTOR
=
'grade-detail-graph'
node
=
world
.
browser
.
find_by_xpath
(
'//div[@id="{parent}"]//div[text()="{progress}"]'
.
format
(
XPATH
=
'//div[@id="{parent}"]//div[text()="{progress}"]'
.
format
(
parent
=
SELECTOR
,
progress
=
progress
,
))
)
node
=
world
.
browser
.
find_by_xpath
(
XPATH
)
assert
node
...
...
@@ -259,7 +260,7 @@ def see_value_in_the_gradebook(_step, label, text):
@step
(
'I submit answer to LTI question$'
)
def
click_grade
(
_step
):
location
=
world
.
scenario_dict
[
'LTI'
]
.
location
.
html_id
()
iframe_name
=
'lti
Launch
Frame-'
+
location
iframe_name
=
'ltiFrame-'
+
location
with
world
.
browser
.
get_iframe
(
iframe_name
)
as
iframe
:
iframe
.
find_by_name
(
'submit-button'
)
.
first
.
click
()
assert
iframe
.
is_text_present
(
'LTI consumer (edX) responded with XML content'
)
...
...
lms/djangoapps/courseware/tests/test_lti_integration.py
View file @
39e97225
...
...
@@ -5,8 +5,6 @@ from . import BaseTestXmodule
from
collections
import
OrderedDict
import
mock
import
urllib
from
xmodule.lti_module
import
LTIModule
from
mock
import
Mock
class
TestLTI
(
BaseTestXmodule
):
...
...
@@ -85,7 +83,6 @@ class TestLTI(BaseTestXmodule):
Makes sure that all parameters extracted.
"""
generated_context
=
self
.
item_module
.
render
(
'student_view'
)
.
content
expected_context
=
{
'display_name'
:
self
.
item_module
.
display_name
,
'input_fields'
:
self
.
correct_headers
,
...
...
@@ -93,7 +90,7 @@ class TestLTI(BaseTestXmodule):
'element_id'
:
self
.
item_module
.
location
.
html_id
(),
'launch_url'
:
'http://www.example.com'
,
# default value
'open_in_a_new_page'
:
True
,
'
ajax_url'
:
self
.
item_descriptor
.
xmodule_runtime
.
ajax_url
,
'
form_url'
:
self
.
item_descriptor
.
xmodule_runtime
.
handler_url
(
self
.
item_module
,
'preview_handler'
)
.
rstrip
(
'/?'
)
,
}
self
.
assertEqual
(
...
...
lms/templates/lti.html
View file @
39e97225
...
...
@@ -12,7 +12,7 @@
<h3
class=
"title"
>
${display_name} (${_('External resource')})
</h3>
<p
class=
"lti-link external"
><a
target=
"_blank"
class=
'link_lti_new_window'
href=
"${form_url}"
class=
''
>
<p
class=
"lti-link external"
><a
target=
"_blank"
class=
'link_lti_new_window'
href=
"${form_url}"
>
${_('View resource in a new window')}
<i
class=
"icon-external-link"
></i>
</a></p>
...
...
@@ -21,6 +21,7 @@
## The result of the form submit will be rendered here.
<iframe
class=
"ltiLaunchFrame"
name=
"ltiFrame-${element_id}"
src=
"${form_url}"
></iframe>
% endif
...
...
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