2014-08-29 06:10:34 +08:00
|
|
|
package org.bigbluebutton
|
2017-01-25 04:43:28 +08:00
|
|
|
/*
|
2012-10-25 04:04:40 +08:00
|
|
|
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
2012-10-16 02:36:54 +08:00
|
|
|
|
2012-10-25 04:04:40 +08:00
|
|
|
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
2012-10-16 02:36:54 +08:00
|
|
|
|
2012-10-25 04:04:40 +08:00
|
|
|
This program is free software; you can redistribute it and/or modify it under the
|
|
|
|
terms of the GNU Lesser General Public License as published by the Free Software
|
|
|
|
Foundation; either version 3.0 of the License, or (at your option) any later
|
|
|
|
version.
|
2012-10-16 02:36:54 +08:00
|
|
|
|
2012-10-25 04:04:40 +08:00
|
|
|
BigBlueButton 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
|
2012-10-16 02:36:54 +08:00
|
|
|
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
|
|
|
2012-10-25 04:04:40 +08:00
|
|
|
You should have received a copy of the GNU Lesser General Public License along
|
|
|
|
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
2012-10-16 23:22:45 +08:00
|
|
|
|
2016-08-24 04:28:33 +08:00
|
|
|
import java.text.DateFormat
|
|
|
|
import java.text.SimpleDateFormat
|
2012-10-11 23:52:41 +08:00
|
|
|
import java.util.ArrayList
|
|
|
|
import java.util.HashMap
|
|
|
|
import java.util.List
|
|
|
|
import java.util.Map
|
|
|
|
import java.util.Properties
|
|
|
|
|
2012-10-13 04:01:27 +08:00
|
|
|
import org.apache.commons.codec.digest.DigestUtils
|
|
|
|
|
2012-10-11 23:52:41 +08:00
|
|
|
import net.oauth.OAuthMessage
|
2012-10-17 06:09:56 +08:00
|
|
|
import net.oauth.signature.OAuthSignatureMethod
|
|
|
|
import net.oauth.signature.HMAC_SHA1
|
|
|
|
import org.bigbluebutton.lti.Parameter
|
2012-10-11 23:52:41 +08:00
|
|
|
|
|
|
|
class ToolController {
|
|
|
|
private static final String CONTROLLER_NAME = 'ToolController'
|
|
|
|
private static final String RESP_CODE_SUCCESS = 'SUCCESS'
|
|
|
|
private static final String RESP_CODE_FAILED = 'FAILED'
|
2013-01-26 04:10:27 +08:00
|
|
|
private static final String REQUEST_METHOD = "request_method";
|
2014-08-28 22:13:40 +08:00
|
|
|
|
2012-10-11 23:52:41 +08:00
|
|
|
LtiService ltiService
|
2012-10-13 04:01:27 +08:00
|
|
|
BigbluebuttonService bigbluebuttonService
|
2014-08-28 22:13:40 +08:00
|
|
|
|
2014-08-29 06:10:34 +08:00
|
|
|
def test = {
|
|
|
|
log.debug CONTROLLER_NAME + "#test"
|
2014-08-28 22:13:40 +08:00
|
|
|
render(text: "<xml></xml>", contentType: "text/xml", encoding: "UTF-8")
|
|
|
|
}
|
|
|
|
|
2014-08-29 06:10:34 +08:00
|
|
|
def index = {
|
2013-01-29 04:53:27 +08:00
|
|
|
log.debug CONTROLLER_NAME + "#index"
|
2017-12-11 23:47:38 +08:00
|
|
|
if (ltiService.consumerMap == null) {
|
|
|
|
ltiService.initConsumerMap()
|
|
|
|
}
|
2013-04-20 05:52:46 +08:00
|
|
|
setLocalization(params)
|
2013-01-26 04:10:27 +08:00
|
|
|
params.put(REQUEST_METHOD, request.getMethod().toUpperCase())
|
2014-03-27 03:45:05 +08:00
|
|
|
ltiService.logParameters(params)
|
2017-12-11 23:47:38 +08:00
|
|
|
// On get requests render the common cartridge.
|
|
|
|
if (request.get) {
|
|
|
|
render(text: getCartridgeXML(), contentType: "text/xml", encoding: "UTF-8")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// On post request proceed with the launch.
|
|
|
|
def endPoint = ltiService.getScheme(request) + "://" + ltiService.endPoint + "/" + grailsApplication.metadata['app.name'] + "/" + params.get("controller") + (params.get("format") != null ? "." + params.get("format") : "")
|
|
|
|
log.info "endPoint: " + endPoint
|
|
|
|
ArrayList<String> missingParams = new ArrayList<String>()
|
|
|
|
|
|
|
|
if (!hasAllRequiredParams(params, missingParams)) {
|
|
|
|
String missingStr = ""
|
|
|
|
for (String str:missingParams) {
|
|
|
|
missingStr += str + ", ";
|
2013-07-19 03:16:21 +08:00
|
|
|
}
|
2017-12-11 23:47:38 +08:00
|
|
|
return renderError("MissingRequiredParameter", "Missing parameters [$missingStr]")
|
|
|
|
}
|
2014-03-27 03:45:05 +08:00
|
|
|
|
2017-12-11 23:47:38 +08:00
|
|
|
def sanitizedParams = sanitizePrametersForBaseString(params)
|
|
|
|
def consumer = ltiService.getConsumer(params.get(Parameter.CONSUMER_ID))
|
|
|
|
if (ltiService.hasRestrictedAccess()) {
|
|
|
|
if (consumer == null) {
|
|
|
|
return renderError("ConsumerNotFound", "Consumer with id = " + params.get(Parameter.CONSUMER_ID) + " was not found.")
|
2013-01-29 04:53:27 +08:00
|
|
|
}
|
2017-12-11 23:47:38 +08:00
|
|
|
log.debug "Found consumer with key " + consumer.get("key") //+ " and sharedSecret " + consumer.get("secret")
|
|
|
|
}
|
|
|
|
def validSignature = checkValidSignature(params.get(REQUEST_METHOD), endPoint, consumer.get("secret"), sanitizedParams, params.get(Parameter.OAUTH_SIGNATURE))
|
|
|
|
if (ltiService.hasRestrictedAccess()) {
|
|
|
|
if (!validSignature) {
|
|
|
|
log.debug "The message has NOT a valid signature."
|
|
|
|
return renderError("InvalidSignature", "Invalid signature (" + params.get(Parameter.OAUTH_SIGNATURE) + ").")
|
|
|
|
}
|
|
|
|
log.debug "The message has a valid signature."
|
2013-01-26 04:10:27 +08:00
|
|
|
} else {
|
2017-12-11 23:47:38 +08:00
|
|
|
log.debug "Access not restricted, valid signature is not required."
|
|
|
|
}
|
|
|
|
def mode = params.containsKey(Parameter.CUSTOM_MODE)? params.get(Parameter.CUSTOM_MODE): ltiService.mode
|
|
|
|
if (!"extended".equals(mode)) {
|
|
|
|
log.debug "LTI service running in simple mode."
|
|
|
|
def result = doJoinMeeting(params)
|
|
|
|
return
|
2013-01-29 04:53:27 +08:00
|
|
|
}
|
2017-12-11 23:47:38 +08:00
|
|
|
log.debug "LTI service running in extended mode."
|
|
|
|
if (!Boolean.parseBoolean(params.get(Parameter.CUSTOM_RECORD)) && !ltiService.allRecordedByDefault()) {
|
|
|
|
log.debug "Parameter custom_record was not sent; immediately redirecting to BBB session!"
|
|
|
|
def result = doJoinMeeting(params)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
session["params"] = params
|
|
|
|
render(view: "index", model: ['params': params, 'recordingList': getSanitizedRecordings(params), 'ismoderator': bigbluebuttonService.isModerator(params)])
|
2013-01-29 04:53:27 +08:00
|
|
|
}
|
2014-03-27 03:45:05 +08:00
|
|
|
|
2014-08-29 06:10:34 +08:00
|
|
|
def join = {
|
2013-01-29 04:53:27 +08:00
|
|
|
if( ltiService.consumerMap == null) ltiService.initConsumerMap()
|
2013-01-26 04:10:27 +08:00
|
|
|
log.debug CONTROLLER_NAME + "#join"
|
2017-12-11 23:47:38 +08:00
|
|
|
def result
|
2013-01-26 04:10:27 +08:00
|
|
|
def sessionParams = session["params"]
|
2013-01-29 04:53:27 +08:00
|
|
|
if( sessionParams != null ) {
|
|
|
|
log.debug "params: " + params
|
|
|
|
log.debug "sessionParams: " + sessionParams
|
|
|
|
result = doJoinMeeting(sessionParams)
|
|
|
|
} else {
|
|
|
|
result = new HashMap<String, String>()
|
2017-12-11 23:47:38 +08:00
|
|
|
result.put("messageKey", "InvalidSession")
|
|
|
|
result.put("message", "Invalid session. User can not execute this action.")
|
2014-08-28 22:13:40 +08:00
|
|
|
}
|
2017-12-11 23:47:38 +08:00
|
|
|
if (result != null && result.containsKey("messageKey")) {
|
|
|
|
log.debug "Error [messageKey:'" + result.get("messageKey") + "', message:'" + result.get("message") + "']"
|
|
|
|
render(view: "error", model: ['messageKey': result.get("messageKey"), 'message': result.get("message")])
|
2013-01-29 04:53:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-29 06:10:34 +08:00
|
|
|
def publish = {
|
2013-01-29 04:53:27 +08:00
|
|
|
log.debug CONTROLLER_NAME + "#publish"
|
|
|
|
Map<String, String> result
|
|
|
|
def sessionParams = session["params"]
|
2013-01-29 05:58:55 +08:00
|
|
|
if( sessionParams == null ) {
|
|
|
|
result = new HashMap<String, String>()
|
2017-12-11 23:47:38 +08:00
|
|
|
result.put("messageKey", "InvalidSession")
|
|
|
|
result.put("message", "Invalid session. User can not execute this action.")
|
2013-01-29 05:58:55 +08:00
|
|
|
} else if ( !bigbluebuttonService.isModerator(sessionParams) ) {
|
|
|
|
result = new HashMap<String, String>()
|
2017-12-11 23:47:38 +08:00
|
|
|
result.put("messageKey", "NotAllowed")
|
|
|
|
result.put("message", "User not allowed to execute this action.")
|
2013-01-29 05:58:55 +08:00
|
|
|
} else {
|
2017-12-11 23:47:38 +08:00
|
|
|
// Execute the publish command
|
2013-01-29 04:53:27 +08:00
|
|
|
result = bigbluebuttonService.doPublishRecordings(params)
|
2014-08-28 22:13:40 +08:00
|
|
|
}
|
2017-12-11 23:47:38 +08:00
|
|
|
if( result.containsKey("messageKey")) {
|
|
|
|
log.debug "Error [messageKey:'" + result.get("messageKey") + "', message:'" + result.get("message") + "']"
|
|
|
|
render(view: "error", model: ['messageKey': result.get("messageKey"), 'message': result.get("message")])
|
2013-01-29 04:53:27 +08:00
|
|
|
} else {
|
2016-08-24 23:07:35 +08:00
|
|
|
render(view: "index", model: ['params': sessionParams, 'recordingList': getSanitizedRecordings(sessionParams), 'ismoderator': bigbluebuttonService.isModerator(sessionParams)])
|
2013-01-29 04:53:27 +08:00
|
|
|
}
|
|
|
|
}
|
2013-01-26 04:10:27 +08:00
|
|
|
|
2014-08-29 06:10:34 +08:00
|
|
|
def delete = {
|
2013-01-29 04:53:27 +08:00
|
|
|
log.debug CONTROLLER_NAME + "#delete"
|
|
|
|
Map<String, String> result
|
|
|
|
def sessionParams = session["params"]
|
2013-01-29 05:58:55 +08:00
|
|
|
if( sessionParams == null ) {
|
|
|
|
result = new HashMap<String, String>()
|
2017-12-11 23:47:38 +08:00
|
|
|
result.put("messageKey", "InvalidSession")
|
|
|
|
result.put("message", "Invalid session. User can not execute this action.")
|
2013-01-29 05:58:55 +08:00
|
|
|
} else if ( !bigbluebuttonService.isModerator(sessionParams) ) {
|
|
|
|
result = new HashMap<String, String>()
|
2017-12-11 23:47:38 +08:00
|
|
|
result.put("messageKey", "NotAllowed")
|
|
|
|
result.put("message", "User not allowed to execute this action.")
|
2013-01-29 05:58:55 +08:00
|
|
|
} else {
|
2017-12-11 23:47:38 +08:00
|
|
|
// Execute the delete command.
|
2013-01-29 04:53:27 +08:00
|
|
|
result = bigbluebuttonService.doDeleteRecordings(params)
|
|
|
|
}
|
2017-12-11 23:47:38 +08:00
|
|
|
if( result.containsKey("messageKey")) {
|
|
|
|
log.debug "Error [messageKey:'" + result.get("messageKey") + "', message:'" + result.get("message") + "']"
|
|
|
|
render(view: "error", model: ['messageKey': result.get("messageKey"), 'message': result.get("message")])
|
2013-01-29 04:53:27 +08:00
|
|
|
} else {
|
2016-08-24 23:07:35 +08:00
|
|
|
render(view: "index", model: ['params': sessionParams, 'recordingList': getSanitizedRecordings(sessionParams), 'ismoderator': bigbluebuttonService.isModerator(sessionParams)])
|
2013-01-29 04:53:27 +08:00
|
|
|
}
|
2013-01-26 04:10:27 +08:00
|
|
|
}
|
2014-03-27 03:45:05 +08:00
|
|
|
|
2014-08-30 00:57:02 +08:00
|
|
|
private void setLocalization(Map<String, String> params){
|
2013-01-29 04:53:27 +08:00
|
|
|
String locale = params.get(Parameter.LAUNCH_LOCALE)
|
|
|
|
locale = (locale == null || locale.equals("")?"en":locale)
|
|
|
|
String[] localeCodes = locale.split("_")
|
2017-12-11 23:47:38 +08:00
|
|
|
// Localize the default welcome message
|
|
|
|
session['org.springframework.web.servlet.i18n.SessionLocaleResolver.LOCALE'] = new Locale(localeCodes[0])
|
|
|
|
if (localeCodes.length > 1) {
|
2013-01-29 04:53:27 +08:00
|
|
|
session['org.springframework.web.servlet.i18n.SessionLocaleResolver.LOCALE'] = new Locale(localeCodes[0], localeCodes[1])
|
2017-12-11 23:47:38 +08:00
|
|
|
}
|
2013-04-20 05:52:46 +08:00
|
|
|
}
|
|
|
|
|
2014-08-30 00:57:02 +08:00
|
|
|
private Object doJoinMeeting(Map<String, String> params) {
|
2013-04-20 05:52:46 +08:00
|
|
|
setLocalization(params)
|
2014-03-27 03:45:05 +08:00
|
|
|
String welcome = message(code: "bigbluebutton.welcome.header", args: ["\"{0}\"", "\"{1}\""]) + "<br>"
|
2016-08-24 23:07:35 +08:00
|
|
|
// Check for [custom_]welcome parameter being passed from the LTI
|
2017-12-11 23:47:38 +08:00
|
|
|
if (params.containsKey(Parameter.CUSTOM_WELCOME) && params.get(Parameter.CUSTOM_WELCOME) != null) {
|
2016-08-24 23:07:35 +08:00
|
|
|
welcome = params.get(Parameter.CUSTOM_WELCOME) + "<br>"
|
|
|
|
log.debug "Overriding default welcome message with: [" + welcome + "]"
|
|
|
|
}
|
2017-12-11 23:47:38 +08:00
|
|
|
if (params.containsKey(Parameter.CUSTOM_RECORD) && Boolean.parseBoolean(params.get(Parameter.CUSTOM_RECORD)) || ltiService.allRecordedByDefault()) {
|
2014-03-27 03:45:05 +08:00
|
|
|
welcome += "<br><b>" + message(code: "bigbluebutton.welcome.record") + "</b><br>"
|
|
|
|
log.debug "Adding record warning to welcome message, welcome is now: [" + welcome + "]"
|
|
|
|
}
|
2017-12-11 23:47:38 +08:00
|
|
|
if (params.containsKey(Parameter.CUSTOM_DURATION) && Integer.parseInt(params.get(Parameter.CUSTOM_DURATION)) > 0) {
|
2014-08-29 06:10:34 +08:00
|
|
|
welcome += "<br><b>" + message(code: "bigbluebutton.welcome.duration", args: [params.get(Parameter.CUSTOM_DURATION)]) + "</b><br>"
|
2014-03-27 03:45:05 +08:00
|
|
|
log.debug "Adding duration warning to welcome message, welcome is now: [" + welcome + "]"
|
|
|
|
}
|
|
|
|
welcome += "<br>" + message(code: "bigbluebutton.welcome.footer") + "<br>"
|
2013-01-29 04:53:27 +08:00
|
|
|
String destinationURL = bigbluebuttonService.getJoinURL(params, welcome, ltiService.mode)
|
2017-12-11 23:47:38 +08:00
|
|
|
if (destinationURL == null) {
|
|
|
|
Map<String, String> result = new HashMap<String, String>()
|
|
|
|
result.put("messageKey", "BigBlueButtonServerError")
|
|
|
|
result.put("message", "The join could not be completed")
|
|
|
|
return result
|
2013-01-25 04:52:06 +08:00
|
|
|
}
|
2017-12-11 23:47:38 +08:00
|
|
|
log.debug "It is redirecting to " + destinationURL
|
|
|
|
redirect(url:destinationURL)
|
2012-10-11 23:52:41 +08:00
|
|
|
}
|
2014-08-28 22:13:40 +08:00
|
|
|
|
2012-10-11 23:52:41 +08:00
|
|
|
/**
|
|
|
|
* Assemble all parameters passed that is required to sign the request.
|
|
|
|
* @param the HTTP request parameters
|
|
|
|
* @return the key:val pairs needed for Basic LTI
|
|
|
|
*/
|
2014-08-30 00:57:02 +08:00
|
|
|
private Properties sanitizePrametersForBaseString(Map<String, String> params) {
|
2012-10-11 23:52:41 +08:00
|
|
|
Properties reqProp = new Properties();
|
2014-08-29 06:36:51 +08:00
|
|
|
for (String key : params.keySet()) {
|
|
|
|
if (key == "action" || key == "controller" || key == "format") {
|
2012-10-11 23:52:41 +08:00
|
|
|
// Ignore as these are the grails controller and action tied to this request.
|
|
|
|
continue
|
2017-12-11 23:47:38 +08:00
|
|
|
}
|
|
|
|
if (key == "oauth_signature") {
|
|
|
|
// We don't need this as part of the base string.
|
2012-10-11 23:52:41 +08:00
|
|
|
continue
|
2017-12-11 23:47:38 +08:00
|
|
|
}
|
|
|
|
if (key == "request_method") {
|
|
|
|
// As this is was added by the controller, we don't want it as part of the base string.
|
2013-01-26 04:10:27 +08:00
|
|
|
continue
|
2012-10-11 23:52:41 +08:00
|
|
|
}
|
2014-08-29 06:36:51 +08:00
|
|
|
reqProp.setProperty(key, params.get(key));
|
2012-10-11 23:52:41 +08:00
|
|
|
}
|
|
|
|
return reqProp
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if all required parameters have been passed in the request.
|
|
|
|
* @param params - the HTTP request parameters
|
|
|
|
* @param missingParams - a list of missing parameters
|
|
|
|
* @return - true if all required parameters have been passed in
|
|
|
|
*/
|
2014-08-29 22:47:46 +08:00
|
|
|
private boolean hasAllRequiredParams(Map<String, String> params, ArrayList<String> missingParams) {
|
|
|
|
log.debug "Checking for required parameters"
|
2017-12-11 23:47:38 +08:00
|
|
|
if (ltiService.hasRestrictedAccess() && !params.containsKey(Parameter.CONSUMER_ID)) {
|
2014-08-29 22:47:46 +08:00
|
|
|
missingParams.add(Parameter.CONSUMER_ID);
|
2017-12-11 23:47:38 +08:00
|
|
|
return false
|
2012-10-11 23:52:41 +08:00
|
|
|
}
|
2017-12-11 23:47:38 +08:00
|
|
|
if (ltiService.hasRestrictedAccess() && !params.containsKey(Parameter.OAUTH_SIGNATURE)) {
|
2014-08-29 22:47:46 +08:00
|
|
|
missingParams.add(Parameter.OAUTH_SIGNATURE);
|
2017-12-11 23:47:38 +08:00
|
|
|
return false
|
2012-10-11 23:52:41 +08:00
|
|
|
}
|
2017-12-11 23:47:38 +08:00
|
|
|
if (!params.containsKey(Parameter.RESOURCE_LINK_ID)) {
|
2014-08-29 22:47:46 +08:00
|
|
|
missingParams.add(Parameter.RESOURCE_LINK_ID);
|
2017-12-11 23:47:38 +08:00
|
|
|
return false
|
2013-01-24 06:46:31 +08:00
|
|
|
}
|
2017-12-11 23:47:38 +08:00
|
|
|
return true
|
2012-10-11 23:52:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the passed signature is valid.
|
|
|
|
* @param method - POST or GET method used to make the request
|
|
|
|
* @param URL - The target URL for the Basic LTI tool
|
|
|
|
* @param conSecret - The consumer secret key
|
|
|
|
* @param postProp - the parameters passed in from the tool
|
|
|
|
* @param signature - the passed in signature calculated from the client
|
|
|
|
* @return - TRUE if the signatures matches the calculated signature
|
|
|
|
*/
|
2014-08-29 22:47:46 +08:00
|
|
|
private boolean checkValidSignature(String method, String url, String conSecret, Properties postProp, String signature) {
|
2017-12-11 23:47:38 +08:00
|
|
|
if (!ltiService.hasRestrictedAccess()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
OAuthMessage oam = new OAuthMessage(method, url, postProp.entrySet())
|
|
|
|
//log.debug "OAuthMessage oam = " + oam.toString()
|
|
|
|
HMAC_SHA1 hmac = new HMAC_SHA1()
|
|
|
|
//log.debug "HMAC_SHA1 hmac = " + hmac.toString()
|
|
|
|
hmac.setConsumerSecret(conSecret)
|
|
|
|
log.debug "Base Message String = [ " + hmac.getBaseString(oam) + " ]\n"
|
|
|
|
String calculatedSignature = hmac.getSignature(hmac.getBaseString(oam))
|
|
|
|
log.debug "Calculated: " + calculatedSignature + " Received: " + signature
|
|
|
|
return calculatedSignature.equals(signature)
|
|
|
|
} catch( Exception e ) {
|
|
|
|
log.debug "Exception error: " + e.message
|
|
|
|
return false
|
2014-08-29 22:47:46 +08:00
|
|
|
}
|
2012-10-11 23:52:41 +08:00
|
|
|
}
|
2014-08-28 22:13:40 +08:00
|
|
|
|
2016-08-24 23:07:35 +08:00
|
|
|
/**
|
|
|
|
* Assemble all recordings to be rendered by the view.
|
|
|
|
* @param the HTTP request parameters
|
|
|
|
* @return the key:val pairs needed for Basic LTI
|
|
|
|
*/
|
|
|
|
private List<Object> getSanitizedRecordings(Map<String, String> params) {
|
2017-12-11 23:47:38 +08:00
|
|
|
def recordings = new ArrayList<Object>()
|
|
|
|
def getRecordingsResponse = bigbluebuttonService.getRecordings(params)
|
|
|
|
if (getRecordingsResponse == null) {
|
|
|
|
return recordings
|
|
|
|
}
|
|
|
|
Object response = (Object)getRecordingsResponse.get("recording")
|
|
|
|
if (response instanceof Map<?,?>) {
|
|
|
|
recordings.add(response)
|
|
|
|
}
|
|
|
|
if (response instanceof Collection<?>) {
|
|
|
|
recordings = response
|
|
|
|
}
|
|
|
|
// Sanitize recordings
|
2017-12-12 02:48:16 +08:00
|
|
|
for (recording in recordings) {
|
2017-12-11 23:47:38 +08:00
|
|
|
// Calculate duration.
|
2016-08-24 23:07:35 +08:00
|
|
|
long endTime = Long.parseLong((String)recording.get("endTime"))
|
|
|
|
endTime -= (endTime % 1000)
|
|
|
|
long startTime = Long.parseLong((String)recording.get("startTime"))
|
|
|
|
startTime -= (startTime % 1000)
|
|
|
|
int duration = (endTime - startTime) / 60000
|
2017-12-11 23:47:38 +08:00
|
|
|
// Add duration.
|
2016-08-24 23:07:35 +08:00
|
|
|
recording.put("duration", duration )
|
2017-12-11 23:47:38 +08:00
|
|
|
// Calculate reportDate.
|
2016-08-24 23:07:35 +08:00
|
|
|
DateFormat df = new SimpleDateFormat(message(code: "tool.view.dateFormat"))
|
|
|
|
String reportDate = df.format(new Date(startTime))
|
2017-12-11 23:47:38 +08:00
|
|
|
// Add reportDate.
|
2016-08-24 23:07:35 +08:00
|
|
|
recording.put("reportDate", reportDate)
|
|
|
|
recording.put("unixDate", startTime / 1000)
|
2017-12-12 02:48:16 +08:00
|
|
|
// Add sanitized thumbnails
|
|
|
|
recording.put("thumbnails", sanitizeThumbnails(recording.playback.format))
|
2016-08-24 23:07:35 +08:00
|
|
|
}
|
|
|
|
return recordings
|
|
|
|
}
|
|
|
|
|
2017-12-12 02:48:16 +08:00
|
|
|
private List<Object> sanitizeThumbnails(Object format) {
|
|
|
|
if (format.preview == null || format.preview.images == null || format.preview.images.image == null) {
|
|
|
|
return new ArrayList()
|
|
|
|
}
|
|
|
|
if (format.preview.images.image instanceof Map<?,?>) {
|
|
|
|
return new ArrayList(format.preview.images.image)
|
|
|
|
}
|
|
|
|
return format.preview.images.image
|
|
|
|
}
|
|
|
|
|
2013-07-19 03:16:21 +08:00
|
|
|
private String getCartridgeXML(){
|
2014-08-29 06:10:34 +08:00
|
|
|
def lti_endpoint = ltiService.retrieveBasicLtiEndpoint() + '/' + grailsApplication.metadata['app.name']
|
|
|
|
def launch_url = 'http://' + lti_endpoint + '/tool'
|
|
|
|
def secure_launch_url = 'https://' + lti_endpoint + '/tool'
|
2016-08-23 04:20:33 +08:00
|
|
|
def icon = 'http://' + lti_endpoint + '/assets/icon.ico'
|
|
|
|
def secure_icon = 'https://' + lti_endpoint + '/assets/icon.ico'
|
2014-08-29 06:10:34 +08:00
|
|
|
def isSSLEnabled = ltiService.isSSLEnabled('https://' + lti_endpoint + '/tool/test')
|
2013-07-19 03:16:21 +08:00
|
|
|
def cartridge = '' +
|
2014-08-28 22:13:40 +08:00
|
|
|
'<?xml version="1.0" encoding="UTF-8"?>' +
|
|
|
|
'<cartridge_basiclti_link xmlns="http://www.imsglobal.org/xsd/imslticc_v1p0"' +
|
|
|
|
' xmlns:blti = "http://www.imsglobal.org/xsd/imsbasiclti_v1p0"' +
|
|
|
|
' xmlns:lticm ="http://www.imsglobal.org/xsd/imslticm_v1p0"' +
|
|
|
|
' xmlns:lticp ="http://www.imsglobal.org/xsd/imslticp_v1p0"' +
|
|
|
|
' xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"' +
|
|
|
|
' xsi:schemaLocation = "http://www.imsglobal.org/xsd/imslticc_v1p0 http://www.imsglobal.org/xsd/lti/ltiv1p0/imslticc_v1p0.xsd' +
|
|
|
|
' http://www.imsglobal.org/xsd/imsbasiclti_v1p0 http://www.imsglobal.org/xsd/lti/ltiv1p0/imsbasiclti_v1p0.xsd' +
|
|
|
|
' http://www.imsglobal.org/xsd/imslticm_v1p0 http://www.imsglobal.org/xsd/lti/ltiv1p0/imslticm_v1p0.xsd' +
|
|
|
|
' http://www.imsglobal.org/xsd/imslticp_v1p0 http://www.imsglobal.org/xsd/lti/ltiv1p0/imslticp_v1p0.xsd">' +
|
|
|
|
' <blti:title>BigBlueButton</blti:title>' +
|
2014-08-29 06:10:34 +08:00
|
|
|
' <blti:description>Single Sign On into BigBlueButton</blti:description>' +
|
2014-08-29 03:01:54 +08:00
|
|
|
' <blti:launch_url>' + launch_url + '</blti:launch_url>' +
|
|
|
|
(isSSLEnabled? ' <blti:secure_launch_url>' + secure_launch_url + '</blti:secure_launch_url>': '') +
|
|
|
|
' <blti:icon>' + icon + '</blti:icon>' +
|
|
|
|
(isSSLEnabled? ' <blti:secure_icon>' + secure_icon + '</blti:secure_icon>': '') +
|
2014-08-28 22:13:40 +08:00
|
|
|
' <blti:vendor>' +
|
2014-09-30 01:11:57 +08:00
|
|
|
' <lticp:code>bigbluebutton</lticp:code>' +
|
2014-08-28 22:13:40 +08:00
|
|
|
' <lticp:name>BigBlueButton</lticp:name>' +
|
|
|
|
' <lticp:description>Open source web conferencing system for distance learning.</lticp:description>' +
|
|
|
|
' <lticp:url>http://www.bigbluebutton.org/</lticp:url>' +
|
|
|
|
' </blti:vendor>' +
|
|
|
|
' <cartridge_bundle identifierref="BLTI001_Bundle"/>' +
|
|
|
|
' <cartridge_icon identifierref="BLTI001_Icon"/>' +
|
|
|
|
'</cartridge_basiclti_link>'
|
|
|
|
|
2013-07-19 03:16:21 +08:00
|
|
|
return cartridge
|
|
|
|
}
|
2017-12-11 23:47:38 +08:00
|
|
|
|
|
|
|
private void renderError(key, message) {
|
|
|
|
log.debug "Error [resultMessageKey:'" + key + "', resultMessage:'" + message + "']"
|
|
|
|
render(view: "error", model: ['resultMessageKey': key, 'resultMessage': message])
|
|
|
|
}
|
2012-10-11 23:52:41 +08:00
|
|
|
}
|