Skip to content

Commit

Permalink
SAK-30431 - Initial commit for lti.frameResize support
Browse files Browse the repository at this point in the history
  • Loading branch information
csev committed Mar 2, 2016
1 parent ccef29e commit c88c8ef
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 7 deletions.
27 changes: 27 additions & 0 deletions basiclti/basiclti-docs/resources/docs/sakai-api-test/resize.htm
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<html>
<head>
<title>Testing lti.frameResize</title>
</head>
<body>
<h1>Testing lti.frameResize</h1>
<p>
This script sends the "lti.frameResize" to its parent
frame using <b>parent.postMessage()</b> to sneak across
origins. You can just link to this file with any
key and secret to test the behavior.
</p>
<script>
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
function zap() {
var num = getRandomInt(200,800);
console.log("Sending "+num);
parent.postMessage(JSON.stringify({
subject: "lti.frameResize",
height: num
}), "*");
}
</script>
<button onclick="zap();return false;" value="click">Press to Send Random Height</button><br/>
</body>
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,13 @@ public void doView(RenderRequest request, RenderResponse response)
text.append("try { portalMaximizeTool(); } catch (err) { }\n");
text.append("</script>\n");
}
text.append("<iframe ");
String submit_uuid = UUID.randomUUID().toString().replace("-","_");
text.append("<iframe id=\"LtiLaunchFrame_");
text.append(submit_uuid);
text.append("\" height=\"");
if ( frameHeight == null ) frameHeight = "1200";
text.append("height=\""+frameHeight+"\" \n");
text.append(frameHeight);
text.append("\" \n");
text.append("width=\"100%\" frameborder=\"0\" marginwidth=\"0\"\n");
text.append("marginheight=\"0\" scrolling=\"auto\"\n");
text.append("src=\""+iframeUrl+"\">\n");
Expand All @@ -264,7 +268,27 @@ public void doView(RenderRequest request, RenderResponse response)
text.append("<a href=\""+iframeUrl+"\">");
text.append(rb.getString("noiframe.press.here"));
text.append("</a>\n");
text.append("</iframe>");
text.append("</iframe>\n");

// Add support for lti resize messages
text.append("<script>\n");
text.append("window.addEventListener('message', function(e) {\n");
text.append(" try {\n");
text.append(" var message = JSON.parse(e.data);\n");
text.append(" var idval = 'LtiLaunchFrame_");
text.append(submit_uuid);
text.append("';\n");
text.append(" if ( message.subject == 'lti.frameResize' ) {\n");
text.append(" var height = message.height;\n");
text.append(" document.getElementById(idval).height = height;\n");
text.append(" console.log('Reveived lti.frameResize height='+height);\n");
text.append(" }\n");
text.append(" } catch (error) {\n");
text.append(" console.log('lti.frameResize of '+idval+' failed height='+height);\n");
text.append(" console.log(e.data);\n");
text.append(" }\n");
text.append("});\n");
text.append("</script>\n");
}
out.println(text);
dPrint("==== doView complete ====");
Expand Down
62 changes: 62 additions & 0 deletions basiclti/docs/POSTMESSAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@

Support for Emerging "lti" postmessages
---------------------------------------

Sakai has support for an informal standard currently supported
by Canvas, Lumen Learning, and others that allows a tool nestled
in an iframe to inform its parent that it needs to so a resize
even if it the parent and frame are served from different origins.

The trick is to use web messaging:

https://www.w3.org/TR/2012/WD-webmessaging-20120313/

The messages and parameters are defined by convention and
hopefully will evolve to being a standard. For example,
to commuicate that a tool has been resized it would
run the following JavaScript:

parent.postMessage(JSON.stringify({
subject: "lti.frameResize",
height: num
}), "*");


Support in Sakai
----------------

It is necessary to add a message listener at each of the points in Sakai
where iframe markup is generated that might include an LTI URL. So far
the following tools have been changed:

* Lessons
* LTI Portlet (External Tool)
* Iframe tool

There may be more iframes that we identify as we go forward.

Use in Sakai Tool Markup
------------------------

Code has been added to headscripts.js to send this message if the
Sakai tool is using setMainFrameHeight() to communicate height changes
to the parent document. The code detects whether or not we are
same or different origins and uses the old resize trick or the
postMessage as appropriate.

So this means that auto-resizing Sakai tools will resize when
placed in an iframe Canvas or a Sakai hosted on a different domain.

Test Harness
------------

To test this in Sakai or in Canvas, you can use this:

https://online.dr-chuck.com/sakai-api-test/resize.htm

Use any key/secret. You press a button and this tool resizes itself
randomly each time the button is presed. Watch the console
to see what is happening.



16 changes: 16 additions & 0 deletions basiclti/web-ifp/src/bundle/vm/main.vm
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,22 @@ $tlang.getString("new.page.launch") </p> <p>
scrolling="auto"
src="$!source">
</iframe>
<script>
window.addEventListener('message', function(e) {
try {
var message = JSON.parse(e.data);
var idval = 'wciframe';
if ( message.subject == 'lti.frameResize' ) {
var height = message.height;
document.getElementById(idval).height = height;
console.log('Reveived lti.frameResize height='+height);
}
} catch (error) {
console.log('lti.frameResize of '+idval+' failed height='+height);
console.log(e.data);
}
});
</script>
</div>
#end
</div>
17 changes: 17 additions & 0 deletions lessonbuilder/tool/src/webapp/templates/ShowItem.html
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,23 @@
marginheight="0"
scrolling="auto">
</iframe>
<script>
window.addEventListener('message', function(e) {
try {
var message = JSON.parse(e.data);
var idval = 'iframe1';
if ( message.subject == 'lti.frameResize' ) {
var height = message.height;
document.getElementById(idval).height = height;
console.log('Reveived lti.frameResize height='+height);
}
} catch (error) {
console.log('lti.frameResize of '+idval+' failed height='+height);
console.log(e.data);
}
});
</script>

</div>
</body>
</html>
72 changes: 68 additions & 4 deletions reference/library/src/webapp/js/headscripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,13 +241,49 @@ function clickOnEnter(event, element)
return true;
}

// SAK-30431 Adapted from Lumen Learning / Bracken Mosbacker
function lti_frameResize(new_height) {
parent.postMessage(JSON.stringify({
subject: "lti.frameResize",
height: new_height
}), "*");
}

function lti_hideLMSNavigation() {
parent.postMessage(JSON.stringify({
subject: "lti.hideModuleNavigation",
show: false
}), "*");
}

function lti_showLMSNavigation() {
parent.postMessage(JSON.stringify({
subject: "lti.showModuleNavigation",
show: true
}), "*");
}

// tell the parent iframe to scroll to top
function lti_scrollParentToTop() {
parent.postMessage(JSON.stringify({
subject: "lti.scrollToTop"
}), "*");
}

// De-bounce the resize activity
var MainFrameHeightTimeOut = false;

// set the parent iframe's height to hold our entire contents
// pass -1 for no max
function setMainFrameHeightWithMax(id, maxHeight)
{
if ( ! inIframe() ) return;
// some browsers need a moment to finish rendering so the height and scroll are correct
setTimeout("setMainFrameHeightNow('"+id+"',"+maxHeight+")", 1);
if ( MainFrameHeightTimeOut ) {
clearTimeout(MainFrameHeightTimeOut);
MainFrameHeightTimeOut = false;
}
MainFrameHeightTimeOut = setTimeout("setMainFrameHeightNow('"+id+"',"+maxHeight+")", 1000);
}

function setMainFrameHeight(id)
Expand All @@ -260,9 +296,26 @@ function setMainFrameHeightNow(id, maxHeight)
{
// If we have been inlined, do nothing
if ( ! inIframe() ) return;
// run the script only if this window's name matches the id parameter
// this tells us that the iframe in parent by the name of 'id' is the one who spawned us
if (typeof window.name !== "undefined" && id !== window.name) return;

// if this window's name matches the id parameter, we on same origin
// If they do not match we are in cross origin and so we need to use
// postMessage() to inform our parent frame
if (typeof window.name !== "undefined" && id !== window.name) {
var height = getDocumentHeight();
try {
parent.postMessage(JSON.stringify({
subject: "lti.frameResize",
height: height,
windowid: id
}), "*");
console.log("lti.frameResize postMessage sent height="+height);
}
catch (error)
{
console.log("lti.frameResize postMessage failed height="+height);
}
return;
}

//SAK-21209 check we can access the document,
//ie this could be a Basic LTI request and therefore we are not allowed
Expand Down Expand Up @@ -347,6 +400,17 @@ if (document.documentElement && document.documentElement.scrollHeight) /* Strict
return Math.max (document.body.scrollHeight, doc_height);
}

/* get height of our window without looking "up" */
function getDocumentHeight ()
{
var doc_height = document.height ? document.height : 0; // Safari uses document.height

if (document.documentElement && document.documentElement.scrollHeight) /* Strict mode */
return Math.max (document.documentElement.scrollHeight, doc_height);
else /* quirks mode */
return Math.max (document.body.scrollHeight, doc_height);
}


// find the object position in its window
// inspired by http://www.quirksmode.org/js/findpos.html
Expand Down
16 changes: 16 additions & 0 deletions web/web-portlet/src/bundle/vm/main.vm
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,22 @@ $tlang.getString("gen.trouble.loading")
scrolling="auto"
src="$!source">
</iframe>
<script>
window.addEventListener('message', function(e) {
try {
var message = JSON.parse(e.data);
var idval = 'id$placement';
if ( message.subject == 'lti.frameResize' ) {
var height = message.height;
document.getElementById(idval).height = height;
console.log('Reveived lti.frameResize height='+height);
}
} catch (error) {
console.log('lti.frameResize of '+idval+' failed height='+height);
console.log(e.data);
}
});
</script>
</div>
#end
</div>

0 comments on commit c88c8ef

Please sign in to comment.