Skip to content

Commit

Permalink
MDL-49609 mod_lti: Refactor content-item and make it work using JS
Browse files Browse the repository at this point in the history
* Rebase and resolve conflicts from initial patch.
* Reorganise contentitem and contentitem_return pages.
* Add capability checks for contentitem and contentitem_return pages.
* Move the building of Content-Item selection request to a local
  lib function.
* Move contentitem_return processing logic to a local lib function.
* Fix type settings update. Content-item checkbox does not get
  checked/unchecked on load.
* Fix  tool settings update. Disabled content-item checkbox gets unchecked when
  tool settings form is submitted.
* Add "Select content" button on load which launches the content item
  selection dialogue.
* Move hardcoded HTML and JS to mustache templates and AMD modules.
* Use standard YUI dialog for displaying the Content-Item selection page.
* Added processing for the following Content-Item properties:
  - text
  - icon
* On ContentItem selection, fill out form with the configuration data retrieved,
  instead of automatically saving the tool and redirecting to the course page.
* Removed section- and sectionreturn-related code since we're not automatically
  adding the tool on ContentItem selection.
* On mod_lti_mod_form, enable configuration fields if they support ContentItem
  selection.
* New form-field module for setting fields using JS
* Change types_config table's 'value' column from char(255) to text
  • Loading branch information
junpataleta committed Sep 23, 2016
1 parent d8f9109 commit c1fae2b
Show file tree
Hide file tree
Showing 20 changed files with 911 additions and 603 deletions.
1 change: 1 addition & 0 deletions mod/lti/amd/build/contentitem.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions mod/lti/amd/build/contentitem_return.min.js
@@ -0,0 +1 @@
define([],function(){return{init:function(a){window!=top&&parent.processContentItemReturnData(a)}}});
1 change: 1 addition & 0 deletions mod/lti/amd/build/form-field.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

122 changes: 122 additions & 0 deletions mod/lti/amd/src/contentitem.js
@@ -0,0 +1,122 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Launches the modal dialogue that contains the iframe that sends the Content-Item selection request to an
* LTI tool provider that supports Content-Item type message.
*
* See template: mod_lti/contentitem
*
* @module mod_lti/contentitem
* @class contentitem
* @package mod_lti
* @copyright 2016 Jun Pataleta <jun@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 3.2
*/
define(['jquery', 'core/notification', 'core/str', 'core/templates', 'mod_lti/form-field', 'core/yui'],
function($, notification, str, templates, FormField) {
var dialogue;
var contentItem = {
/**
* Init function.
*
* @param {string} url The URL for the content item selection.
* @param {object} postData The data to be sent for the content item selection request.
*/
init: function(url, postData) {
var dialogueTitle = '';
str.get_string('selectcontent', 'lti').then(function(title) {
dialogueTitle = title;
var context = {
url: url,
postData: postData
};
return templates.render('mod_lti/contentitem', context);

}).then(function(html, js) {
// Set dialog's body content.
dialogue = new M.core.dialogue({
modal: true,
headerContent: dialogueTitle,
bodyContent: html,
draggable: true,
width: '800px',
height: '600px'
});

// Show dialog.
dialogue.show();

// Destroy after hiding.
dialogue.after('visibleChange', function(e) {
// Going from visible to hidden.
if (e.prevVal && !e.newVal) {
this.destroy();
// Fetch notifications.
notification.fetchNotifications();
}
}, dialogue);

templates.runTemplateJS(js);

}).fail(notification.exception);
}
};

/**
* Array of form fields for LTI tool configuration.
*
* @type {*[]}
*/
var ltiFormFields = [
new FormField('name', FormField.TYPES.TEXT, false, ''),
new FormField('introeditor', FormField.TYPES.EDITOR, false, ''),
new FormField('toolurl', FormField.TYPES.TEXT, true, ''),
new FormField('securetoolurl', FormField.TYPES.TEXT, true, ''),
new FormField('instructorchoiceacceptgrades', FormField.TYPES.CHECKBOX, true, true),
new FormField('instructorchoicesendname', FormField.TYPES.CHECKBOX, true, true),
new FormField('instructorchoicesendemailaddr', FormField.TYPES.CHECKBOX, true, true),
new FormField('instructorcustomparameters', FormField.TYPES.TEXT, true, ''),
new FormField('icon', FormField.TYPES.TEXT, true, ''),
new FormField('secureicon', FormField.TYPES.TEXT, true, ''),
new FormField('launchcontainer', FormField.TYPES.SELECT, true, 0)
];

/**
* Window function that can be called from mod_lti/contentitem_return to close the dialogue and process the return data.
*
* @param {object} returnData The fetched configuration data from the Content-Item selection dialogue.
*/
window.processContentItemReturnData = function(returnData) {
if (dialogue) {
dialogue.hide();
}

// Populate LTI configuration fields from return data.
var index;
for (index in ltiFormFields) {
var field = ltiFormFields[index];
var value = null;
if ($.type(returnData[field.name]) !== 'undefined') {
value = returnData[field.name];
}
field.setFieldValue(value);
}
};

return contentItem;
}
);
40 changes: 40 additions & 0 deletions mod/lti/amd/src/contentitem_return.js
@@ -0,0 +1,40 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Processes the result of LTI tool creation from a Content-Item message type.
*
* @module mod_lti/contentitem_return
* @class contentitem_return
* @package mod_lti
* @copyright 2016 Jun Pataleta <jun@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 3.2
*/
define([], function() {
return {
/**
* Init function.
*
* @param {string} returnData The returned data.
*/
init: function(returnData) {
if (window != top) {
// Send return data to be processed by the parent window.
parent.processContentItemReturnData(returnData);
}
}
};
});
107 changes: 107 additions & 0 deletions mod/lti/amd/src/form-field.js
@@ -0,0 +1,107 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* A module that enables the setting of form field values on the client side.
*
* @module mod_lti/form-field
* @class form-field
* @package mod_lti
* @copyright 2016 Jun Pataleta <jun@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 3.2
*/
define(['jquery'],
function($) {
/**
* Form field class.
*
* @param {string} name Field name.
* @param {number} type The field type.
* @param {boolean} resetIfUndefined Flag to reset the field to the default value if undefined in the return data.
* @param {string|number|boolean} defaultValue The default value to use for the field.
* @constructor
*/
var FormField = function(name, type, resetIfUndefined, defaultValue) {
this.name = name;
this.id = 'id_' + this.name;
this.selector = '#' + this.id;
this.type = type;
this.resetIfUndefined = resetIfUndefined;
this.defaultValue = defaultValue;
};

/**
* Form field types.
*
* @type {{TEXT: number, SELECT: number, CHECKBOX: number, EDITOR: number}}
*/
FormField.TYPES = {
TEXT: 1,
SELECT: 2,
CHECKBOX: 3,
EDITOR: 4
};

/**
* Sets the values for a form field.
*
* @param {string|boolean|number} value The value to be set into the field.
*/
FormField.prototype.setFieldValue = function(value) {
if (value === null) {
if (this.resetIfUndefined) {
value = this.defaultValue;
} else {
// No need set the field value if value is null and there's no need to reset the field.
return;
}
}

switch (this.type) {
case FormField.TYPES.CHECKBOX:
if (value) {
$(this.selector).prop('checked', true);
} else {
$(this.selector).prop('checked', false);
}
break;
case FormField.TYPES.EDITOR:
if ($.type(value.text) !== 'undefined') {
/* global tinyMCE:false */

// Set text in editor's editable content, if applicable.
// Check if it is an Atto editor.
var attoEditor = $(this.selector + 'editable');
if (attoEditor.length) {
attoEditor.html(value.text);
} else if (typeof tinyMCE !== 'undefined') {
// If the editor is not Atto, try to fallback to TinyMCE.
tinyMCE.execInstanceCommand(this.id, 'mceInsertContent', false, value.text);
}

// Set text to actual editor text area.
$(this.selector).val(value.text);
}
break;
default:
$(this.selector).val(value);
break;
}
};

return FormField;
}
);
77 changes: 22 additions & 55 deletions mod/lti/contentitem.php
Expand Up @@ -15,7 +15,7 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Display a page containing an iframe for the content-item selection process.
* Handle sending a user to a tool provider to initiate a content-item selection.
*
* @package mod_lti
* @copyright 2015 Vital Source Technologies http://vitalsource.com
Expand All @@ -24,66 +24,33 @@
*/

require_once('../../config.php');
require_once($CFG->dirroot.'/mod/lti/lib.php');
require_once($CFG->dirroot.'/mod/lti/locallib.php');
require_once($CFG->dirroot . '/mod/lti/lib.php');
require_once($CFG->dirroot . '/mod/lti/locallib.php');

$courseid = required_param('course', PARAM_INT);
$sectionid = required_param('section', PARAM_INT);
$id = required_param('id', PARAM_INT);
$sectionreturn = required_param('sr', PARAM_INT);

$title = optional_param('title', null, PARAM_TEXT);
$courseid = required_param('course', PARAM_INT);
$title = optional_param('title', '', PARAM_TEXT);
$text = optional_param('text', '', PARAM_RAW);

// Check access and capabilities.
$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);

require_login($course);
$context = context_course::instance($courseid);
require_capability('moodle/course:manageactivities', $context);
require_capability('mod/lti:addcoursetool', $context);

$url = new moodle_url('/mod/lti/contentitem.php', array('course' => $courseid));

$contentitem = new moodle_url('/mod/lti/contentitem2.php',
array('course' => $courseid, 'section' => $sectionid, 'id' => $id, 'sr' => $sectionreturn, 'title' => $title));

echo "<p id=\"id_warning\" style=\"display: none; color: red; font-weight: bold; margin-top: 1em; padding-top: 1em;\">\n";
echo get_string('register_warning', 'lti');
echo "\n</p>\n";

echo '<iframe id="contentframe" height="600px" width="100%" src="' . $contentitem->out() . '" onload="doOnload()"></iframe>';

// Output script to make the object tag be as large as possible.
$resize = '
<script type="text/javascript">
//<![CDATA[
function doReveal() {
var el = document.getElementById(\'id_warning\');
el.style.display = \'block\';
}
function doOnload() {
window.clearTimeout(mod_lti_timer);
parent.M.mod_lti.editor.removeLoading();
}
var mod_lti_timer = window.setTimeout(doReveal, 20000);
parent.YUI().use("node", "event", function(Y) {
//Take scrollbars off the outer document to prevent double scroll bar effect
var doc = parent.Y.one("body");
doc.setStyle("overflow", "hidden");
var frame = parent.Y.one("#contentframe");
var padding = 15; //The bottom of the iframe wasn\'t visible on some themes. Probably because of border widths, etc.
var lastHeight;
var resize = function(e) {
var viewportHeight = doc.get("winHeight");
if(lastHeight !== Math.min(doc.get("docHeight"), viewportHeight)){
frame.setStyle("height", viewportHeight - frame.getY() - padding + "px");
lastHeight = Math.min(doc.get("docHeight"), doc.get("winHeight"));
}
};
// Set the return URL. We send the launch container along to help us avoid frames-within-frames when the user returns.
$returnurlparams = [
'course' => $course->id,
'id' => $id,
'sesskey' => sesskey()
];
$returnurl = new \moodle_url('/mod/lti/contentitem_return.php', $returnurlparams);

resize();
// Prepare the request.
$request = lti_build_content_item_selection_request($id, $course, $returnurl, $title, $text, [], []);

parent.Y.on("windowresize", resize);
});
//]]
</script>
';
// Get the launch HTML.
$content = lti_post_launch_html($request->params, $request->url, false);

echo $resize;
echo $content;

0 comments on commit c1fae2b

Please sign in to comment.