Updated bbb-lti to fix issues with grails 2.3.6
This commit is contained in:
parent
1d26b39ed5
commit
6f9fbe9f0f
@ -1,85 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src/java"/>
|
||||
<classpathentry kind="src" path="src/groovy"/>
|
||||
<classpathentry kind="src" path="grails-app/conf"/>
|
||||
<classpathentry kind="src" path="grails-app/controllers"/>
|
||||
<classpathentry kind="src" path="grails-app/domain"/>
|
||||
<classpathentry kind="src" path="grails-app/services"/>
|
||||
<classpathentry kind="src" path="grails-app/taglib"/>
|
||||
<classpathentry kind="src" path="test/integration"/>
|
||||
<classpathentry kind="src" path="test/unit"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/ant/lib/ant.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/commons-el-1.0.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/spring-test-2.5.6.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/oro-2.0.8.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/log4j-1.2.15.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/jsr107cache-1.0.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/commons-fileupload-1.2.1.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/ant-trax.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/commons-collections-3.2.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/commons-lang-2.4.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/spring-webmvc-2.5.6.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/hsqldb-1.8.0.5.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/ant-1.7.0.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/jcl-over-slf4j-1.5.6.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/backport-util-concurrent-3.0.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/jasper-compiler-5.5.15.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/commons-validator-1.3.0.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/jsp-api-2.0.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/jetty-naming-6.1.14.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/jetty-util-6.1.14.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/slf4j-api-1.5.6.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/start.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/servlet-api-2.5-6.1.14.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/ognl-2.6.9.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/commons-dbcp-1.2.1.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/cglib-nodep-2.1_3.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/jline-0.9.91.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/jasper-compiler-jdt-5.5.15.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/standard-2.4.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/org.springframework.binding-2.0.3.RELEASE.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/standard-2.3.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/ejb3-persistence-3.3.0.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/commons-io-1.4.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/jetty-6.1.14.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/spring-2.5.6.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/jstl-2.3.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/jetty-plus-6.1.14.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/jstl-2.4.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/xpp3_min-1.1.3.4.O.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/commons-codec-1.3.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/commons-pool-1.2.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/sitemesh-2.4.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/org.springframework.webflow-2.0.3.RELEASE.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/junit-3.8.2.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/commons-beanutils-1.7.0.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/oscache-2.4.1.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/jta-1.1.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/ehcache-1.5.0.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/serializer.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/ant-nodeps-1.7.0.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/groovy-all-1.6.3.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/gant_groovy1.6-1.6.0.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/svnkit-1.2.0.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/slf4j-log4j12-1.5.6.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/jsp-api-2.1.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/jasper-runtime-5.5.15.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/ant-junit-1.7.0.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/ant-launcher-1.7.0.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/commons-cli-1.0.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/ivy-2.0.0.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/antlr-2.7.6.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/lib/org.springframework.js-2.0.3.RELEASE.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/dist/grails-scripts-1.1.1.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/dist/grails-gorm-1.1.1.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/dist/grails-webflow-1.1.1.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/dist/grails-bootstrap-1.1.1.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/dist/grails-resources-1.1.1.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/dist/grails-crud-1.1.1.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/dist/grails-core-1.1.1.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/dist/grails-spring-1.1.1.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/dist/grails-web-1.1.1.jar"/>
|
||||
<classpathentry kind="var" path="GRAILS_HOME/dist/grails-test-1.1.1.jar"/>
|
||||
</classpath>
|
@ -1,5 +1,5 @@
|
||||
#Grails Metadata file
|
||||
#Thu Mar 20 10:48:08 PDT 2014
|
||||
#Wed Aug 27 13:06:23 PDT 2014
|
||||
app.grails.version=2.3.6
|
||||
app.name=lti
|
||||
app.version=0.1.2
|
||||
app.version=0.2
|
||||
|
@ -15,7 +15,6 @@
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
modules = {
|
||||
application {
|
||||
resource url:'js/application.js'
|
||||
|
@ -15,8 +15,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
grails.servlet.version = "3.0"
|
||||
grails.servlet.version = "3.0" // Change depending on target container compliance (2.5 or 3.0)
|
||||
grails.project.class.dir = "target/classes"
|
||||
grails.project.test.class.dir = "target/test-classes"
|
||||
grails.project.test.reports.dir = "target/test-reports"
|
||||
@ -26,48 +25,76 @@ grails.project.source.level = 1.6
|
||||
//grails.project.war.file = "target/${appName}-${appVersion}.war"
|
||||
|
||||
grails.project.fork = [
|
||||
// configure settings for compilation JVM, note that if you alter the Groovy version forked compilation is required
|
||||
// compile: [maxMemory: 256, minMemory: 64, debug: false, maxPerm: 256, daemon:true],
|
||||
|
||||
// configure settings for the test-app JVM, uses the daemon by default
|
||||
test: [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256, daemon:true],
|
||||
// configure settings for the run-app JVM
|
||||
run: [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256, forkReserve:false],
|
||||
// configure settings for the run-war JVM
|
||||
war: [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256, forkReserve:false],
|
||||
// configure settings for the Console UI JVM
|
||||
console: [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256]
|
||||
]
|
||||
|
||||
grails.project.dependency.resolver = "maven" // or ivy
|
||||
grails.project.dependency.resolution = {
|
||||
// inherit Grails' default dependencies
|
||||
inherits("global") {
|
||||
// specify dependency exclusions here; for example, uncomment this to disable ehcache:
|
||||
// excludes 'ehcache'
|
||||
}
|
||||
log "error"
|
||||
checksums true
|
||||
legacyResolve false
|
||||
log "error" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose'
|
||||
checksums true // Whether to verify checksums on resolve
|
||||
legacyResolve false // whether to do a secondary resolve on plugin installation, not advised and here for backwards compatibility
|
||||
|
||||
repositories {
|
||||
inherits true
|
||||
inherits true // Whether to inherit repository definitions from plugins
|
||||
|
||||
grailsPlugins()
|
||||
grailsHome()
|
||||
mavenLocal()
|
||||
grailsCentral()
|
||||
mavenCentral()
|
||||
|
||||
mavenRepo "http://snapshots.repository.codehaus.org"
|
||||
mavenRepo "http://repository.codehaus.org"
|
||||
mavenRepo "http://download.java.net/maven/2/"
|
||||
mavenRepo "http://repository.jboss.com/maven2/"
|
||||
//mavenRepo "https://raw.github.com/blindsidenetworks/oauth/mvn-repo/"
|
||||
// uncomment these (or add new ones) to enable remote dependency resolution from public Maven repositories
|
||||
//mavenRepo "http://repository.codehaus.org"
|
||||
//mavenRepo "http://download.java.net/maven/2/"
|
||||
//mavenRepo "http://repository.jboss.com/maven2/"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
//runtime "commons-net:commons-net:3.0.1"
|
||||
//runtime "net.oauth:oauth:1.0.1"
|
||||
// specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes e.g.
|
||||
// runtime 'mysql:mysql-connector-java:5.1.27'
|
||||
// runtime 'org.postgresql:postgresql:9.3-1100-jdbc41'
|
||||
}
|
||||
|
||||
plugins {
|
||||
// plugins for the build system only
|
||||
build ":tomcat:7.0.50.1"
|
||||
|
||||
// plugins for the compile step
|
||||
compile ":scaffolding:2.0.2"
|
||||
compile ':cache:1.1.1'
|
||||
|
||||
// plugins needed at runtime but not for compilation
|
||||
runtime ":hibernate:3.6.10.8" // or ":hibernate4:4.3.1.1"
|
||||
runtime ":database-migration:1.3.8"
|
||||
runtime ":jquery:1.11.0"
|
||||
runtime ":resources:1.2.1"
|
||||
// Uncomment these (or add new ones) to enable additional resources capabilities
|
||||
//runtime ":zipped-resources:1.0.1"
|
||||
//runtime ":cached-resources:1.1"
|
||||
//runtime ":yui-minify-resources:0.1.5"
|
||||
runtime ':twitter-bootstrap:3.1.1'
|
||||
|
||||
// An alternative to the default resources plugin is the asset-pipeline plugin
|
||||
//compile ":asset-pipeline:1.5.0"
|
||||
|
||||
// Uncomment these to enable additional asset-pipeline capabilities
|
||||
//compile ":sass-asset-pipeline:1.5.1"
|
||||
//compile ":less-asset-pipeline:1.5.0"
|
||||
//compile ":coffee-asset-pipeline:1.5.0"
|
||||
//compile ":handlebars-asset-pipeline:1.0.0.3"
|
||||
}
|
||||
}
|
||||
|
@ -1,139 +1,134 @@
|
||||
/*
|
||||
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
|
||||
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// locations to search for config files that get merged into the main config;
|
||||
// config files can be ConfigSlurper scripts, Java properties files, or classes
|
||||
// in the classpath in ConfigSlurper format
|
||||
|
||||
grails.config.locations = [ "classpath:lti.properties"]
|
||||
// grails.config.locations = [ "classpath:${appName}-config.properties",
|
||||
// "classpath:${appName}-config.groovy",
|
||||
// "file:${userHome}/.grails/${appName}-config.properties",
|
||||
// "file:${userHome}/.grails/${appName}-config.groovy"]
|
||||
|
||||
// if (System.properties["${appName}.config.location"]) {
|
||||
// grails.config.locations << "file:" + System.properties["${appName}.config.location"]
|
||||
// }
|
||||
|
||||
grails.project.groupId = appName // change this to alter the default package name and Maven publishing destination
|
||||
|
||||
grails.mime.file.extensions = true // enables the parsing of file extensions from URLs into the request format
|
||||
grails.mime.use.accept.header = false
|
||||
// The ACCEPT header will not be used for content negotiation for user agents containing the following strings (defaults to the 4 major rendering engines)
|
||||
grails.mime.disable.accept.header.userAgents = ['Gecko', 'WebKit', 'Presto', 'Trident']
|
||||
grails.mime.types = [ // the first one is the default format
|
||||
all: '*/*', // 'all' maps to '*' or the first available format in withFormat
|
||||
atom: 'application/atom+xml',
|
||||
css: 'text/css',
|
||||
csv: 'text/csv',
|
||||
form: 'application/x-www-form-urlencoded',
|
||||
html: ['text/html','application/xhtml+xml'],
|
||||
js: 'text/javascript',
|
||||
json: ['application/json', 'text/json'],
|
||||
multipartForm: 'multipart/form-data',
|
||||
rss: 'application/rss+xml',
|
||||
text: 'text/plain',
|
||||
hal: ['application/hal+json','application/hal+xml'],
|
||||
xml: ['text/xml', 'application/xml']
|
||||
]
|
||||
|
||||
// URL Mapping Cache Max Size, defaults to 5000
|
||||
//grails.urlmapping.cache.maxsize = 1000
|
||||
|
||||
// What URL patterns should be processed by the resources plugin
|
||||
grails.resources.adhoc.patterns = ['/images/*', '/css/*', '/js/*', '/plugins/*']
|
||||
grails.resources.adhoc.excludes = ['/WEB-INF/**']
|
||||
|
||||
// Legacy setting for codec used to encode data with ${}
|
||||
grails.views.default.codec = "html"
|
||||
|
||||
// The default scope for controllers. May be prototype, session or singleton.
|
||||
// If unspecified, controllers are prototype scoped.
|
||||
grails.controllers.defaultScope = 'singleton'
|
||||
|
||||
// GSP settings
|
||||
grails {
|
||||
views {
|
||||
gsp {
|
||||
encoding = 'UTF-8'
|
||||
htmlcodec = 'xml' // use xml escaping instead of HTML4 escaping
|
||||
codecs {
|
||||
expression = 'html' // escapes values inside ${}
|
||||
scriptlet = 'html' // escapes output from scriptlets in GSPs
|
||||
taglib = 'none' // escapes output from taglibs
|
||||
staticparts = 'none' // escapes output from static template parts
|
||||
}
|
||||
}
|
||||
// escapes all not-encoded output at final stage of outputting
|
||||
// filteringCodecForContentType.'text/html' = 'html'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
grails.converters.encoding = "UTF-8"
|
||||
// scaffolding templates configuration
|
||||
grails.scaffolding.templates.domainSuffix = 'Instance'
|
||||
|
||||
// Set to false to use the new Grails 1.2 JSONBuilder in the render method
|
||||
grails.json.legacy.builder = false
|
||||
// enabled native2ascii conversion of i18n properties files
|
||||
grails.enable.native2ascii = true
|
||||
// packages to include in Spring bean scanning
|
||||
grails.spring.bean.packages = []
|
||||
// whether to disable processing of multi part requests
|
||||
grails.web.disable.multipart=false
|
||||
|
||||
// request parameters to mask when logging exceptions
|
||||
grails.exceptionresolver.params.exclude = ['password']
|
||||
|
||||
// configure auto-caching of queries by default (if false you can cache individual queries with 'cache: true')
|
||||
grails.hibernate.cache.queries = false
|
||||
|
||||
environments {
|
||||
development {
|
||||
grails.logging.jul.usebridge = true
|
||||
}
|
||||
production {
|
||||
grails.logging.jul.usebridge = false
|
||||
}
|
||||
}
|
||||
|
||||
// log4j configuration
|
||||
log4j = {
|
||||
appenders {
|
||||
rollingFile name:"logfile", maxFileSize:1000000, file:"/var/log/bigbluebutton/bbb-lti.log", layout:pattern(conversionPattern: '%d{[dd.MM.yy HH:mm:ss.SSS]} %-5p %c %x - %m%n')
|
||||
console name:'console', layout:pattern(conversionPattern: '%d{[dd.MM.yy HH:mm:ss.SSS]} %-5p %c %x - %m%n')
|
||||
'null' name:'stacktrace'
|
||||
}
|
||||
debug logfile:"grails.app"
|
||||
|
||||
error 'org.codehaus.groovy.grails.web.servlet', // controllers
|
||||
'org.codehaus.groovy.grails.web.pages', // GSP
|
||||
'org.codehaus.groovy.grails.web.sitemesh', // layouts
|
||||
'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping
|
||||
'org.codehaus.groovy.grails.web.mapping', // URL mapping
|
||||
'org.codehaus.groovy.grails.commons', // core / classloading
|
||||
'org.codehaus.groovy.grails.plugins', // plugins
|
||||
'org.codehaus.groovy.grails.orm.hibernate', // hibernate integration
|
||||
'org.springframework',
|
||||
'org.hibernate',
|
||||
'net.sf.ehcache.hibernate'
|
||||
|
||||
warn 'org.mortbay.log'
|
||||
|
||||
}
|
||||
/*
|
||||
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
|
||||
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// locations to search for config files that get merged into the main config;
|
||||
// config files can be ConfigSlurper scripts, Java properties files, or classes
|
||||
// in the classpath in ConfigSlurper format
|
||||
|
||||
grails.config.locations = [ "classpath:lti.properties"]
|
||||
// grails.config.locations = [ "classpath:${appName}-config.properties",
|
||||
// "classpath:${appName}-config.groovy",
|
||||
// "file:${userHome}/.grails/${appName}-config.properties",
|
||||
// "file:${userHome}/.grails/${appName}-config.groovy"]
|
||||
|
||||
// if (System.properties["${appName}.config.location"]) {
|
||||
// grails.config.locations << "file:" + System.properties["${appName}.config.location"]
|
||||
// }
|
||||
|
||||
grails.project.groupId = appName // change this to alter the default package name and Maven publishing destination
|
||||
|
||||
// The ACCEPT header will not be used for content negotiation for user agents containing the following strings (defaults to the 4 major rendering engines)
|
||||
grails.mime.disable.accept.header.userAgents = ['Gecko', 'WebKit', 'Presto', 'Trident']
|
||||
grails.mime.types = [ // the first one is the default format
|
||||
all: '*/*', // 'all' maps to '*' or the first available format in withFormat
|
||||
atom: 'application/atom+xml',
|
||||
css: 'text/css',
|
||||
csv: 'text/csv',
|
||||
form: 'application/x-www-form-urlencoded',
|
||||
html: ['text/html','application/xhtml+xml'],
|
||||
js: 'text/javascript',
|
||||
json: ['application/json', 'text/json'],
|
||||
multipartForm: 'multipart/form-data',
|
||||
rss: 'application/rss+xml',
|
||||
text: 'text/plain',
|
||||
hal: ['application/hal+json','application/hal+xml'],
|
||||
xml: ['text/xml', 'application/xml']
|
||||
]
|
||||
|
||||
// URL Mapping Cache Max Size, defaults to 5000
|
||||
//grails.urlmapping.cache.maxsize = 1000
|
||||
|
||||
// What URL patterns should be processed by the resources plugin
|
||||
grails.resources.adhoc.patterns = ['/images/*', '/css/*', '/js/*', '/plugins/*']
|
||||
grails.resources.adhoc.excludes = ['/WEB-INF/**']
|
||||
|
||||
// Legacy setting for codec used to encode data with ${}
|
||||
grails.views.default.codec = "html"
|
||||
|
||||
// The default scope for controllers. May be prototype, session or singleton.
|
||||
// If unspecified, controllers are prototype scoped.
|
||||
grails.controllers.defaultScope = 'singleton'
|
||||
|
||||
// GSP settings
|
||||
grails {
|
||||
views {
|
||||
gsp {
|
||||
encoding = 'UTF-8'
|
||||
htmlcodec = 'xml' // use xml escaping instead of HTML4 escaping
|
||||
codecs {
|
||||
expression = 'html' // escapes values inside ${}
|
||||
scriptlet = 'html' // escapes output from scriptlets in GSPs
|
||||
taglib = 'none' // escapes output from taglibs
|
||||
staticparts = 'none' // escapes output from static template parts
|
||||
}
|
||||
}
|
||||
// escapes all not-encoded output at final stage of outputting
|
||||
// filteringCodecForContentType.'text/html' = 'html'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
grails.converters.encoding = "UTF-8"
|
||||
// scaffolding templates configuration
|
||||
grails.scaffolding.templates.domainSuffix = 'Instance'
|
||||
|
||||
// Set to false to use the new Grails 1.2 JSONBuilder in the render method
|
||||
grails.json.legacy.builder = false
|
||||
// enabled native2ascii conversion of i18n properties files
|
||||
grails.enable.native2ascii = true
|
||||
// packages to include in Spring bean scanning
|
||||
grails.spring.bean.packages = []
|
||||
// whether to disable processing of multi part requests
|
||||
grails.web.disable.multipart=false
|
||||
|
||||
// request parameters to mask when logging exceptions
|
||||
grails.exceptionresolver.params.exclude = ['password']
|
||||
|
||||
// configure auto-caching of queries by default (if false you can cache individual queries with 'cache: true')
|
||||
grails.hibernate.cache.queries = false
|
||||
|
||||
environments {
|
||||
development {
|
||||
grails.logging.jul.usebridge = true
|
||||
}
|
||||
production {
|
||||
grails.logging.jul.usebridge = false
|
||||
// TODO: grails.serverURL = "http://www.changeme.com"
|
||||
}
|
||||
}
|
||||
|
||||
// log4j configuration
|
||||
log4j = {
|
||||
// Example of changing the log pattern for the default console appender:
|
||||
//
|
||||
//appenders {
|
||||
// console name:'stdout', layout:pattern(conversionPattern: '%c{2} %m%n')
|
||||
//}
|
||||
|
||||
error 'org.codehaus.groovy.grails.web.servlet', // controllers
|
||||
'org.codehaus.groovy.grails.web.pages', // GSP
|
||||
'org.codehaus.groovy.grails.web.sitemesh', // layouts
|
||||
'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping
|
||||
'org.codehaus.groovy.grails.web.mapping', // URL mapping
|
||||
'org.codehaus.groovy.grails.commons', // core / classloading
|
||||
'org.codehaus.groovy.grails.plugins', // plugins
|
||||
'org.codehaus.groovy.grails.orm.hibernate', // hibernate integration
|
||||
'org.springframework',
|
||||
'org.hibernate',
|
||||
'net.sf.ehcache.hibernate'
|
||||
}
|
||||
|
@ -15,7 +15,6 @@
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
dataSource {
|
||||
pooled = true
|
||||
jmxExport = true
|
||||
|
@ -15,11 +15,10 @@
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
class UrlMappings {
|
||||
|
||||
static mappings = {
|
||||
"/$controller/$action?/$id"{
|
||||
"/$controller/$action?/$id?(.$format)?"{
|
||||
constraints {
|
||||
// apply constraints here
|
||||
}
|
||||
|
@ -21,8 +21,10 @@
|
||||
# BigBlueButton integration information
|
||||
#----------------------------------------------------
|
||||
# This URL is where the BBB client is accessible.
|
||||
#bigbluebuttonURL=http://test-install.blindsidenetworks.com/bigbluebutton
|
||||
bigbluebuttonURL=http://localhost/bigbluebutton
|
||||
# Salt which is used by 3rd-party apps to authenticate api calls
|
||||
#bigbluebuttonSalt=8cd8ef52e8e101574e400365b55e11a6
|
||||
bigbluebuttonSalt=bbb_salt
|
||||
|
||||
# LTI basic information
|
||||
|
@ -15,6 +15,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.bigbluebutton
|
||||
|
||||
import java.util.ArrayList
|
||||
import java.util.HashMap
|
||||
@ -29,27 +30,29 @@ import net.oauth.signature.OAuthSignatureMethod
|
||||
import net.oauth.signature.HMAC_SHA1
|
||||
import org.bigbluebutton.lti.Parameter
|
||||
|
||||
import BigbluebuttonService
|
||||
import LtiService
|
||||
|
||||
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'
|
||||
private static final String REQUEST_METHOD = "request_method";
|
||||
|
||||
|
||||
LtiService ltiService
|
||||
BigbluebuttonService bigbluebuttonService
|
||||
|
||||
def index = {
|
||||
|
||||
def test() {
|
||||
ltiService.logParameters(params)
|
||||
render(text: "<xml></xml>", contentType: "text/xml", encoding: "UTF-8")
|
||||
}
|
||||
|
||||
def index() {
|
||||
if( ltiService.consumerMap == null) ltiService.initConsumerMap()
|
||||
log.debug CONTROLLER_NAME + "#index"
|
||||
|
||||
setLocalization(params)
|
||||
|
||||
|
||||
params.put(REQUEST_METHOD, request.getMethod().toUpperCase())
|
||||
ltiService.logParameters(params)
|
||||
|
||||
|
||||
if( request.post ){
|
||||
Map<String, String> result = new HashMap<String, String>()
|
||||
ArrayList<String> missingParams = new ArrayList<String>()
|
||||
@ -62,7 +65,7 @@ class ToolController {
|
||||
log.debug "Found consumer with key " + consumer.get("key") //+ " and sharedSecret " + consumer.get("secret")
|
||||
if (checkValidSignature(params.get(REQUEST_METHOD), ltiService.endPoint, consumer.get("secret"), sanitizedParams, params.get(Parameter.OAUTH_SIGNATURE))) {
|
||||
log.debug "The message has a valid signature."
|
||||
|
||||
|
||||
if( !"extended".equals(ltiService.mode) ) {
|
||||
log.debug "LTI service running in simple mode."
|
||||
result = doJoinMeeting(params)
|
||||
@ -73,18 +76,18 @@ class ToolController {
|
||||
result = doJoinMeeting(params)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
log.debug "The message has NOT a valid signature."
|
||||
result.put("resultMessageKey", "InvalidSignature")
|
||||
result.put("resultMessage", "Invalid signature (" + params.get(Parameter.OAUTH_SIGNATURE) + ").")
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
result.put("resultMessageKey", "ConsumerNotFound")
|
||||
result.put("resultMessage", "Consumer with id = " + params.get(Parameter.CONSUMER_ID) + " was not found.")
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
String missingStr = ""
|
||||
for(String str:missingParams) {
|
||||
@ -121,7 +124,7 @@ class ToolController {
|
||||
}
|
||||
}
|
||||
|
||||
def join = {
|
||||
def join() {
|
||||
if( ltiService.consumerMap == null) ltiService.initConsumerMap()
|
||||
log.debug CONTROLLER_NAME + "#join"
|
||||
Map<String, String> result
|
||||
@ -136,7 +139,7 @@ class ToolController {
|
||||
result = new HashMap<String, String>()
|
||||
result.put("resultMessageKey", "InvalidSession")
|
||||
result.put("resultMessage", "Invalid session. User can not execute this action.")
|
||||
}
|
||||
}
|
||||
|
||||
if( result.containsKey("resultMessageKey")) {
|
||||
log.debug "Error [resultMessageKey:'" + result.get("resultMessageKey") + "', resultMessage:'" + result.get("resultMessage") + "']"
|
||||
@ -145,7 +148,7 @@ class ToolController {
|
||||
|
||||
}
|
||||
|
||||
def publish = {
|
||||
def publish() {
|
||||
log.debug CONTROLLER_NAME + "#publish"
|
||||
Map<String, String> result
|
||||
|
||||
@ -162,10 +165,10 @@ class ToolController {
|
||||
} else {
|
||||
log.debug "params: " + params
|
||||
log.debug "sessionParams: " + sessionParams
|
||||
|
||||
|
||||
//Execute the publish command
|
||||
result = bigbluebuttonService.doPublishRecordings(params)
|
||||
}
|
||||
}
|
||||
|
||||
if( result.containsKey("resultMessageKey")) {
|
||||
log.debug "Error [resultMessageKey:'" + result.get("resultMessageKey") + "', resultMessage:'" + result.get("resultMessage") + "']"
|
||||
@ -182,14 +185,14 @@ class ToolController {
|
||||
/// Add duration
|
||||
recording.put("duration", duration )
|
||||
}
|
||||
|
||||
|
||||
render(view: "index", model: ['params': sessionParams, 'recordingList': recordings, 'ismoderator': bigbluebuttonService.isModerator(sessionParams)])
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def delete = {
|
||||
def delete() {
|
||||
log.debug CONTROLLER_NAME + "#delete"
|
||||
Map<String, String> result
|
||||
|
||||
@ -206,7 +209,7 @@ class ToolController {
|
||||
} else {
|
||||
log.debug "params: " + params
|
||||
log.debug "sessionParams: " + sessionParams
|
||||
|
||||
|
||||
//Execute the delete command
|
||||
result = bigbluebuttonService.doDeleteRecordings(params)
|
||||
}
|
||||
@ -242,23 +245,23 @@ class ToolController {
|
||||
session['org.springframework.web.servlet.i18n.SessionLocaleResolver.LOCALE'] = new Locale(localeCodes[0], localeCodes[1])
|
||||
else
|
||||
session['org.springframework.web.servlet.i18n.SessionLocaleResolver.LOCALE'] = new Locale(localeCodes[0])
|
||||
|
||||
|
||||
log.debug "Locale has been set to " + locale
|
||||
|
||||
}
|
||||
|
||||
private Object doJoinMeeting(params) {
|
||||
Map<String, String> result = new HashMap<String, String>()
|
||||
|
||||
|
||||
setLocalization(params)
|
||||
String welcome = message(code: "bigbluebutton.welcome.header", args: ["\"{0}\"", "\"{1}\""]) + "<br>"
|
||||
log.debug "Localized default welcome message: [" + welcome + "]"
|
||||
|
||||
// Check for [custom_]welcome parameter being passed from the LTI
|
||||
if (params.get(Parameter.CUSTOM_WELCOME) != null) {
|
||||
welcome = params.get(Parameter.CUSTOM_WELCOME) + "<br>"
|
||||
log.debug "Overriding default welcome message with: [" + welcome + "]"
|
||||
}
|
||||
// Check for [custom_]welcome parameter being passed from the LTI
|
||||
if (params.get(Parameter.CUSTOM_WELCOME) != null) {
|
||||
welcome = params.get(Parameter.CUSTOM_WELCOME) + "<br>"
|
||||
log.debug "Overriding default welcome message with: [" + welcome + "]"
|
||||
}
|
||||
|
||||
if ( Boolean.parseBoolean(params.get(Parameter.CUSTOM_RECORD)) ) {
|
||||
welcome += "<br><b>" + message(code: "bigbluebutton.welcome.record") + "</b><br>"
|
||||
@ -266,7 +269,9 @@ class ToolController {
|
||||
}
|
||||
|
||||
if ( Integer.parseInt(params.get(Parameter.CUSTOM_DURATION)) > 0 ) {
|
||||
welcome += "<br><b>" + message(code: "bigbluebutton.welcome.duration", args: [params.get(Parameter.CUSTOM_DURATION)]) + "</b><br>"
|
||||
welcome += "<br><b>" + message(code: "bigbluebutton.welcome.duration", args: [
|
||||
params.get(Parameter.CUSTOM_DURATION)
|
||||
]) + "</b><br>"
|
||||
log.debug "Adding duration warning to welcome message, welcome is now: [" + welcome + "]"
|
||||
}
|
||||
|
||||
@ -275,24 +280,24 @@ class ToolController {
|
||||
|
||||
String destinationURL = bigbluebuttonService.getJoinURL(params, welcome, ltiService.mode)
|
||||
log.debug "redirecting to " + destinationURL
|
||||
|
||||
|
||||
if( destinationURL != null ) {
|
||||
redirect(url:destinationURL)
|
||||
} else {
|
||||
result.put("resultMessageKey", "BigBlueButtonServerError")
|
||||
result.put("resultMessage", "The join could not be completed")
|
||||
}
|
||||
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
private Properties sanitizePrametersForBaseString(Object params) {
|
||||
|
||||
|
||||
Properties reqProp = new Properties();
|
||||
for (String key : ((Map<String, String>)params).keySet()) {
|
||||
if (key == "action" || key == "controller") {
|
||||
@ -348,11 +353,11 @@ class ToolController {
|
||||
* @return - TRUE if the signatures matches the calculated signature
|
||||
*/
|
||||
private boolean checkValidSignature(String method, String URL, String conSecret, Object postProp, String signature) {
|
||||
log.debug( "Starting checkValidSignature()" )
|
||||
log.debug( "Starting checkValidSignature()" )
|
||||
OAuthMessage oam = new OAuthMessage(method, URL, ((Properties)postProp).entrySet())
|
||||
log.debug( "OAuthMessage oam = " + oam.toString() )
|
||||
log.debug( "OAuthMessage oam = " + oam.toString() )
|
||||
HMAC_SHA1 hmac = new HMAC_SHA1()
|
||||
log.debug( "HMAC_SHA1 hmac = " + hmac.toString() )
|
||||
log.debug( "HMAC_SHA1 hmac = " + hmac.toString() )
|
||||
hmac.setConsumerSecret(conSecret)
|
||||
|
||||
log.debug("Base Message String = [ " + hmac.getBaseString(oam) + " ]\n")
|
||||
@ -360,36 +365,33 @@ class ToolController {
|
||||
log.debug("Calculated: " + calculatedSignature + " Received: " + signature)
|
||||
return calculatedSignature.equals(signature)
|
||||
}
|
||||
|
||||
|
||||
private String getCartridgeXML(){
|
||||
def cartridge = '' +
|
||||
'<?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>' +
|
||||
' <blti:description>Single Sign On into BigBlueButton</blti:description>' +
|
||||
' <blti:launch_url>' + ltiService.retrieveBasicLtiEndpoint() + '</blti:launch_url>' +
|
||||
' <blti:icon>' + ltiService.retrieveIconEndpoint() + '</blti:icon>' +
|
||||
' <blti:vendor>' +
|
||||
' <lticp:code>BBB</lticp:code>' +
|
||||
' <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>'
|
||||
|
||||
'<?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>' +
|
||||
' <blti:description>Single Sign On into BigBlueButton</blti:description>' +
|
||||
' <blti:launch_url>' + ltiService.retrieveBasicLtiEndpoint() + '</blti:launch_url>' +
|
||||
' <blti:icon>' + ltiService.retrieveIconEndpoint() + '</blti:icon>' +
|
||||
' <blti:vendor>' +
|
||||
' <lticp:code>BBB</lticp:code>' +
|
||||
' <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>'
|
||||
|
||||
return cartridge
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -15,40 +15,42 @@
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.bigbluebutton
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
import java.io.StringReader
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.text.MessageFormat
|
||||
import java.util.ArrayList
|
||||
import java.util.HashMap
|
||||
import java.util.List
|
||||
import java.util.Map
|
||||
import java.util.Random
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.DocumentBuilder
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
import javax.xml.parsers.ParserConfigurationException
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.w3c.dom.Document
|
||||
import org.w3c.dom.Node
|
||||
import org.w3c.dom.NodeList
|
||||
import org.xml.sax.InputSource
|
||||
import org.xml.sax.SAXException
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.codec.digest.DigestUtils
|
||||
|
||||
import org.bigbluebutton.api.Proxy
|
||||
import org.bigbluebutton.lti.Role
|
||||
import org.bigbluebutton.lti.Parameter
|
||||
|
||||
import grails.transaction.Transactional
|
||||
|
||||
@Transactional
|
||||
class BigbluebuttonService {
|
||||
|
||||
boolean transactional = false
|
||||
|
||||
def url = "http://test-install.blindsidenetworks.com/bigbluebutton"
|
||||
def salt = "8cd8ef52e8e101574e400365b55e11a6"
|
||||
|
||||
@ -64,19 +66,19 @@ class BigbluebuttonService {
|
||||
} catch (ParserConfigurationException e) {
|
||||
logger.error("Failed to initialise BaseProxy", e)
|
||||
}
|
||||
|
||||
|
||||
//Instantiate bbbProxy and initialize it with default url and salt
|
||||
bbbProxy = new Proxy(url, salt)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public String getJoinURL(params, welcome, mode){
|
||||
//Set the injected values
|
||||
if( !url.equals(bbbProxy.url) && !url.equals("") ) bbbProxy.setUrl(url)
|
||||
if( !salt.equals(bbbProxy.salt) && !salt.equals("") ) bbbProxy.setSalt(salt)
|
||||
|
||||
String joinURL = null
|
||||
|
||||
|
||||
String meetingName = getValidatedMeetingName(params.get(Parameter.RESOURCE_LINK_TITLE))
|
||||
String meetingID = getValidatedMeetingId(params.get(Parameter.RESOURCE_LINK_ID), params.get(Parameter.CONSUMER_ID))
|
||||
String attendeePW = DigestUtils.shaHex("ap" + params.get(Parameter.RESOURCE_LINK_ID) + params.get(Parameter.CONSUMER_ID))
|
||||
@ -86,7 +88,7 @@ class BigbluebuttonService {
|
||||
String userFullName = getValidatedUserFullName(params, isModerator)
|
||||
String courseTitle = getValidatedCourseTitle(params.get(Parameter.COURSE_TITLE))
|
||||
String userID = getValidatedUserId(params.get(Parameter.USER_ID))
|
||||
|
||||
|
||||
Integer voiceBridge = 0
|
||||
String record = false
|
||||
Integer duration = 0
|
||||
@ -95,12 +97,12 @@ class BigbluebuttonService {
|
||||
record = getValidatedBBBRecord(params.get(Parameter.CUSTOM_RECORD))
|
||||
duration = getValidatedBBBDuration(params.get(Parameter.CUSTOM_DURATION))
|
||||
}
|
||||
|
||||
|
||||
String[] values = [meetingName, courseTitle]
|
||||
String welcomeMsg = MessageFormat.format(welcome, values)
|
||||
|
||||
|
||||
String meta = getMonitoringMetaData(params)
|
||||
|
||||
|
||||
String createURL = getCreateURL( meetingName, meetingID, attendeePW, moderatorPW, welcomeMsg, voiceBridge, logoutURL, record, duration, meta )
|
||||
log.debug "createURL: " + createURL
|
||||
Map<String, Object> createResponse = doAPICall(createURL)
|
||||
@ -110,22 +112,21 @@ class BigbluebuttonService {
|
||||
String returnCode = (String) createResponse.get("returncode")
|
||||
String messageKey = (String) createResponse.get("messageKey")
|
||||
if ( Proxy.APIRESPONSE_SUCCESS.equals(returnCode) ||
|
||||
(Proxy.APIRESPONSE_FAILED.equals(returnCode) && (Proxy.MESSAGEKEY_IDNOTUNIQUE.equals(messageKey) || Proxy.MESSAGEKEY_DUPLICATEWARNING.equals(messageKey)) ) ){
|
||||
joinURL = bbbProxy.getJoinURL( userFullName, meetingID, isModerator? moderatorPW: attendeePW, (String) createResponse.get("createTime"), userID);
|
||||
(Proxy.APIRESPONSE_FAILED.equals(returnCode) && (Proxy.MESSAGEKEY_IDNOTUNIQUE.equals(messageKey) || Proxy.MESSAGEKEY_DUPLICATEWARNING.equals(messageKey)) ) ){
|
||||
joinURL = bbbProxy.getJoinURL( userFullName, meetingID, isModerator? moderatorPW: attendeePW, (String) createResponse.get("createTime"), userID);
|
||||
}
|
||||
}
|
||||
|
||||
return joinURL
|
||||
|
||||
}
|
||||
|
||||
|
||||
public Object getRecordings(params){
|
||||
//Set the injected values
|
||||
if( !url.equals(bbbProxy.url) && !url.equals("") ) bbbProxy.setUrl(url)
|
||||
if( !salt.equals(bbbProxy.salt) && !salt.equals("") ) bbbProxy.setSalt(salt)
|
||||
|
||||
String meetingID = getValidatedMeetingId(params.get(Parameter.RESOURCE_LINK_ID), params.get(Parameter.CONSUMER_ID))
|
||||
|
||||
|
||||
String recordingsURL = bbbProxy.getGetRecordingsURL( meetingID )
|
||||
log.debug "recordingsURL: " + recordingsURL
|
||||
Map<String, Object> recordings = doAPICall(recordingsURL)
|
||||
@ -135,7 +136,7 @@ class BigbluebuttonService {
|
||||
String messageKey = (String) recordings.get("messageKey")
|
||||
if ( Proxy.APIRESPONSE_SUCCESS.equals(returnCode) && messageKey == null ){
|
||||
return recordings.get("recordings")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
@ -147,9 +148,9 @@ class BigbluebuttonService {
|
||||
if( !salt.equals(bbbProxy.salt) && !salt.equals("") ) bbbProxy.setSalt(salt)
|
||||
|
||||
Map<String, Object> result
|
||||
|
||||
|
||||
String recordingId = getValidatedBBBRecordingId(params.get(Parameter.BBB_RECORDING_ID))
|
||||
|
||||
|
||||
if( !recordingId.equals("") ){
|
||||
String deleteRecordingsURL = bbbProxy.getDeleteRecordingsURL( recordingId )
|
||||
log.debug "deleteRecordingsURL: " + deleteRecordingsURL
|
||||
@ -162,17 +163,17 @@ class BigbluebuttonService {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
public Object doPublishRecordings(params){
|
||||
//Set the injected values
|
||||
if( !url.equals(bbbProxy.url) && !url.equals("") ) bbbProxy.setUrl(url)
|
||||
if( !salt.equals(bbbProxy.salt) && !salt.equals("") ) bbbProxy.setSalt(salt)
|
||||
|
||||
|
||||
Map<String, Object> result
|
||||
|
||||
String recordingId = getValidatedBBBRecordingId(params.get(Parameter.BBB_RECORDING_ID))
|
||||
String publish = getValidatedBBBRecordingPublished(params.get(Parameter.BBB_RECORDING_PUBLISHED))
|
||||
|
||||
|
||||
if( !recordingId.equals("") ){
|
||||
String publishRecordingsURL = bbbProxy.getPublishRecordingsURL( recordingId, "true".equals(publish)?"false":"true" )
|
||||
log.debug "publishRecordingsURL: " + publishRecordingsURL
|
||||
@ -185,23 +186,23 @@ class BigbluebuttonService {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
public boolean isModerator(params) {
|
||||
boolean isModerator = params.get(Parameter.ROLES) != null? Role.isModerator(params.get(Parameter.ROLES)): true
|
||||
return isModerator
|
||||
}
|
||||
|
||||
|
||||
private String getCreateURL(String name, String meetingID, String attendeePW, String moderatorPW, String welcome, Integer voiceBridge, String logoutURL, String record, Integer duration, String meta ) {
|
||||
voiceBridge = ( voiceBridge == null || voiceBridge == 0 )? 70000 + new Random(System.currentTimeMillis()).nextInt(10000): voiceBridge;
|
||||
|
||||
String url = bbbProxy.getCreateURL(name, meetingID, attendeePW, moderatorPW, welcome, "", voiceBridge.toString(), "", logoutURL, "", record, duration.toString(), meta );
|
||||
return url;
|
||||
}
|
||||
|
||||
|
||||
private String getValidatedMeetingName(String meetingName){
|
||||
return (meetingName == null || meetingName == "")? "Meeting": meetingName
|
||||
}
|
||||
|
||||
|
||||
private String getValidatedMeetingId(String resourceId, String consumerId){
|
||||
return DigestUtils.shaHex(resourceId + consumerId)
|
||||
}
|
||||
@ -209,7 +210,7 @@ class BigbluebuttonService {
|
||||
private String getValidatedLogoutURL(String logoutURL){
|
||||
return (logoutURL == null)? "": logoutURL
|
||||
}
|
||||
|
||||
|
||||
private String getValidatedUserFullName(params, boolean isModerator){
|
||||
String userFullName = params.get(Parameter.USER_FULL_NAME)
|
||||
String userFirstName = params.get(Parameter.USER_FIRSTNAME)
|
||||
@ -226,7 +227,7 @@ class BigbluebuttonService {
|
||||
userFullName = isModerator? "Moderator" : "Attendee"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return userFullName
|
||||
}
|
||||
|
||||
@ -237,15 +238,15 @@ class BigbluebuttonService {
|
||||
private String getValidatedUserId(String userId){
|
||||
return (userId == null)? "": userId
|
||||
}
|
||||
|
||||
|
||||
private Integer getValidatedBBBVoiceBridge(String voiceBridge){
|
||||
return (voiceBridge != null )? voiceBridge.toInteger(): 0
|
||||
}
|
||||
|
||||
|
||||
private String getValidatedBBBRecord(String record){
|
||||
return Boolean.valueOf(record).toString();
|
||||
}
|
||||
|
||||
|
||||
private Integer getValidatedBBBDuration(String duration){
|
||||
return (duration != null )? duration.toInteger(): 0
|
||||
}
|
||||
@ -257,7 +258,7 @@ class BigbluebuttonService {
|
||||
private String getValidatedBBBRecordingPublished(String published){
|
||||
return (published != null && published.equals("true") )? "true": "false"
|
||||
}
|
||||
|
||||
|
||||
private String getMonitoringMetaData(params){
|
||||
String meta
|
||||
|
||||
@ -269,10 +270,10 @@ class BigbluebuttonService {
|
||||
meta += "&meta_contextId=" + bbbProxy.getStringEncoded(params.get(Parameter.COURSE_ID) == null? "": params.get(Parameter.COURSE_ID))
|
||||
meta += "&meta_contextActivity=" + bbbProxy.getStringEncoded(params.get(Parameter.RESOURCE_LINK_TITLE) == null? "": params.get(Parameter.RESOURCE_LINK_TITLE))
|
||||
meta += "&meta_contextActivityDescription=" + bbbProxy.getStringEncoded(params.get(Parameter.RESOURCE_LINK_DESCRIPTION) == null? "": params.get(Parameter.RESOURCE_LINK_DESCRIPTION))
|
||||
|
||||
|
||||
return meta
|
||||
}
|
||||
|
||||
|
||||
/** Make an API call */
|
||||
private Map<String, Object> doAPICall(String query) {
|
||||
StringBuilder urlStr = new StringBuilder(query);
|
||||
@ -280,7 +281,7 @@ class BigbluebuttonService {
|
||||
try {
|
||||
// open connection
|
||||
//log.debug("doAPICall.call: " + query );
|
||||
|
||||
|
||||
URL url = new URL(urlStr.toString());
|
||||
HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
|
||||
httpConnection.setUseCaches(false);
|
||||
@ -300,14 +301,16 @@ class BigbluebuttonService {
|
||||
String line = reader.readLine();
|
||||
while (line != null) {
|
||||
if( !line.startsWith("<?xml version=\"1.0\"?>"))
|
||||
xml.append(line.trim());
|
||||
xml.append(line.trim());
|
||||
line = reader.readLine();
|
||||
}
|
||||
} finally {
|
||||
if (reader != null)
|
||||
reader.close();
|
||||
if (isr != null)
|
||||
isr.close();
|
||||
if ( reader != null ) {
|
||||
reader.close()
|
||||
}
|
||||
if ( isr != null ) {
|
||||
isr.close()
|
||||
}
|
||||
}
|
||||
httpConnection.disconnect();
|
||||
|
||||
@ -316,13 +319,13 @@ class BigbluebuttonService {
|
||||
//Patch to fix the NaN error
|
||||
String stringXml = xml.toString();
|
||||
stringXml = stringXml.replaceAll(">.\\s+?<", "><");
|
||||
|
||||
|
||||
Document dom = null;
|
||||
dom = docBuilder.parse(new InputSource( new StringReader(stringXml)));
|
||||
|
||||
|
||||
Map<String, Object> response = getNodesAsMap(dom, "response");
|
||||
//log.debug("doAPICall.responseMap: " + response);
|
||||
|
||||
|
||||
String returnCode = (String) response.get("returncode");
|
||||
if (Proxy.APIRESPONSE_FAILED.equals(returnCode)) {
|
||||
log.debug("doAPICall." + (String) response.get("messageKey") + ": Message=" + (String) response.get("message"));
|
||||
@ -344,7 +347,7 @@ class BigbluebuttonService {
|
||||
log.debug("doAPICall.Exception: Message=" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Get all nodes under the specified element tag name as a Java map */
|
||||
protected Map<String, Object> getNodesAsMap(Document dom, String elementTagName) {
|
||||
Node firstNode = dom.getElementsByTagName(elementTagName).item(0);
|
||||
@ -361,12 +364,12 @@ class BigbluebuttonService {
|
||||
&& ( node.getChildNodes().item(0).getNodeType() == org.w3c.dom.Node.TEXT_NODE || node.getChildNodes().item(0).getNodeType() == org.w3c.dom.Node.CDATA_SECTION_NODE) ) {
|
||||
String nodeValue = node.getTextContent();
|
||||
map.put(nodeName, nodeValue != null ? nodeValue.trim() : null);
|
||||
|
||||
|
||||
} else if (node.getChildNodes().getLength() == 0
|
||||
&& node.getNodeType() != org.w3c.dom.Node.TEXT_NODE
|
||||
&& node.getNodeType() != org.w3c.dom.Node.CDATA_SECTION_NODE) {
|
||||
map.put(nodeName, "");
|
||||
|
||||
|
||||
} else if ( node.getChildNodes().getLength() >= 1
|
||||
&& node.getChildNodes().item(0).getChildNodes().item(0).getNodeType() != org.w3c.dom.Node.TEXT_NODE
|
||||
&& node.getChildNodes().item(0).getChildNodes().item(0).getNodeType() != org.w3c.dom.Node.CDATA_SECTION_NODE ) {
|
||||
@ -377,12 +380,11 @@ class BigbluebuttonService {
|
||||
list.add(processNode(n));
|
||||
}
|
||||
map.put(nodeName, list);
|
||||
|
||||
|
||||
} else {
|
||||
map.put(nodeName, processNode(node));
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
@ -15,23 +15,23 @@
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.bigbluebutton
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import grails.transaction.Transactional
|
||||
import java.util.Map
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import javax.crypto.Mac
|
||||
import org.apache.commons.codec.binary.Base64
|
||||
|
||||
@Transactional
|
||||
class LtiService {
|
||||
|
||||
boolean transactional = false
|
||||
|
||||
def endPoint = "http://localhost/lti/tool"
|
||||
def consumers = "demo:welcome"
|
||||
def mode = "simple"
|
||||
|
||||
Map<String, String> consumerMap
|
||||
|
||||
|
||||
def retrieveIconEndpoint() {
|
||||
return endPoint.replaceFirst("tool", "images/icon.ico")
|
||||
}
|
||||
@ -39,16 +39,16 @@ class LtiService {
|
||||
def retrieveBasicLtiEndpoint() {
|
||||
return endPoint
|
||||
}
|
||||
|
||||
|
||||
private Map<String, String> getConsumer(consumerId) {
|
||||
Map<String, String> consumer = null
|
||||
|
||||
|
||||
if( this.consumerMap.containsKey(consumerId) ){
|
||||
consumer = new HashMap<String, String>()
|
||||
consumer.put("key", consumerId);
|
||||
consumer.put("key", consumerId)
|
||||
consumer.put("secret", this.consumerMap.get(consumerId))
|
||||
}
|
||||
|
||||
|
||||
return consumer
|
||||
}
|
||||
|
||||
@ -63,25 +63,22 @@ class LtiService {
|
||||
this.consumerMap.put(consumer[0], consumer[1])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String sign(String sharedSecret, String data) throws Exception
|
||||
{
|
||||
|
||||
public String sign(String sharedSecret, String data) throws Exception {
|
||||
Mac mac = setKey(sharedSecret)
|
||||
|
||||
|
||||
// Signed String must be BASE64 encoded.
|
||||
byte[] signBytes = mac.doFinal(data.getBytes("UTF8"));
|
||||
String signature = encodeBase64(signBytes);
|
||||
byte[] signBytes = mac.doFinal(data.getBytes("UTF8"))
|
||||
String signature = encodeBase64(signBytes)
|
||||
return signature;
|
||||
}
|
||||
|
||||
private Mac setKey(String sharedSecret) throws Exception
|
||||
{
|
||||
Mac mac = Mac.getInstance("HmacSHA1");
|
||||
byte[] keyBytes = sharedSecret.getBytes("UTF8");
|
||||
SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1");
|
||||
mac.init(signingKey);
|
||||
|
||||
private Mac setKey(String sharedSecret) throws Exception {
|
||||
Mac mac = Mac.getInstance("HmacSHA1")
|
||||
byte[] keyBytes = sharedSecret.getBytes("UTF8")
|
||||
SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1")
|
||||
mac.init(signingKey)
|
||||
return mac
|
||||
}
|
||||
|
@ -81,7 +81,6 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- index -->
|
||||
<a href="#page-body" class="skip"><g:message code="default.link.skip.label" default="Skip to content…"/></a>
|
||||
<div id="status" role="complementary">
|
||||
<h1>Application Status</h1>
|
||||
|
@ -1,32 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<!--[if lt IE 7 ]> <html lang="en" class="no-js ie6"> <![endif]-->
|
||||
<!--[if IE 7 ]> <html lang="en" class="no-js ie7"> <![endif]-->
|
||||
<!--[if IE 8 ]> <html lang="en" class="no-js ie8"> <![endif]-->
|
||||
<!--[if IE 9 ]> <html lang="en" class="no-js ie9"> <![endif]-->
|
||||
<!--[if (gt IE 9)|!(IE)]><!--> <html lang="en" class="no-js"><!--<![endif]-->
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title><g:message code="tool.view.title" /></title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" href="${resource(dir: 'images', file: 'favicon.ico')}" type="image/x-icon">
|
||||
<link rel="apple-touch-icon" href="${resource(dir: 'images', file: 'apple-touch-icon.png')}">
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="${resource(dir: 'images', file: 'apple-touch-icon-retina.png')}">
|
||||
<link rel="stylesheet" href="${resource(dir: 'css', file: 'main.css')}" type="text/css">
|
||||
<link rel="stylesheet" href="${resource(dir: 'css', file: 'mobile.css')}" type="text/css">
|
||||
<g:layoutHead/>
|
||||
<g:javascript library="application"/>
|
||||
<r:layoutResources />
|
||||
</head>
|
||||
<body>
|
||||
<div id="spinner" class="spinner" style="display:none;">
|
||||
<img src="${resource(dir:'images',file:'spinner.gif')}" alt="Spinner" />
|
||||
</div>
|
||||
<div class="logo" id="logo" role="banner"><img src="${resource(dir: 'images', file: 'bbb_logo.jpg')}" alt="<g:message code="tool.view.app" />" /></div>
|
||||
<g:layoutBody />
|
||||
|
||||
<div class="footer" role="contentinfo"></div>
|
||||
<div id="spinner" class="spinner" style="display:none;"><g:message code="spinner.alt" default="Loading…"/></div>
|
||||
<r:layoutResources />
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html>
|
||||
<!--[if lt IE 7 ]> <html lang="en" class="no-js ie6"> <![endif]-->
|
||||
<!--[if IE 7 ]> <html lang="en" class="no-js ie7"> <![endif]-->
|
||||
<!--[if IE 8 ]> <html lang="en" class="no-js ie8"> <![endif]-->
|
||||
<!--[if IE 9 ]> <html lang="en" class="no-js ie9"> <![endif]-->
|
||||
<!--[if (gt IE 9)|!(IE)]><!--> <html lang="en" class="no-js"><!--<![endif]-->
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title><g:layoutTitle default="Grails"/></title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" href="${resource(dir: 'images', file: 'favicon.ico')}" type="image/x-icon">
|
||||
<link rel="apple-touch-icon" href="${resource(dir: 'images', file: 'apple-touch-icon.png')}">
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="${resource(dir: 'images', file: 'apple-touch-icon-retina.png')}">
|
||||
<link rel="stylesheet" href="${resource(dir: 'css', file: 'main.css')}" type="text/css">
|
||||
<link rel="stylesheet" href="${resource(dir: 'css', file: 'mobile.css')}" type="text/css">
|
||||
<g:layoutHead/>
|
||||
<g:javascript library="application"/>
|
||||
<r:layoutResources />
|
||||
</head>
|
||||
<body>
|
||||
<div id="grailsLogo" role="banner"><a href="http://grails.org"><img src="${resource(dir: 'images', file: 'grails_logo.png')}" alt="Grails"/></a></div>
|
||||
<g:layoutBody/>
|
||||
<div class="footer" role="contentinfo"></div>
|
||||
<div id="spinner" class="spinner" style="display:none;"><g:message code="spinner.alt" default="Loading…"/></div>
|
||||
<r:layoutResources />
|
||||
</body>
|
||||
</html>
|
||||
|
@ -0,0 +1,20 @@
|
||||
package org.bigbluebutton
|
||||
|
||||
import grails.test.mixin.TestFor
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
* See the API for {@link grails.test.mixin.services.ServiceUnitTestMixin} for usage instructions
|
||||
*/
|
||||
@TestFor(BigbluebuttonService)
|
||||
class BigbluebuttonServiceSpec extends Specification {
|
||||
|
||||
def setup() {
|
||||
}
|
||||
|
||||
def cleanup() {
|
||||
}
|
||||
|
||||
void "test something"() {
|
||||
}
|
||||
}
|
20
bbb-lti/test/unit/org/bigbluebutton/LtiServiceSpec.groovy
Normal file
20
bbb-lti/test/unit/org/bigbluebutton/LtiServiceSpec.groovy
Normal file
@ -0,0 +1,20 @@
|
||||
package org.bigbluebutton
|
||||
|
||||
import grails.test.mixin.TestFor
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
* See the API for {@link grails.test.mixin.services.ServiceUnitTestMixin} for usage instructions
|
||||
*/
|
||||
@TestFor(LtiService)
|
||||
class LtiServiceSpec extends Specification {
|
||||
|
||||
def setup() {
|
||||
}
|
||||
|
||||
def cleanup() {
|
||||
}
|
||||
|
||||
void "test something"() {
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package org.bigbluebutton
|
||||
|
||||
import grails.test.mixin.TestFor
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
* See the API for {@link grails.test.mixin.web.ControllerUnitTestMixin} for usage instructions
|
||||
*/
|
||||
@TestFor(ToolController)
|
||||
class ToolControllerSpec extends Specification {
|
||||
|
||||
def setup() {
|
||||
}
|
||||
|
||||
def cleanup() {
|
||||
}
|
||||
|
||||
void "test something"() {
|
||||
}
|
||||
}
|
@ -1,3 +1,20 @@
|
||||
/*
|
||||
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
|
||||
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
if (typeof jQuery !== 'undefined') {
|
||||
(function($) {
|
||||
$('#spinner').ajaxStart(function() {
|
||||
|
Loading…
Reference in New Issue
Block a user