From adffa8d67311fd58a0aee6f658f5f8e316cb23be Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Thu, 9 Mar 2017 20:32:14 +0000 Subject: [PATCH 01/16] - build getMeetings and getMeetingInfo responses using freemarker --- bbb-common-web/.gitignore | 56 ++++++ bbb-common-web/README.md | 1 + bbb-common-web/build.sbt | 90 +++++++++ bbb-common-web/project/Build.scala | 0 bbb-common-web/project/build.properties | 1 + bbb-common-web/project/plugins.sbt | 8 + .../org/bigbluebutton/api/domain/Config.java | 0 .../bigbluebutton/api/domain/Extension.java | 0 .../org/bigbluebutton/api/domain/Meeting.java | 21 +- .../bigbluebutton/api/domain/Playback.java | 0 .../org/bigbluebutton/api/domain/Poll.java | 0 .../bigbluebutton/api/domain/Recording.java | 0 .../bigbluebutton/api/domain/Recordings.java | 0 .../org/bigbluebutton/api/domain/User.java | 0 .../bigbluebutton/api/domain/UserSession.java | 0 .../api/util/MeetingResponseDetail.java | 23 +++ .../api/util/MeetingsResponse.java | 12 ++ .../api/util/ResponseBuilder.java | 97 +++++++++ .../src/test/resources/get-meeting-info.ftl | 78 ++++++++ .../src/test/resources/get-meetings.ftl | 78 ++++++++ .../api/util/ResponseBuilderTest.scala | 185 ++++++++++++++++++ .../org/bigbluebutton/api/util/UnitSpec.scala | 8 + bigbluebutton-web/build.gradle | 1 + .../web/controllers/ApiController.groovy | 58 +++--- .../org/bigbluebutton/api/MeetingService.java | 19 +- .../api/messaging/MessagingService.java | 1 + .../api/messaging/RedisMessagingService.java | 4 + .../api/messaging/RedisStorageService.java | 13 ++ .../WEB-INF/freemarker/get-meeting-info.ftl | 78 ++++++++ .../WEB-INF/freemarker/get-meetings.ftl | 78 ++++++++ 30 files changed, 860 insertions(+), 50 deletions(-) create mode 100644 bbb-common-web/.gitignore create mode 100644 bbb-common-web/README.md create mode 100755 bbb-common-web/build.sbt create mode 100755 bbb-common-web/project/Build.scala create mode 100755 bbb-common-web/project/build.properties create mode 100755 bbb-common-web/project/plugins.sbt rename {bigbluebutton-web/src => bbb-common-web/src/main}/java/org/bigbluebutton/api/domain/Config.java (100%) rename {bigbluebutton-web/src => bbb-common-web/src/main}/java/org/bigbluebutton/api/domain/Extension.java (100%) mode change 100644 => 100755 rename {bigbluebutton-web/src => bbb-common-web/src/main}/java/org/bigbluebutton/api/domain/Meeting.java (94%) rename {bigbluebutton-web/src => bbb-common-web/src/main}/java/org/bigbluebutton/api/domain/Playback.java (100%) rename {bigbluebutton-web/src => bbb-common-web/src/main}/java/org/bigbluebutton/api/domain/Poll.java (100%) rename {bigbluebutton-web/src => bbb-common-web/src/main}/java/org/bigbluebutton/api/domain/Recording.java (100%) rename {bigbluebutton-web/src => bbb-common-web/src/main}/java/org/bigbluebutton/api/domain/Recordings.java (100%) rename {bigbluebutton-web/src => bbb-common-web/src/main}/java/org/bigbluebutton/api/domain/User.java (100%) rename {bigbluebutton-web/src => bbb-common-web/src/main}/java/org/bigbluebutton/api/domain/UserSession.java (100%) create mode 100755 bbb-common-web/src/main/java/org/bigbluebutton/api/util/MeetingResponseDetail.java create mode 100755 bbb-common-web/src/main/java/org/bigbluebutton/api/util/MeetingsResponse.java create mode 100755 bbb-common-web/src/main/java/org/bigbluebutton/api/util/ResponseBuilder.java create mode 100755 bbb-common-web/src/test/resources/get-meeting-info.ftl create mode 100755 bbb-common-web/src/test/resources/get-meetings.ftl create mode 100755 bbb-common-web/src/test/scala/org/bigbluebutton/api/util/ResponseBuilderTest.scala create mode 100755 bbb-common-web/src/test/scala/org/bigbluebutton/api/util/UnitSpec.scala create mode 100755 bigbluebutton-web/web-app/WEB-INF/freemarker/get-meeting-info.ftl create mode 100755 bigbluebutton-web/web-app/WEB-INF/freemarker/get-meetings.ftl diff --git a/bbb-common-web/.gitignore b/bbb-common-web/.gitignore new file mode 100644 index 0000000000..82f02806ec --- /dev/null +++ b/bbb-common-web/.gitignore @@ -0,0 +1,56 @@ +.DS_Store +._.DS_Store* +.metadata +.project +.classpath +.settings +.history +.worksheet +gen +**/*.swp +**/*~.nib +**/build/ +**/*.pbxuser +**/*.perspective +**/*.perspectivev3 +*.xcworkspace +*.xcuserdatad +**/target +target +*.iml +project/*.ipr +project/*.iml +project/*.iws +project/out +project/*/target +project/target +project/*/bin +project/*/build +project/*.iml +project/*/*.iml +project/.idea +project/.idea/* +.idea +.idea/* +.idea/**/* +.DS_Store +project/.DS_Store +project/*/.DS_Store +tm.out +tmlog*.log +*.tm*.epoch +out/ +provisioning/.vagrant +provisioning/*/.vagrant +provisioning/*/*.known +/sbt/akka-patterns-store/ +/daemon/src/build/ +*.lock +log/ +tmp/ +build/ +akka-patterns-store/ +lib_managed/ +.cache +bin/ + diff --git a/bbb-common-web/README.md b/bbb-common-web/README.md new file mode 100644 index 0000000000..3cfa0f7331 --- /dev/null +++ b/bbb-common-web/README.md @@ -0,0 +1 @@ + see http://code.google.com/p/bigbluebutton/wiki/DevelopingBBB diff --git a/bbb-common-web/build.sbt b/bbb-common-web/build.sbt new file mode 100755 index 0000000000..1e694fb23f --- /dev/null +++ b/bbb-common-web/build.sbt @@ -0,0 +1,90 @@ +name := "bbb-common-web" + +organization := "org.bigbluebutton" + +version := "0.0.1-SNAPSHOT" + +// We want to have our jar files in lib_managed dir. +// This way we'll have the right path when we import +// into eclipse. +retrieveManaged := true + +testOptions in Test += Tests.Argument(TestFrameworks.Specs2, "html", "console", "junitxml") + +testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-h", "target/scalatest-reports") + +libraryDependencies += "commons-lang" % "commons-lang" % "2.5" +libraryDependencies += "org.freemarker" % "freemarker" % "2.3.23" + +libraryDependencies += "org.pegdown" % "pegdown" % "1.4.0" % "test" +libraryDependencies += "junit" % "junit" % "4.12" % "test" +libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test" + +// https://mvnrepository.com/artifact/org.mockito/mockito-core +libraryDependencies += "org.mockito" % "mockito-core" % "2.7.12" % "test" +libraryDependencies += "org.scalactic" %% "scalactic" % "3.0.1" % "test" +libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.1" % "test" + +seq(Revolver.settings: _*) + +//----------- +// Packaging +// +// Reference: +// http://xerial.org/blog/2014/03/24/sbt/ +// http://www.scala-sbt.org/sbt-pgp/usage.html +// http://www.scala-sbt.org/0.13/docs/Using-Sonatype.html +// http://central.sonatype.org/pages/requirements.html +// http://central.sonatype.org/pages/releasing-the-deployment.html +//----------- + +// Build pure Java lib (i.e. without scala) +// Do not append Scala versions to the generated artifacts +crossPaths := false + +// This forbids including Scala related libraries into the dependency +autoScalaLibrary := false + +/*************************** +* When developing, change the version above to x.x.x-SNAPSHOT then use the file resolver to +* publish to the local maven repo using "sbt publish" +*/ +// Uncomment this to publish to local maven repo while commenting out the nexus repo +publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/.m2/repository"))) + + +// Comment this out when publishing to local maven repo using SNAPSHOT version. +// To push to sonatype "sbt publishSigned" +//publishTo := { +// val nexus = "https://oss.sonatype.org/" +// if (isSnapshot.value) +// Some("snapshots" at nexus + "content/repositories/snapshots") +// else +// Some("releases" at nexus + "service/local/staging/deploy/maven2") +//} + +// Enables publishing to maven repo +publishMavenStyle := true + +publishArtifact in Test := false + +pomIncludeRepository := { _ => false } + +pomExtra := ( + + git@github.com:bigbluebutton/bigbluebutton.git + scm:git:git@github.com:bigbluebutton/bigbluebutton.git + + + + ritzalam + Richard Alam + http://www.bigbluebutton.org + + ) + +licenses := Seq("LGPL-3.0" -> url("http://opensource.org/licenses/LGPL-3.0")) + +homepage := Some(url("http://www.bigbluebutton.org")) + + diff --git a/bbb-common-web/project/Build.scala b/bbb-common-web/project/Build.scala new file mode 100755 index 0000000000..e69de29bb2 diff --git a/bbb-common-web/project/build.properties b/bbb-common-web/project/build.properties new file mode 100755 index 0000000000..a6e117b610 --- /dev/null +++ b/bbb-common-web/project/build.properties @@ -0,0 +1 @@ +sbt.version=0.13.8 diff --git a/bbb-common-web/project/plugins.sbt b/bbb-common-web/project/plugins.sbt new file mode 100755 index 0000000000..d33e342247 --- /dev/null +++ b/bbb-common-web/project/plugins.sbt @@ -0,0 +1,8 @@ +addSbtPlugin("io.spray" % "sbt-revolver" % "0.7.2") + +addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.2.0") + +addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") + + + diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Config.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Config.java similarity index 100% rename from bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Config.java rename to bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Config.java diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Extension.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Extension.java old mode 100644 new mode 100755 similarity index 100% rename from bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Extension.java rename to bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Extension.java diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Meeting.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java similarity index 94% rename from bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Meeting.java rename to bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java index 6af99082cb..733d18432e 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Meeting.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java @@ -19,20 +19,14 @@ package org.bigbluebutton.api.domain; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; - import org.apache.commons.lang.RandomStringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; + public class Meeting { - private static Logger log = LoggerFactory.getLogger(Meeting.class); - + private static final long MILLIS_IN_A_MINUTE = 60000; private String name; @@ -68,6 +62,7 @@ public class Meeting { private final ConcurrentMap registeredUsers; private final ConcurrentMap configs; private final Boolean isBreakout; + private final List breakoutRooms = new ArrayList(); private long lastUserLeftOn = 0; @@ -102,6 +97,14 @@ public class Meeting { configs = new ConcurrentHashMap(); } + public void addBreakoutRoom(String meetingId) { + breakoutRooms.add(meetingId); + } + + public List getBreakoutRooms() { + return breakoutRooms; + } + public String storeConfig(boolean defaultConfig, String config) { String token = RandomStringUtils.randomAlphanumeric(8); while (configs.containsKey(token)) { diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Playback.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Playback.java similarity index 100% rename from bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Playback.java rename to bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Playback.java diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Poll.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Poll.java similarity index 100% rename from bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Poll.java rename to bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Poll.java diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Recording.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Recording.java similarity index 100% rename from bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Recording.java rename to bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Recording.java diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Recordings.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Recordings.java similarity index 100% rename from bigbluebutton-web/src/java/org/bigbluebutton/api/domain/Recordings.java rename to bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Recordings.java diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/domain/User.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/User.java similarity index 100% rename from bigbluebutton-web/src/java/org/bigbluebutton/api/domain/User.java rename to bbb-common-web/src/main/java/org/bigbluebutton/api/domain/User.java diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/domain/UserSession.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/UserSession.java similarity index 100% rename from bigbluebutton-web/src/java/org/bigbluebutton/api/domain/UserSession.java rename to bbb-common-web/src/main/java/org/bigbluebutton/api/domain/UserSession.java diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/MeetingResponseDetail.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/MeetingResponseDetail.java new file mode 100755 index 0000000000..d9ebe5293f --- /dev/null +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/MeetingResponseDetail.java @@ -0,0 +1,23 @@ +package org.bigbluebutton.api.util; + + +import org.bigbluebutton.api.domain.Meeting; + +public class MeetingResponseDetail { + + private final String createdOn; + private final Meeting meeting; + + public MeetingResponseDetail(String createdOn, Meeting meeting) { + this.createdOn = createdOn; + this.meeting = meeting; + } + + public String getCreatedOn() { + return createdOn; + } + + public Meeting getMeeting() { + return meeting; + } +} diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/MeetingsResponse.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/MeetingsResponse.java new file mode 100755 index 0000000000..0e67f3eadc --- /dev/null +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/MeetingsResponse.java @@ -0,0 +1,12 @@ +package org.bigbluebutton.api.util; + +import java.util.Collection; + +public class MeetingsResponse { + + public final Collection meetings; + + public MeetingsResponse(Collection meetings) { + this.meetings = meetings; + } +} diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ResponseBuilder.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ResponseBuilder.java new file mode 100755 index 0000000000..86685d86fd --- /dev/null +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ResponseBuilder.java @@ -0,0 +1,97 @@ +package org.bigbluebutton.api.util; + +import org.bigbluebutton.api.domain.Meeting; +import org.apache.commons.lang.StringUtils; + +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.util.*; + +import freemarker.template.*; + +public class ResponseBuilder { + Configuration cfg = new Configuration(Configuration.VERSION_2_3_23); + + public ResponseBuilder(File templatesLoc) { + + try { + cfg.setDirectoryForTemplateLoading(templatesLoc); + } catch (IOException e) { + e.printStackTrace(); + } + cfg.setDefaultEncoding("UTF-8"); + cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); + cfg.setLogTemplateExceptions(false); + } + + public String formatPrettyDate(Long timestamp) { + return new Date(timestamp).toString(); + } + + public String buildGetMeetingInfoResponse(Meeting meeting, String returnCode) { + String createdOn = formatPrettyDate(meeting.getCreateTime()); + + Template ftl = null; + try { + ftl = cfg.getTemplate("get-meeting-info.ftl"); + } catch (IOException e) { + e.printStackTrace(); + } + + StringWriter xmlText = new StringWriter(); + + Map root = new HashMap(); + root.put("returnCode", returnCode); + root.put("createdOn", createdOn); + root.put("meeting", meeting); + + try { + ftl.process(root, xmlText); + } catch (TemplateException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + return xmlText.toString(); + } + + public String buildGetMeetingsResponse(Collection meetings, String returnCode) { + + ArrayList meetingResponseDetails = new ArrayList(); + + for (Meeting meeting : meetings) { + String createdOn = formatPrettyDate(meeting.getCreateTime()); + MeetingResponseDetail details = new MeetingResponseDetail(createdOn, meeting); + meetingResponseDetails.add(details); + } + + Template ftl = null; + try { + ftl = cfg.getTemplate("get-meetings.ftl"); + } catch (IOException e) { + e.printStackTrace(); + } + + StringWriter xmlText = new StringWriter(); + + Map root = new HashMap(); + root.put("returnCode", returnCode); + root.put("meetingDetailsList", meetingResponseDetails); + + for (MeetingResponseDetail details : (ArrayList)root.get("meetingDetailsList")) { + System.out.println(details.getMeeting().getName()); + } + + try { + ftl.process(root, xmlText); + } catch (TemplateException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + return xmlText.toString(); + } +} diff --git a/bbb-common-web/src/test/resources/get-meeting-info.ftl b/bbb-common-web/src/test/resources/get-meeting-info.ftl new file mode 100755 index 0000000000..c26d16d0e5 --- /dev/null +++ b/bbb-common-web/src/test/resources/get-meeting-info.ftl @@ -0,0 +1,78 @@ +<#-- GET_RECORDINGS FreeMarker XML template --> + + <#-- Where code is a 'SUCCESS' or 'FAILED' String --> + ${returnCode} + ${meeting.getName()} + ${meeting.getExternalId()} + ${meeting.getInternalId()} + ${meeting.getCreateTime()?c} + ${createdOn} + ${meeting.getTelVoice()} + ${meeting.getDialNumber()} + ${meeting.getViewerPassword()} + ${meeting.getModeratorPassword()} + ${meeting.isRunning()?c} + ${meeting.getDuration()} + ${meeting.hasUserJoined()?c} + ${meeting.isRecord()?c} + ${meeting.isForciblyEnded()?c} + ${meeting.getStartTime()?c} + ${meeting.getEndTime()} + ${meeting.getNumUsers()} + ${meeting.getNumListenOnly()} + ${meeting.getNumVoiceJoined()} + ${meeting.getNumVideos()} + ${meeting.getMaxUsers()} + ${meeting.getNumModerators()} + + <#list meeting.getUsers() as att> + + ${att.getInternalUserId()} + ${att.getFullname()} + ${att.getRole()} + ${att.isPresenter()?c} + ${att.isListeningOnly()?c} + ${att.isVoiceJoined()?c} + ${att.hasVideo()?c} + <#if meeting.getUserCustomData(att.getExternalUserId())??> + <#assign ucd = meeting.getUserCustomData(att.getExternalUserId())> + + <#list ucd?keys as prop> + <${prop}> + + + + + + + <#assign m = meeting.getMetadata()> + + <#list m?keys as prop> + <${prop}> + + + + <#if messageKey?has_content> + ${messageKey} + + + <#if message?has_content> + ${message} + + + <#if meeting.isBreakout()> + + ${meeting.getParentMeetingId()} + ${meeting.getSequence()} + + + + <#list meeting.getBreakoutRooms()> + + <#items as room> + ${room} + + + + + \ No newline at end of file diff --git a/bbb-common-web/src/test/resources/get-meetings.ftl b/bbb-common-web/src/test/resources/get-meetings.ftl new file mode 100755 index 0000000000..fdee481dbd --- /dev/null +++ b/bbb-common-web/src/test/resources/get-meetings.ftl @@ -0,0 +1,78 @@ +<#-- GET_RECORDINGS FreeMarker XML template --> + + <#-- Where code is a 'SUCCESS' or 'FAILED' String --> + ${returnCode} + <#list meetingDetailsList> + + <#items as meetingDetail> + <#assign meeting = meetingDetail.getMeeting()> + + ${meeting.getName()} + ${meeting.getExternalId()} + ${meeting.getInternalId()} + ${meeting.getCreateTime()?c} + ${meetingDetail.getCreatedOn()} + ${meeting.getTelVoice()} + ${meeting.getDialNumber()} + ${meeting.getViewerPassword()} + ${meeting.getModeratorPassword()} + ${meeting.isRunning()?c} + ${meeting.getDuration()} + ${meeting.hasUserJoined()?c} + ${meeting.isRecord()?c} + ${meeting.isForciblyEnded()?c} + ${meeting.getStartTime()?c} + ${meeting.getEndTime()} + ${meeting.getNumUsers()} + ${meeting.getNumListenOnly()} + ${meeting.getNumVoiceJoined()} + ${meeting.getNumVideos()} + ${meeting.getMaxUsers()} + ${meeting.getNumModerators()} + + <#list meetingDetail.meeting.getUsers() as att> + + ${att.getInternalUserId()} + ${att.getFullname()} + ${att.getRole()} + ${att.isPresenter()?c} + ${att.isListeningOnly()?c} + ${att.isVoiceJoined()?c} + ${att.hasVideo()?c} + <#if meeting.getUserCustomData(att.getExternalUserId())??> + <#assign ucd = meetingDetail.meeting.getUserCustomData(att.getExternalUserId())> + + <#list ucd?keys as prop> + <${prop}> + + + + + + + <#assign m = meetingDetail.meeting.getMetadata()> + + <#list m?keys as prop> + <${prop}> + + + + <#if meetingDetail.meeting.isBreakout()> + + ${meetingDetail.meeting.getParentMeetingId()} + ${meetingDetail.meeting.getSequence()} + + + + <#list meetingDetail.meeting.getBreakoutRooms()> + + <#items as room> + ${room} + + + + + + + + \ No newline at end of file diff --git a/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/ResponseBuilderTest.scala b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/ResponseBuilderTest.scala new file mode 100755 index 0000000000..b8a786f4ae --- /dev/null +++ b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/ResponseBuilderTest.scala @@ -0,0 +1,185 @@ +package org.bigbluebutton.api.util + +import java.io.File +import java.util + +import org.bigbluebutton.api.domain.{Meeting, User} +import org.scalatest._ + +class ResponseBuilderTest extends UnitSpec { + + it should "find template" in { + val current = new java.io.File( "." ).getCanonicalPath() + println("Current dir:"+current) + + + + val meetingInfo = new util.TreeMap[String, String]() + meetingInfo.put("foo", "foo") + meetingInfo.put("bar", "baz") + + val meeting: Meeting = new Meeting.Builder("extMid", "intMid", System.currentTimeMillis()) + .withName("Demo Meeting").withMaxUsers(25) + .withModeratorPass("mp").withViewerPass("ap") + .withRecording(true).withDuration(600) + .withLogoutUrl("/logoutUrl").withTelVoice("85115").withWebVoice("85115") + .withDialNumber("6135551234").withDefaultAvatarURL("/avatar") + .withAutoStartRecording(false).withAllowStartStopRecording(true) + .withWebcamsOnlyForModerator(false).withMetadata(meetingInfo) + .withWelcomeMessageTemplate("hello").withWelcomeMessage("hello") + .isBreakout(false).build + + meeting.setParentMeetingId("ParentMeetingId") + meeting.setSequence(0); + + meeting.addBreakoutRoom("breakout-room-id-1") + meeting.addBreakoutRoom("breakout-room-id-2") + meeting.addBreakoutRoom("breakout-room-id-3") + + val user: User = new User("uid1", "extuid1", "Richard", "moderator", "/aygwapo") + meeting.userJoined(user) + + val user2: User = new User("uid2", "extuid2", "Richard 2", "moderator", "/aygwapo") + meeting.userJoined(user2) + + val user3: User = new User("uid3", "extuid3", "Richard 3", "moderator", "/aygwapo") + meeting.userJoined(user2) + + val custData = new util.HashMap[String, String]() + custData.put("gwapo", "totoo") + + meeting.addUserCustomData("extuid1", custData) + + val templateLoc = new File("src/test/resources") + val builder = new ResponseBuilder(templateLoc) + def response = builder.buildGetMeetingInfoResponse(meeting, "success") + println(response) + + assert(templateLoc.exists()) + } + + it should "return meetings" in { + val meetingInfo1 = new util.TreeMap[String, String]() + meetingInfo1.put("foo", "foo") + meetingInfo1.put("bar", "baz") + + val meeting1: Meeting = new Meeting.Builder("extMid1", "intMid1", System.currentTimeMillis()) + .withName("Demo Meeting 1").withMaxUsers(25) + .withModeratorPass("mp").withViewerPass("ap") + .withRecording(true).withDuration(600) + .withLogoutUrl("/logoutUrl").withTelVoice("85115").withWebVoice("85115") + .withDialNumber("6135551234").withDefaultAvatarURL("/avatar") + .withAutoStartRecording(false).withAllowStartStopRecording(true) + .withWebcamsOnlyForModerator(false).withMetadata(meetingInfo1) + .withWelcomeMessageTemplate("hello").withWelcomeMessage("hello") + .isBreakout(false).build + + meeting1.setParentMeetingId("ParentMeetingId") + meeting1.setSequence(0); + + meeting1.addBreakoutRoom("breakout-room-id-1") + meeting1.addBreakoutRoom("breakout-room-id-2") + meeting1.addBreakoutRoom("breakout-room-id-3") + + val userm11: User = new User("uid1", "extuid1", "Richard", "moderator", "/aygwapo") + meeting1.userJoined(userm11) + + val userm12: User = new User("uid2", "extuid2", "Richard 2", "moderator", "/aygwapo") + meeting1.userJoined(userm12) + + val userm13: User = new User("uid3", "extuid3", "Richard 3", "moderator", "/aygwapo") + meeting1.userJoined(userm13) + + val custDatam1 = new util.HashMap[String, String]() + custDatam1.put("gwapo", "totoo") + + meeting1.addUserCustomData("extuid1", custDatam1) + + val meetingInfo2 = new util.TreeMap[String, String]() + meetingInfo2.put("foo", "foo") + meetingInfo2.put("bar", "baz") + + val meeting2: Meeting = new Meeting.Builder("extMid2", "intMid2", System.currentTimeMillis()) + .withName("Demo Meeting 2").withMaxUsers(25) + .withModeratorPass("mp").withViewerPass("ap") + .withRecording(true).withDuration(600) + .withLogoutUrl("/logoutUrl").withTelVoice("85115").withWebVoice("85115") + .withDialNumber("6135551234").withDefaultAvatarURL("/avatar") + .withAutoStartRecording(false).withAllowStartStopRecording(true) + .withWebcamsOnlyForModerator(false).withMetadata(meetingInfo2) + .withWelcomeMessageTemplate("hello").withWelcomeMessage("hello") + .isBreakout(false).build + + meeting2.setParentMeetingId("ParentMeetingId") + meeting2.setSequence(0); + + meeting2.addBreakoutRoom("breakout-room-id-1") + meeting2.addBreakoutRoom("breakout-room-id-2") + meeting2.addBreakoutRoom("breakout-room-id-3") + + val userm21: User = new User("uid1", "extuid1", "Richard", "moderator", "/aygwapo") + meeting2.userJoined(userm21) + + val userm22: User = new User("uid2", "extuid2", "Richard 2", "moderator", "/aygwapo") + meeting2.userJoined(userm22) + + val userm23: User = new User("uid3", "extuid3", "Richard 3", "moderator", "/aygwapo") + meeting2.userJoined(userm23) + + val custDatam2 = new util.HashMap[String, String]() + custDatam2.put("gwapo", "totoo") + + meeting2.addUserCustomData("extuid1", custDatam2) + + + val meetingInfo3 = new util.TreeMap[String, String]() + meetingInfo3.put("foo", "foo") + meetingInfo3.put("bar", "baz") + + val meeting3: Meeting = new Meeting.Builder("extMid", "intMid", System.currentTimeMillis()) + .withName("Demo Meeting").withMaxUsers(25) + .withModeratorPass("mp").withViewerPass("ap") + .withRecording(true).withDuration(600) + .withLogoutUrl("/logoutUrl").withTelVoice("85115").withWebVoice("85115") + .withDialNumber("6135551234").withDefaultAvatarURL("/avatar") + .withAutoStartRecording(false).withAllowStartStopRecording(true) + .withWebcamsOnlyForModerator(false).withMetadata(meetingInfo3) + .withWelcomeMessageTemplate("hello").withWelcomeMessage("hello") + .isBreakout(false).build + + meeting3.setParentMeetingId("ParentMeetingId") + meeting3.setSequence(0); + + meeting3.addBreakoutRoom("breakout-room-id-1") + meeting3.addBreakoutRoom("breakout-room-id-2") + meeting3.addBreakoutRoom("breakout-room-id-3") + + val user: User = new User("uid1", "extuid1", "Richard", "moderator", "/aygwapo") + meeting3.userJoined(user) + + val user2: User = new User("uid2", "extuid2", "Richard 2", "moderator", "/aygwapo") + meeting3.userJoined(user2) + + val user3: User = new User("uid3", "extuid3", "Richard 3", "moderator", "/aygwapo") + meeting3.userJoined(user2) + + val custData = new util.HashMap[String, String]() + custData.put("gwapo", "totoo") + + meeting3.addUserCustomData("extuid1", custData) + + + + val meetings = new util.ArrayList[Meeting]() + meetings.add(meeting1) + meetings.add(meeting2) + meetings.add(meeting3) + + val templateLoc = new File("src/test/resources") + val builder = new ResponseBuilder(templateLoc) + def response = builder.buildGetMeetingsResponse(meetings, "success") + println(response) + + assert(templateLoc.exists()) + } +} \ No newline at end of file diff --git a/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/UnitSpec.scala b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/UnitSpec.scala new file mode 100755 index 0000000000..fef2d5a231 --- /dev/null +++ b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/UnitSpec.scala @@ -0,0 +1,8 @@ +package org.bigbluebutton.api.util + +import org.scalatest.FlatSpec +import org.scalatest.BeforeAndAfterAll +import org.scalatest.WordSpec +import org.scalatest.Matchers + +abstract class UnitSpec extends FlatSpec with Matchers with BeforeAndAfterAll diff --git a/bigbluebutton-web/build.gradle b/bigbluebutton-web/build.gradle index ce6c71534b..9ada920238 100755 --- a/bigbluebutton-web/build.gradle +++ b/bigbluebutton-web/build.gradle @@ -26,6 +26,7 @@ dependencies { compile 'com.zaxxer:nuprocess:1.1.0' compile 'org.bigbluebutton:bbb-common-message:0.0.18-SNAPSHOT' + compile 'org.bigbluebutton:bbb-common-web:0.0.1-SNAPSHOT' // Logging // Commenting out as it results in build failure (ralam - may 11, 2014) diff --git a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy index 5cf6e37d54..50e6a46615 100755 --- a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy +++ b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy @@ -19,6 +19,7 @@ package org.bigbluebutton.web.controllers import com.google.gson.Gson +import org.bigbluebutton.api.util.ResponseBuilder import javax.servlet.ServletRequest; @@ -53,9 +54,10 @@ import org.bigbluebutton.web.services.turn.StunTurnService; import org.bigbluebutton.web.services.turn.TurnEntry; import org.json.JSONArray; import org.json.JSONObject; - +import org.bigbluebutton.api.util.ResponseBuilder import freemarker.template.Configuration; import freemarker.cache.WebappTemplateLoader; +import java.io.File; class ApiController { private static final Integer SESSION_TIMEOUT = 14400 // 4 hours @@ -75,6 +77,8 @@ class ApiController { PresentationUrlDownloadService presDownloadService StunTurnService stunTurnService + + /* general methods */ def index = { log.debug CONTROLLER_NAME + "#index" @@ -791,7 +795,15 @@ class ApiController { return; } - respondWithConferenceDetails(meeting, null, null, null); + def templateLoc = getServletContext().getRealPath("/WEB-INF/freemarker") + ResponseBuilder responseBuilder = new ResponseBuilder(new File(templateLoc)) + + def xmlText = responseBuilder.buildGetMeetingInfoResponse(meeting, "success") + withFormat { + xml { + render(text: xmlText, contentType: "text/xml") + } + } } /************************************ @@ -850,41 +862,14 @@ class ApiController { } } else { response.addHeader("Cache-Control", "no-cache") + + def templateLoc = getServletContext().getRealPath("/WEB-INF/freemarker") + ResponseBuilder responseBuilder = new ResponseBuilder(new File(templateLoc)) + + def xmlText = responseBuilder.buildGetMeetingsResponse(mtgs, "success") withFormat { xml { - render(contentType:"text/xml") { - response() { - returncode(RESP_CODE_SUCCESS) - meetings { - for (m in mtgs) { - meeting { - meetingID() { mkp.yield(m.getExternalId()) } - internalMeetingID() { mkp.yield(m.getInternalId()) } - if (m.isBreakout()) { - parentMeetingID() { mkp.yield(m.getParentMeetingId()) } - sequence(m.getSequence()) - } - isBreakout() { mkp.yield(m.isBreakout()) } - meetingName() { mkp.yield(m.getName()) } - createTime(m.getCreateTime()) - createDate(formatPrettyDate(m.getCreateTime())) - voiceBridge() { mkp.yield(m.getTelVoice()) } - dialNumber() { mkp.yield(m.getDialNumber()) } - attendeePW() { mkp.yield(m.getViewerPassword()) } - moderatorPW() { mkp.yield(m.getModeratorPassword()) } - hasBeenForciblyEnded(m.isForciblyEnded() ? "true" : "false") - running(m.isRunning() ? "true" : "false") - participantCount(m.getNumUsers()) - listenerCount(m.getNumListenOnly()) - voiceParticipantCount(m.getNumVoiceJoined()) - videoCount(m.getNumVideos()) - duration(m.duration) - hasUserJoined(m.hasUserJoined()) - } - } - } - } - } + render(text: xmlText, contentType: "text/xml") } } } @@ -1798,6 +1783,9 @@ class ApiController { } return; } + + + def cfg = new Configuration() // Load the XML template diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java index 2d4f22553d..996417b5b4 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java @@ -302,6 +302,19 @@ public class MeetingService implements MessageListener { } private void handleCreateMeeting(Meeting m) { + Map breakoutMetadata = new TreeMap(); + + if (m.isBreakout()){ + breakoutMetadata.put("meetingId", m.getExternalId()); + breakoutMetadata.put("sequence", m.getSequence().toString()); + breakoutMetadata.put("parentMeetingId", m.getParentMeetingId()); + Meeting parent = meetings.get(m.getParentMeetingId()); + parent.addBreakoutRoom(m.getExternalId()); + if (parent.isRecord()) { + messagingService.addBreakoutRoom(parent.getInternalId(), m.getInternalId()); + } + } + if (m.isRecord()) { Map metadata = new TreeMap(); metadata.putAll(m.getMetadata()); @@ -310,12 +323,6 @@ public class MeetingService implements MessageListener { metadata.put("meetingName", m.getName()); metadata.put("isBreakout", m.isBreakout().toString()); - Map breakoutMetadata = new TreeMap(); - breakoutMetadata.put("meetingId", m.getExternalId()); - if (m.isBreakout()){ - breakoutMetadata.put("sequence", m.getSequence().toString()); - breakoutMetadata.put("parentMeetingId", m.getParentMeetingId()); - } messagingService.recordMeetingInfo(m.getInternalId(), metadata, breakoutMetadata); } diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessagingService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessagingService.java index 70974ae0b4..7e57ba4bbc 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessagingService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessagingService.java @@ -28,6 +28,7 @@ import java.util.Set; public interface MessagingService { void recordMeetingInfo(String meetingId, Map info, Map breakoutInfo); + void addBreakoutRoom(String parentId, String breakoutId); void destroyMeeting(String meetingID); void createMeeting(String meetingID, String externalMeetingID, String parentMeetingID, String meetingName, Boolean recorded, diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisMessagingService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisMessagingService.java index 768be2a43f..9909890f26 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisMessagingService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisMessagingService.java @@ -53,6 +53,10 @@ public class RedisMessagingService implements MessagingService { storeService.recordMeetingInfo(meetingId, info, breakoutInfo); } + public void addBreakoutRoom(String parentId, String breakoutId) { + storeService.addBreakoutRoom(parentId, breakoutId); + } + public void destroyMeeting(String meetingID) { DestroyMeetingMessage msg = new DestroyMeetingMessage(meetingID); String json = MessageToJson.destroyMeetingMessageToJson(msg); diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisStorageService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisStorageService.java index f27a34d9b9..f458ed4eaa 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisStorageService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisStorageService.java @@ -54,6 +54,19 @@ public class RedisStorageService { jedis.close(); } } + + public void addBreakoutRoom(String parentId, String breakoutId) { + Jedis jedis = redisPool.getResource(); + try { + + log.debug("Saving breakout room for meeting {}", parentId); + jedis.sadd("meeting:breakout:rooms:" + parentId, breakoutId); + } catch (Exception e) { + log.warn("Cannot record the info meeting:" + parentId, e); + } finally { + jedis.close(); + } + } public void removeMeeting(String meetingId){ Jedis jedis = redisPool.getResource(); diff --git a/bigbluebutton-web/web-app/WEB-INF/freemarker/get-meeting-info.ftl b/bigbluebutton-web/web-app/WEB-INF/freemarker/get-meeting-info.ftl new file mode 100755 index 0000000000..c26d16d0e5 --- /dev/null +++ b/bigbluebutton-web/web-app/WEB-INF/freemarker/get-meeting-info.ftl @@ -0,0 +1,78 @@ +<#-- GET_RECORDINGS FreeMarker XML template --> + + <#-- Where code is a 'SUCCESS' or 'FAILED' String --> + ${returnCode} + ${meeting.getName()} + ${meeting.getExternalId()} + ${meeting.getInternalId()} + ${meeting.getCreateTime()?c} + ${createdOn} + ${meeting.getTelVoice()} + ${meeting.getDialNumber()} + ${meeting.getViewerPassword()} + ${meeting.getModeratorPassword()} + ${meeting.isRunning()?c} + ${meeting.getDuration()} + ${meeting.hasUserJoined()?c} + ${meeting.isRecord()?c} + ${meeting.isForciblyEnded()?c} + ${meeting.getStartTime()?c} + ${meeting.getEndTime()} + ${meeting.getNumUsers()} + ${meeting.getNumListenOnly()} + ${meeting.getNumVoiceJoined()} + ${meeting.getNumVideos()} + ${meeting.getMaxUsers()} + ${meeting.getNumModerators()} + + <#list meeting.getUsers() as att> + + ${att.getInternalUserId()} + ${att.getFullname()} + ${att.getRole()} + ${att.isPresenter()?c} + ${att.isListeningOnly()?c} + ${att.isVoiceJoined()?c} + ${att.hasVideo()?c} + <#if meeting.getUserCustomData(att.getExternalUserId())??> + <#assign ucd = meeting.getUserCustomData(att.getExternalUserId())> + + <#list ucd?keys as prop> + <${prop}> + + + + + + + <#assign m = meeting.getMetadata()> + + <#list m?keys as prop> + <${prop}> + + + + <#if messageKey?has_content> + ${messageKey} + + + <#if message?has_content> + ${message} + + + <#if meeting.isBreakout()> + + ${meeting.getParentMeetingId()} + ${meeting.getSequence()} + + + + <#list meeting.getBreakoutRooms()> + + <#items as room> + ${room} + + + + + \ No newline at end of file diff --git a/bigbluebutton-web/web-app/WEB-INF/freemarker/get-meetings.ftl b/bigbluebutton-web/web-app/WEB-INF/freemarker/get-meetings.ftl new file mode 100755 index 0000000000..fdee481dbd --- /dev/null +++ b/bigbluebutton-web/web-app/WEB-INF/freemarker/get-meetings.ftl @@ -0,0 +1,78 @@ +<#-- GET_RECORDINGS FreeMarker XML template --> + + <#-- Where code is a 'SUCCESS' or 'FAILED' String --> + ${returnCode} + <#list meetingDetailsList> + + <#items as meetingDetail> + <#assign meeting = meetingDetail.getMeeting()> + + ${meeting.getName()} + ${meeting.getExternalId()} + ${meeting.getInternalId()} + ${meeting.getCreateTime()?c} + ${meetingDetail.getCreatedOn()} + ${meeting.getTelVoice()} + ${meeting.getDialNumber()} + ${meeting.getViewerPassword()} + ${meeting.getModeratorPassword()} + ${meeting.isRunning()?c} + ${meeting.getDuration()} + ${meeting.hasUserJoined()?c} + ${meeting.isRecord()?c} + ${meeting.isForciblyEnded()?c} + ${meeting.getStartTime()?c} + ${meeting.getEndTime()} + ${meeting.getNumUsers()} + ${meeting.getNumListenOnly()} + ${meeting.getNumVoiceJoined()} + ${meeting.getNumVideos()} + ${meeting.getMaxUsers()} + ${meeting.getNumModerators()} + + <#list meetingDetail.meeting.getUsers() as att> + + ${att.getInternalUserId()} + ${att.getFullname()} + ${att.getRole()} + ${att.isPresenter()?c} + ${att.isListeningOnly()?c} + ${att.isVoiceJoined()?c} + ${att.hasVideo()?c} + <#if meeting.getUserCustomData(att.getExternalUserId())??> + <#assign ucd = meetingDetail.meeting.getUserCustomData(att.getExternalUserId())> + + <#list ucd?keys as prop> + <${prop}> + + + + + + + <#assign m = meetingDetail.meeting.getMetadata()> + + <#list m?keys as prop> + <${prop}> + + + + <#if meetingDetail.meeting.isBreakout()> + + ${meetingDetail.meeting.getParentMeetingId()} + ${meetingDetail.meeting.getSequence()} + + + + <#list meetingDetail.meeting.getBreakoutRooms()> + + <#items as room> + ${room} + + + + + + + + \ No newline at end of file From 71bcd779b7cba43c43b3b2417a2125d366070a84 Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Thu, 9 Mar 2017 23:01:15 +0000 Subject: [PATCH 02/16] - process breakout rooms in events.xml and metadata.xml --- .../org/bigbluebutton/api/MeetingService.java | 2 +- .../lib/recordandplayback/events_archiver.rb | 19 +++++++++++++++++++ .../scripts/process/presentation.rb | 13 ++++++++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java index 996417b5b4..4cfde4e47a 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java @@ -303,9 +303,9 @@ public class MeetingService implements MessageListener { private void handleCreateMeeting(Meeting m) { Map breakoutMetadata = new TreeMap(); + breakoutMetadata.put("meetingId", m.getExternalId()); if (m.isBreakout()){ - breakoutMetadata.put("meetingId", m.getExternalId()); breakoutMetadata.put("sequence", m.getSequence().toString()); breakoutMetadata.put("parentMeetingId", m.getParentMeetingId()); Meeting parent = meetings.get(m.getParentMeetingId()); diff --git a/record-and-playback/core/lib/recordandplayback/events_archiver.rb b/record-and-playback/core/lib/recordandplayback/events_archiver.rb index f384e1baf6..54a61b70a9 100755 --- a/record-and-playback/core/lib/recordandplayback/events_archiver.rb +++ b/record-and-playback/core/lib/recordandplayback/events_archiver.rb @@ -54,6 +54,10 @@ module BigBlueButton @redis.hgetall("meeting:breakout:#{meeting_id}") end + def breakout_rooms_for(meeting_id) + @redis.smembers("meeting:breakout:rooms:#{meeting_id}") + end + def num_events_for(meeting_id) @redis.llen("meeting:#{meeting_id}:recordings") end @@ -82,6 +86,10 @@ module BigBlueButton @redis.del("meeting:breakout:#{meeting_id}") end + def delete_breakout_rooms_for(meeting_id) + @redis.del("meeting:breakout:rooms:#{meeting_id}") + end + def build_header(message_type) return { "timestamp" => BigBlueButton.monotonic_clock, # @@ -187,12 +195,22 @@ module BigBlueButton meeting_metadata = @redis.metadata_for(meeting_id) breakout_metadata = @redis.breakout_metadata_for(meeting_id) + breakout_rooms = @redis.breakout_rooms_for(meeting_id) version = YAML::load(File.open('../../core/scripts/bigbluebutton.yml'))["bbb_version"] if (meeting_metadata != nil) xml.recording(:meeting_id => meeting_id, :bbb_version => version) { xml.metadata(meeting_metadata) xml.breakout(breakout_metadata) + + if (breakout_rooms != nil) + xml.breakoutRooms() { + breakout_rooms.each do |breakout_room| + xml.breakoutRoom(breakout_room) + end + } + end + msgs = @redis.events_for(meeting_id) msgs.each do |msg| res = @redis.event_info_for(meeting_id, msg) @@ -231,6 +249,7 @@ module BigBlueButton end @redis.delete_metadata_for(meeting_id) @redis.delete_breakout_metadata_for(meeting_id) + @redis.delete_breakout_rooms_for(meeting_id) end def save_events_to_file(directory, result) diff --git a/record-and-playback/presentation/scripts/process/presentation.rb b/record-and-playback/presentation/scripts/process/presentation.rb index 53f68d4ec8..25f5ff878f 100755 --- a/record-and-playback/presentation/scripts/process/presentation.rb +++ b/record-and-playback/presentation/scripts/process/presentation.rb @@ -1,4 +1,4 @@ -# Set encoding to utf-8 + # Set encoding to utf-8 # encoding: UTF-8 # @@ -109,6 +109,17 @@ if not FileTest.directory?(target_dir) end_time = recording.at_xpath("end_time") end_time.content = real_end_time + breakout_xpath = @doc.xpath("//breakout") + breakout_rooms_xpath = @doc.xpath("//breakoutRooms") + + if (breakout_xpath != nil) + recording << breakout_xpath + end + + if (breakout_rooms_xpath != nil) + recording << breakout_rooms_xpath + end + participants = recording.at_xpath("participants") participants.content = BigBlueButton::Events.get_num_participants("#{target_dir}/events.xml") From 90b535026017280111f021ec9c87e7485a6e3820 Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Fri, 10 Mar 2017 19:38:35 +0000 Subject: [PATCH 03/16] - add sample metadata.xml --- .../test/resources/breakout-room-metadata.xml | 26 +++++++++++++++++++ .../meeting-has-breakout-metadata.xml | 22 ++++++++++++++++ .../meeting-no-breakout-metadata.xml | 18 +++++++++++++ 3 files changed, 66 insertions(+) create mode 100755 bbb-common-web/src/test/resources/breakout-room-metadata.xml create mode 100755 bbb-common-web/src/test/resources/meeting-has-breakout-metadata.xml create mode 100755 bbb-common-web/src/test/resources/meeting-no-breakout-metadata.xml diff --git a/bbb-common-web/src/test/resources/breakout-room-metadata.xml b/bbb-common-web/src/test/resources/breakout-room-metadata.xml new file mode 100755 index 0000000000..1ce0b093d5 --- /dev/null +++ b/bbb-common-web/src/test/resources/breakout-room-metadata.xml @@ -0,0 +1,26 @@ + + 32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780 + published + true + 1489173065780 + 1489173199386 + + + f2041d123b6a4b994e7ad87ee9d348496a73472c-1489173065780 + random-2810069 (Room - 2) + true + + + presentation + http://192.168.23.22/playback/presentation/0.9.0/playback.html?meetingId=32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780 + 9841 + 126376 + + + + Welcome tohttp://192.168.23.22/presentation/32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780/presentation/743dd59a958334b4cdcdaa302846d0c0eadcf9ff-1489173070800/thumbnails/thumb-1.png + + + + + diff --git a/bbb-common-web/src/test/resources/meeting-has-breakout-metadata.xml b/bbb-common-web/src/test/resources/meeting-has-breakout-metadata.xml new file mode 100755 index 0000000000..987d637d68 --- /dev/null +++ b/bbb-common-web/src/test/resources/meeting-has-breakout-metadata.xml @@ -0,0 +1,22 @@ + + f3ffe06acedf425565cc024c8ebe89a6552e8782-1489172964374 + published + true + 1489172964374 + 1489173221647 + + 32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780 + 721d83a3907548734d4a505992ebb94ec1454a91-1489173065780 + + + false + random-2810069 + random-2810069 + + + presentation + http://192.168.23.22/playback/presentation/0.9.0/playback.html?meetingId=f3ffe06acedf425565cc024c8ebe89a6552e8782-1489172964374 + 87598 + 226417 + + diff --git a/bbb-common-web/src/test/resources/meeting-no-breakout-metadata.xml b/bbb-common-web/src/test/resources/meeting-no-breakout-metadata.xml new file mode 100755 index 0000000000..c274c26aa7 --- /dev/null +++ b/bbb-common-web/src/test/resources/meeting-no-breakout-metadata.xml @@ -0,0 +1,18 @@ + + f3ffe06acedf425565cc024c8ebe89a6552e8782-1489095153417 + published + true + 1489095153417 + 1489095772224 + + random-2810069 + random-2810069 + false + + + presentation + http://192.168.23.22/playback/presentation/0.9.0/playback.html?meetingId=f3ffe06acedf425565cc024c8ebe89a6552e8782-1489095153417 + 191283 + 545949 + + From 4e59521268f249842808b2ff356d85831a5e92e1 Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Fri, 10 Mar 2017 19:39:20 +0000 Subject: [PATCH 04/16] - store breakout metadata and breakout rooms in events.xml only if they are present. --- .../lib/recordandplayback/events_archiver.rb | 31 +++++++++++++------ .../scripts/process/presentation.rb | 2 ++ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/record-and-playback/core/lib/recordandplayback/events_archiver.rb b/record-and-playback/core/lib/recordandplayback/events_archiver.rb index 54a61b70a9..8f06be2284 100755 --- a/record-and-playback/core/lib/recordandplayback/events_archiver.rb +++ b/record-and-playback/core/lib/recordandplayback/events_archiver.rb @@ -50,10 +50,18 @@ module BigBlueButton @redis.hgetall("meeting:info:#{meeting_id}") end + def has_breakout_metadata_for(meeting_id) + @redis.exists("meeting:breakout:#{meeting_id}") + end + def breakout_metadata_for(meeting_id) @redis.hgetall("meeting:breakout:#{meeting_id}") end + def has_breakout_rooms_for(meeting_id) + @redis.exists("meeting:breakout:rooms:#{meeting_id}") + end + def breakout_rooms_for(meeting_id) @redis.smembers("meeting:breakout:rooms:#{meeting_id}") end @@ -194,21 +202,26 @@ module BigBlueButton result = xml.instruct! :xml, :version => "1.0", :encoding=>"UTF-8" meeting_metadata = @redis.metadata_for(meeting_id) - breakout_metadata = @redis.breakout_metadata_for(meeting_id) - breakout_rooms = @redis.breakout_rooms_for(meeting_id) version = YAML::load(File.open('../../core/scripts/bigbluebutton.yml'))["bbb_version"] if (meeting_metadata != nil) xml.recording(:meeting_id => meeting_id, :bbb_version => version) { xml.metadata(meeting_metadata) - xml.breakout(breakout_metadata) - if (breakout_rooms != nil) - xml.breakoutRooms() { - breakout_rooms.each do |breakout_room| - xml.breakoutRoom(breakout_room) - end - } + if (@redis.has_breakout_metadata_for(meeting_id)) + breakout_metadata = @redis.breakout_metadata_for(meeting_id) + xml.breakout(breakout_metadata) + end + + if (@redis.has_breakout_rooms_for(meeting_id)) + breakout_rooms = @redis.breakout_rooms_for(meeting_id) + if (breakout_rooms != nil) + xml.breakoutRooms() { + breakout_rooms.each do |breakout_room| + xml.breakoutRoom(breakout_room) + end + } + end end msgs = @redis.events_for(meeting_id) diff --git a/record-and-playback/presentation/scripts/process/presentation.rb b/record-and-playback/presentation/scripts/process/presentation.rb index 25f5ff878f..ac4f787b25 100755 --- a/record-and-playback/presentation/scripts/process/presentation.rb +++ b/record-and-playback/presentation/scripts/process/presentation.rb @@ -109,6 +109,8 @@ if not FileTest.directory?(target_dir) end_time = recording.at_xpath("end_time") end_time.content = real_end_time + ## Copy the breakout and breakout rooms node from + ## events.xml if present. breakout_xpath = @doc.xpath("//breakout") breakout_rooms_xpath = @doc.xpath("//breakoutRooms") From 6f6ca61077d732333d4a8c2b90429eca1882f6ed Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Fri, 10 Mar 2017 19:40:33 +0000 Subject: [PATCH 05/16] - store breakout metadata and breakout rooms into redis --- .../org/bigbluebutton/api/MeetingService.java | 15 +++++++----- .../api/messaging/MessagingService.java | 3 ++- .../api/messaging/RedisMessagingService.java | 8 +++++-- .../api/messaging/RedisStorageService.java | 23 +++++++++++-------- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java index 4cfde4e47a..f1ee969752 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java @@ -302,12 +302,7 @@ public class MeetingService implements MessageListener { } private void handleCreateMeeting(Meeting m) { - Map breakoutMetadata = new TreeMap(); - breakoutMetadata.put("meetingId", m.getExternalId()); - if (m.isBreakout()){ - breakoutMetadata.put("sequence", m.getSequence().toString()); - breakoutMetadata.put("parentMeetingId", m.getParentMeetingId()); Meeting parent = meetings.get(m.getParentMeetingId()); parent.addBreakoutRoom(m.getExternalId()); if (parent.isRecord()) { @@ -323,7 +318,15 @@ public class MeetingService implements MessageListener { metadata.put("meetingName", m.getName()); metadata.put("isBreakout", m.isBreakout().toString()); - messagingService.recordMeetingInfo(m.getInternalId(), metadata, breakoutMetadata); + messagingService.recordMeetingInfo(m.getInternalId(), metadata); + + if (m.isBreakout()) { + Map breakoutMetadata = new TreeMap(); + breakoutMetadata.put("meetingId", m.getExternalId()); + breakoutMetadata.put("sequence", m.getSequence().toString()); + breakoutMetadata.put("parentMeetingId", m.getParentMeetingId()); + messagingService.recordBreakoutInfo(m.getInternalId(), breakoutMetadata); + } } Map logData = new HashMap(); diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessagingService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessagingService.java index 7e57ba4bbc..3ccd54a659 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessagingService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/MessagingService.java @@ -27,7 +27,8 @@ import java.util.Map; import java.util.Set; public interface MessagingService { - void recordMeetingInfo(String meetingId, Map info, Map breakoutInfo); + void recordMeetingInfo(String meetingId, Map info); + void recordBreakoutInfo(String meetingId, Map breakoutInfo); void addBreakoutRoom(String parentId, String breakoutId); void destroyMeeting(String meetingID); void createMeeting(String meetingID, String externalMeetingID, diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisMessagingService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisMessagingService.java index 9909890f26..38abbf4171 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisMessagingService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisMessagingService.java @@ -49,8 +49,12 @@ public class RedisMessagingService implements MessagingService { private MessageSender sender; private ToJsonEncoder encoder = new ToJsonEncoder(); - public void recordMeetingInfo(String meetingId, Map info, Map breakoutInfo) { - storeService.recordMeetingInfo(meetingId, info, breakoutInfo); + public void recordMeetingInfo(String meetingId, Map info) { + storeService.recordMeetingInfo(meetingId, info); + } + + public void recordBreakoutInfo(String meetingId, Map breakoutInfo) { + storeService.recordBreakoutInfo(meetingId, breakoutInfo); } public void addBreakoutRoom(String parentId, String breakoutId) { diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisStorageService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisStorageService.java index f458ed4eaa..ac456a57b7 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisStorageService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/messaging/RedisStorageService.java @@ -30,8 +30,7 @@ public class RedisStorageService { Protocol.DEFAULT_DATABASE, "BbbRed5AppsPub"); } - public void recordMeetingInfo(String meetingId, Map info, - Map breakoutInfo) { + public void recordMeetingInfo(String meetingId, Map info) { Jedis jedis = redisPool.getResource(); try { for (String key : info.keySet()) { @@ -40,14 +39,6 @@ public class RedisStorageService { log.debug("Saving metadata in {}", meetingId); jedis.hmset("meeting:info:" + meetingId, info); - - for (String breakoutKey : breakoutInfo.keySet()) { - log.debug("Storing breakout metadata {} = {}", breakoutKey, - breakoutInfo.get(breakoutKey)); - } - - log.debug("Saving breakout metadata in {}", meetingId); - jedis.hmset("meeting:breakout:" + meetingId, breakoutInfo); } catch (Exception e) { log.warn("Cannot record the info meeting:" + meetingId, e); } finally { @@ -55,6 +46,18 @@ public class RedisStorageService { } } + public void recordBreakoutInfo(String meetingId, Map breakoutInfo) { + Jedis jedis = redisPool.getResource(); + try { + log.debug("Saving breakout metadata in {}", meetingId); + jedis.hmset("meeting:breakout:" + meetingId, breakoutInfo); + } catch (Exception e) { + log.warn("Cannot record the info meeting:" + meetingId, e); + } finally { + jedis.close(); + } + } + public void addBreakoutRoom(String parentId, String breakoutId) { Jedis jedis = redisPool.getResource(); try { From 80a0c9f386b55bfa8626ad43ac6dd952051b5b1e Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Fri, 10 Mar 2017 22:01:56 +0000 Subject: [PATCH 06/16] - start implementing recording metadata reader --- bbb-common-web/build.sbt | 19 +++++++++++++++++++ bbb-common-web/project/plugins.sbt | 1 + .../api/domain/RecordingMetadata.java | 7 +++++++ .../domain/RecordingMetadataMeetingMeta.scala | 8 ++++++++ .../api/domain/RecordingMetadataPlayback.java | 7 +++++++ .../util/RecordingMetadataReaderHelper.java | 8 ++++++++ .../RecordingMetadataReaderHelperTest.scala | 12 ++++++++++++ 7 files changed, 62 insertions(+) create mode 100755 bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadata.java create mode 100755 bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataMeetingMeta.scala create mode 100755 bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataPlayback.java create mode 100755 bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java create mode 100755 bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingMetadataReaderHelperTest.scala diff --git a/bbb-common-web/build.sbt b/bbb-common-web/build.sbt index 1e694fb23f..019f86ccec 100755 --- a/bbb-common-web/build.sbt +++ b/bbb-common-web/build.sbt @@ -4,6 +4,18 @@ organization := "org.bigbluebutton" version := "0.0.1-SNAPSHOT" +scalaVersion := "2.11.7" + +scalacOptions ++= Seq( + "-unchecked", + "-deprecation", + "-Xlint", + "-Ywarn-dead-code", + "-language:_", + "-target:jvm-1.8", + "-encoding", "UTF-8" +) + // We want to have our jar files in lib_managed dir. // This way we'll have the right path when we import // into eclipse. @@ -13,8 +25,15 @@ testOptions in Test += Tests.Argument(TestFrameworks.Specs2, "html", "console", testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-h", "target/scalatest-reports") +val scalaV = "2.11.7" + +libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaV +libraryDependencies += "org.scala-lang" % "scala-library" % scalaV +libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaV + libraryDependencies += "commons-lang" % "commons-lang" % "2.5" libraryDependencies += "org.freemarker" % "freemarker" % "2.3.23" +libraryDependencies += "com.fasterxml.jackson.dataformat" % "jackson-dataformat-xml" % "2.6.3" libraryDependencies += "org.pegdown" % "pegdown" % "1.4.0" % "test" libraryDependencies += "junit" % "junit" % "4.12" % "test" diff --git a/bbb-common-web/project/plugins.sbt b/bbb-common-web/project/plugins.sbt index d33e342247..30bb577b38 100755 --- a/bbb-common-web/project/plugins.sbt +++ b/bbb-common-web/project/plugins.sbt @@ -4,5 +4,6 @@ addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.2.0") addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") +addSbtPlugin("com.artima.supersafe" % "sbtplugin" % "1.1.0") diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadata.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadata.java new file mode 100755 index 0000000000..85e8ce8fdc --- /dev/null +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadata.java @@ -0,0 +1,7 @@ +package org.bigbluebutton.api.domain; + +/** + * Created by ralam on 3/10/2017. + */ +public class RecordingMetadata { +} diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataMeetingMeta.scala b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataMeetingMeta.scala new file mode 100755 index 0000000000..d9a96d715a --- /dev/null +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataMeetingMeta.scala @@ -0,0 +1,8 @@ +package org.bigbluebutton.api.domain + +/** + * Created by ralam on 3/10/2017. + */ +class RecordingMetadataMeetingMeta { + +} diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataPlayback.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataPlayback.java new file mode 100755 index 0000000000..821f30c06f --- /dev/null +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataPlayback.java @@ -0,0 +1,7 @@ +package org.bigbluebutton.api.domain; + +/** + * Created by ralam on 3/10/2017. + */ +public class RecordingMetadataPlayback { +} diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java new file mode 100755 index 0000000000..ff34e46156 --- /dev/null +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java @@ -0,0 +1,8 @@ +package org.bigbluebutton.api.util; + +/** + * Created by ralam on 3/10/2017. + */ +public class RecordingMetadataReaderHelper { + +} diff --git a/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingMetadataReaderHelperTest.scala b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingMetadataReaderHelperTest.scala new file mode 100755 index 0000000000..dc441bc410 --- /dev/null +++ b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingMetadataReaderHelperTest.scala @@ -0,0 +1,12 @@ +package org.bigbluebutton.api.util + +/** + * Created by ralam on 3/10/2017. + */ +class RecordingMetadataReaderHelperTest extends UnitSpec { + + it should "find template" in { + + } + +} From f45acfaf53bd4aff06a938ad2e5d43dd0191cc60 Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Sat, 11 Mar 2017 15:04:01 +0000 Subject: [PATCH 07/16] - deserialize playback from metadata.xml --- .../api/domain/RecordingMetadataPlayback.java | 46 +++++++++++++++++-- .../util/RecordingMetadataReaderHelper.java | 19 ++++++-- .../src/test/resources/playback-metadata.xml | 6 +++ .../RecordingMetadataReaderHelperTest.scala | 22 +++++++++ 4 files changed, 87 insertions(+), 6 deletions(-) create mode 100755 bbb-common-web/src/test/resources/playback-metadata.xml diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataPlayback.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataPlayback.java index 821f30c06f..7e85de01c4 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataPlayback.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataPlayback.java @@ -1,7 +1,47 @@ package org.bigbluebutton.api.domain; -/** - * Created by ralam on 3/10/2017. - */ +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; + +@JacksonXmlRootElement(localName = "playback") public class RecordingMetadataPlayback { + private String format; + private String link; + + @JacksonXmlProperty(localName = "processing_time") + private Long processingTime; + + private Long duration; + + public void setFormat(String format) { + this.format = format; + } + + public String getFormat() { + return format; + } + + public void setLink(String link) { + this.link = link; + } + + public String getLink() { + return link; + } + + public void setProcessingTime(Long processingTime) { + this.processingTime = processingTime; + } + + public Long getProcessingTime() { + return processingTime; + } + + public void setDuration(Long duration) { + this.duration = duration; + } + + public Long getDuration() { + return duration; + } } diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java index ff34e46156..f888d35853 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java @@ -1,8 +1,21 @@ package org.bigbluebutton.api.util; -/** - * Created by ralam on 3/10/2017. - */ + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + public class RecordingMetadataReaderHelper { + public static String inputStreamToString(InputStream is) throws IOException { + StringBuilder sb = new StringBuilder(); + String line; + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + while ((line = br.readLine()) != null) { + sb.append(line); + } + br.close(); + return sb.toString(); + } } diff --git a/bbb-common-web/src/test/resources/playback-metadata.xml b/bbb-common-web/src/test/resources/playback-metadata.xml new file mode 100755 index 0000000000..2da30328ea --- /dev/null +++ b/bbb-common-web/src/test/resources/playback-metadata.xml @@ -0,0 +1,6 @@ + + presentation + http://192.168.23.22/playback/presentation/0.9.0/playback.html?meetingId=f3ffe06acedf425565cc024c8ebe89a6552e8782-1489095153417 + 191283 + 545949 + diff --git a/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingMetadataReaderHelperTest.scala b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingMetadataReaderHelperTest.scala index dc441bc410..859876a169 100755 --- a/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingMetadataReaderHelperTest.scala +++ b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingMetadataReaderHelperTest.scala @@ -1,12 +1,34 @@ package org.bigbluebutton.api.util +import java.io.{File, FileInputStream} +import javax.xml.stream.{XMLInputFactory, XMLStreamReader} + +import com.fasterxml.jackson.dataformat.xml.{JacksonXmlModule, XmlMapper} +import org.bigbluebutton.api.domain.RecordingMetadataPlayback + /** * Created by ralam on 3/10/2017. */ class RecordingMetadataReaderHelperTest extends UnitSpec { it should "find template" in { + val factory: XMLInputFactory = XMLInputFactory.newInstance(); + val templateLoc = new File("src/test/resources/playback-metadata.xml") + val module: JacksonXmlModule = new JacksonXmlModule(); + // and then configure, for example: + module.setDefaultUseWrapper(false); + + val mapper: XmlMapper = new XmlMapper(module) + + //Reading from xml file and creating XMLStreamReader + val reader: XMLStreamReader = factory.createXMLStreamReader(new FileInputStream(templateLoc)) + + val openCredentials: RecordingMetadataPlayback = mapper.readValue(reader, classOf[RecordingMetadataPlayback]) + + println(openCredentials) + + assert(openCredentials.getDuration == 545949) } } From 73b16db384f882325238796906db2b0854f56338 Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Sat, 11 Mar 2017 09:35:06 -0800 Subject: [PATCH 08/16] - deserialize metadata.xml --- bbb-common-web/build.sbt | 2 + .../bigbluebutton/api/domain/Breakout.java | 21 ++++ .../api/domain/BreakoutRoom.java | 18 +++ .../bigbluebutton/api/domain/Extensions.java | 21 ++++ .../org/bigbluebutton/api/domain/Image.java | 59 +++++++++ .../bigbluebutton/api/domain/Metadata.java | 23 ++++ .../org/bigbluebutton/api/domain/Preview.java | 18 +++ .../api/domain/RecordingMetadata.java | 119 +++++++++++++++++- .../domain/RecordingMetadataMeetingMeta.scala | 8 -- .../api/domain/RecordingMetadataPlayback.java | 10 ++ .../test/resources/breakout-room-metadata.xml | 4 + .../src/test/resources/playback-metadata.xml | 1 + .../RecordingMetadataReaderHelperTest.scala | 36 ++++-- .../api/util/ResponseBuilderTest.scala | 4 +- 14 files changed, 324 insertions(+), 20 deletions(-) create mode 100755 bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Breakout.java create mode 100755 bbb-common-web/src/main/java/org/bigbluebutton/api/domain/BreakoutRoom.java create mode 100755 bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Extensions.java create mode 100755 bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Image.java create mode 100755 bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Metadata.java create mode 100755 bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Preview.java delete mode 100755 bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataMeetingMeta.scala diff --git a/bbb-common-web/build.sbt b/bbb-common-web/build.sbt index 019f86ccec..ce48e9aad9 100755 --- a/bbb-common-web/build.sbt +++ b/bbb-common-web/build.sbt @@ -34,6 +34,8 @@ libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaV libraryDependencies += "commons-lang" % "commons-lang" % "2.5" libraryDependencies += "org.freemarker" % "freemarker" % "2.3.23" libraryDependencies += "com.fasterxml.jackson.dataformat" % "jackson-dataformat-xml" % "2.6.3" +// https://mvnrepository.com/artifact/org.codehaus.woodstox/woodstox-core-asl +libraryDependencies += "org.codehaus.woodstox" % "woodstox-core-asl" % "4.4.1" libraryDependencies += "org.pegdown" % "pegdown" % "1.4.0" % "test" libraryDependencies += "junit" % "junit" % "4.12" % "test" diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Breakout.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Breakout.java new file mode 100755 index 0000000000..4a464f516c --- /dev/null +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Breakout.java @@ -0,0 +1,21 @@ +package org.bigbluebutton.api.domain; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText; + + +public class Breakout { + /** + * + */ + + @JacksonXmlProperty(isAttribute = true) + private String parentMeetingId; + + @JacksonXmlProperty(isAttribute = true) + private int sequence; + + @JacksonXmlProperty(isAttribute = true) + private String meetingId; + +} diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/BreakoutRoom.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/BreakoutRoom.java new file mode 100755 index 0000000000..4b8cf0adb3 --- /dev/null +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/BreakoutRoom.java @@ -0,0 +1,18 @@ +package org.bigbluebutton.api.domain; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText; + + +public class BreakoutRoom { + + @JacksonXmlText + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Extensions.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Extensions.java new file mode 100755 index 0000000000..1632465062 --- /dev/null +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Extensions.java @@ -0,0 +1,21 @@ +package org.bigbluebutton.api.domain; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; + +/** + * Created by ritz on 2017-03-11. + */ +public class Extensions { + @JacksonXmlProperty(localName = "preview") + @JacksonXmlElementWrapper(useWrapping = false) + private Preview preview; + + public void setPreview(Preview preview) { + this.preview = preview; + } + + public Preview getPreview() { + return preview; + } +} diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Image.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Image.java new file mode 100755 index 0000000000..678669ee08 --- /dev/null +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Image.java @@ -0,0 +1,59 @@ +package org.bigbluebutton.api.domain; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText; + +public class Image { + + /** + * Welcome to + * http://192.168.23.22/presentation/32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780/presentation/743dd59a958334b4cdcdaa302846d0c0eadcf9ff-1489173070800/thumbnails/thumb-1.png + * + */ + + @JacksonXmlProperty(isAttribute = true) + private String width; + + @JacksonXmlProperty(isAttribute = true) + private String height; + + @JacksonXmlProperty(isAttribute = true) + private String alt; + + @JacksonXmlText + private String value; + + + public void setWidth(String width) { + this.width = width; + } + + public String getWidth() { + return width; + } + + public void setHeight(String height) { + this.height = height; + } + + public String getHeight() { + return height; + } + + public void setAlt(String alt) { + this.alt = alt; + } + + public String getAlt() { + return alt; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + +} diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Metadata.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Metadata.java new file mode 100755 index 0000000000..5e23b1cd43 --- /dev/null +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Metadata.java @@ -0,0 +1,23 @@ +package org.bigbluebutton.api.domain; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; + +import java.util.Map; +import java.util.TreeMap; + +@JacksonXmlRootElement(localName = "meta") +public class Metadata { + private Map map = new TreeMap(); + + @JsonAnyGetter + public Map get() { + return map; + } + + @JsonAnySetter + public void set(String name, String value) { + map.put(name, value); + } +} diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Preview.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Preview.java new file mode 100755 index 0000000000..8ee697b91e --- /dev/null +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Preview.java @@ -0,0 +1,18 @@ +package org.bigbluebutton.api.domain; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; + +public class Preview { + @JacksonXmlElementWrapper(localName = "images") + @JacksonXmlProperty(localName = "image") + private Image[] images; + + // public void setImages(Image[] images) { + // this.images = images; + // } + + // public Image[] getImages() { + // return images; + // } +} diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadata.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadata.java index 85e8ce8fdc..8d86aa5fd0 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadata.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadata.java @@ -1,7 +1,120 @@ package org.bigbluebutton.api.domain; -/** - * Created by ralam on 3/10/2017. - */ +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; + +import java.util.Map; +import java.util.TreeMap; + +@JacksonXmlRootElement(localName = "recording") public class RecordingMetadata { + /** + * + 32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780 + published + true + 1489173065780 + 1489173199386 + + + f2041d123b6a4b994e7ad87ee9d348496a73472c-1489173065780 + random-2810069 (Room - 2) + true + + + presentation + http://192.168.23.22/playback/presentation/0.9.0/playback.html?meetingId=32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780 + 9841 + 126376 + + + + Welcome tohttp://192.168.23.22/presentation/32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780/presentation/743dd59a958334b4cdcdaa302846d0c0eadcf9ff-1489173070800/thumbnails/thumb-1.png + + + + + + */ + + private String id; + private String state; + private boolean published; + + @JacksonXmlProperty(localName = "start_time") + private String startTime; + + @JacksonXmlProperty(localName = "end_time") + private String endTime; + + private Breakout breakout; + + @JacksonXmlElementWrapper(localName = "breakoutRooms") + @JacksonXmlProperty(localName = "breakoutRoom") + private BreakoutRoom[] breakoutRooms; + + private Metadata meta; + + private RecordingMetadataPlayback playback; + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setState(String state) { + this.state = state; + } + + public String getState() { + return state; + } + + public void setPublished(boolean published) { + this.published = published; + } + + public boolean getPublished() { + return published; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getStartTime() { + return startTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public String getEndTime() { + return endTime; + } + + public void setBreakout(Breakout breakout) { + this.breakout = breakout; + } + + public Breakout getBreakout() { + return breakout; + } + + public void setMeta(Metadata meta) { + this.meta = meta; + } + + public Metadata getMeta() { + return meta; + } + + public RecordingMetadataPlayback getPlayback() { + return playback; + } } diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataMeetingMeta.scala b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataMeetingMeta.scala deleted file mode 100755 index d9a96d715a..0000000000 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataMeetingMeta.scala +++ /dev/null @@ -1,8 +0,0 @@ -package org.bigbluebutton.api.domain - -/** - * Created by ralam on 3/10/2017. - */ -class RecordingMetadataMeetingMeta { - -} diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataPlayback.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataPlayback.java index 7e85de01c4..aaecd85727 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataPlayback.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadataPlayback.java @@ -13,6 +13,8 @@ public class RecordingMetadataPlayback { private Long duration; + private Extensions extensions; + public void setFormat(String format) { this.format = format; } @@ -44,4 +46,12 @@ public class RecordingMetadataPlayback { public Long getDuration() { return duration; } + + public void setExtensions(Extensions extensions) { + this.extensions = extensions; + } + + public Extensions getExtensions() { + return extensions; + } } diff --git a/bbb-common-web/src/test/resources/breakout-room-metadata.xml b/bbb-common-web/src/test/resources/breakout-room-metadata.xml index 1ce0b093d5..c3be8c7142 100755 --- a/bbb-common-web/src/test/resources/breakout-room-metadata.xml +++ b/bbb-common-web/src/test/resources/breakout-room-metadata.xml @@ -5,6 +5,10 @@ 1489173065780 1489173199386 + + 32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780 + 721d83a3907548734d4a505992ebb94ec1454a91-1489173065780 + f2041d123b6a4b994e7ad87ee9d348496a73472c-1489173065780 random-2810069 (Room - 2) diff --git a/bbb-common-web/src/test/resources/playback-metadata.xml b/bbb-common-web/src/test/resources/playback-metadata.xml index 2da30328ea..0474ed798d 100755 --- a/bbb-common-web/src/test/resources/playback-metadata.xml +++ b/bbb-common-web/src/test/resources/playback-metadata.xml @@ -3,4 +3,5 @@ http://192.168.23.22/playback/presentation/0.9.0/playback.html?meetingId=f3ffe06acedf425565cc024c8ebe89a6552e8782-1489095153417 191283 545949 + diff --git a/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingMetadataReaderHelperTest.scala b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingMetadataReaderHelperTest.scala index 859876a169..8dc4f13c07 100755 --- a/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingMetadataReaderHelperTest.scala +++ b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingMetadataReaderHelperTest.scala @@ -4,17 +4,17 @@ import java.io.{File, FileInputStream} import javax.xml.stream.{XMLInputFactory, XMLStreamReader} import com.fasterxml.jackson.dataformat.xml.{JacksonXmlModule, XmlMapper} -import org.bigbluebutton.api.domain.RecordingMetadataPlayback +import org.bigbluebutton.api.domain.{RecordingMetadata, RecordingMetadataPlayback} /** * Created by ralam on 3/10/2017. */ class RecordingMetadataReaderHelperTest extends UnitSpec { - it should "find template" in { + it should "deserialize playback part of metadata.xml" in { val factory: XMLInputFactory = XMLInputFactory.newInstance(); - val templateLoc = new File("src/test/resources/playback-metadata.xml") + val xml = new File("src/test/resources/playback-metadata.xml") val module: JacksonXmlModule = new JacksonXmlModule(); // and then configure, for example: module.setDefaultUseWrapper(false); @@ -22,13 +22,35 @@ class RecordingMetadataReaderHelperTest extends UnitSpec { val mapper: XmlMapper = new XmlMapper(module) //Reading from xml file and creating XMLStreamReader - val reader: XMLStreamReader = factory.createXMLStreamReader(new FileInputStream(templateLoc)) + val reader: XMLStreamReader = factory.createXMLStreamReader(new FileInputStream(xml)) - val openCredentials: RecordingMetadataPlayback = mapper.readValue(reader, classOf[RecordingMetadataPlayback]) + val playback: RecordingMetadataPlayback = mapper.readValue(reader, classOf[RecordingMetadataPlayback]) - println(openCredentials) + println("***** FOOO =" + mapper.writeValueAsString(playback)) + + assert(playback.getDuration == 545949) + + } + + it should "deserialize metadata.xml" in { + val factory: XMLInputFactory = XMLInputFactory.newInstance(); + + val xml = new File("src/test/resources/breakout-room-metadata.xml") + val module: JacksonXmlModule = new JacksonXmlModule(); + // and then configure, for example: + module.setDefaultUseWrapper(false); + + val mapper: XmlMapper = new XmlMapper(module) + + //Reading from xml file and creating XMLStreamReader + val reader: XMLStreamReader = factory.createXMLStreamReader(new FileInputStream(xml)) + + val recMeta: RecordingMetadata = mapper.readValue(reader, classOf[RecordingMetadata]) + + println("***** FOOO =" + mapper.writeValueAsString(recMeta)) + + assert(recMeta.getPlayback.getDuration == 126376) - assert(openCredentials.getDuration == 545949) } } diff --git a/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/ResponseBuilderTest.scala b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/ResponseBuilderTest.scala index b8a786f4ae..996936ab67 100755 --- a/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/ResponseBuilderTest.scala +++ b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/ResponseBuilderTest.scala @@ -53,7 +53,7 @@ class ResponseBuilderTest extends UnitSpec { val templateLoc = new File("src/test/resources") val builder = new ResponseBuilder(templateLoc) def response = builder.buildGetMeetingInfoResponse(meeting, "success") - println(response) +// println(response) assert(templateLoc.exists()) } @@ -178,7 +178,7 @@ class ResponseBuilderTest extends UnitSpec { val templateLoc = new File("src/test/resources") val builder = new ResponseBuilder(templateLoc) def response = builder.buildGetMeetingsResponse(meetings, "success") - println(response) +// println(response) assert(templateLoc.exists()) } From 72c42f9d701381a379d26c2409dd1807317261e8 Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Mon, 13 Mar 2017 21:43:39 +0000 Subject: [PATCH 09/16] - build get recordings api response --- .../bigbluebutton/api/domain/Breakout.java | 23 +++++ .../bigbluebutton/api/domain/MeetingInfo.java | 53 +++++++++++ .../org/bigbluebutton/api/domain/Preview.java | 12 +-- .../api/domain/RecordingMetadata.java | 30 +++++++ .../util/RecordingMetadataReaderHelper.java | 38 +++++++- .../api/util/ResponseBuilder.java | 27 ++++++ .../test/resources/breakout-room-metadata.xml | 2 + .../src/test/resources/get-recordings.ftl | 68 ++++++++++++++ .../api/util/ResponseBuilderTest.scala | 16 +++- .../web/controllers/ApiController.groovy | 26 +++--- .../org/bigbluebutton/api/MeetingService.java | 25 ++++-- .../bigbluebutton/api/RecordingService.java | 73 +++++++++++++++ .../WEB-INF/freemarker/get-recordings.ftl | 90 ++++++++++--------- .../lib/recordandplayback/events_archiver.rb | 3 + .../scripts/process/presentation.rb | 5 ++ 15 files changed, 415 insertions(+), 76 deletions(-) create mode 100755 bbb-common-web/src/main/java/org/bigbluebutton/api/domain/MeetingInfo.java create mode 100755 bbb-common-web/src/test/resources/get-recordings.ftl mode change 100644 => 100755 bigbluebutton-web/web-app/WEB-INF/freemarker/get-recordings.ftl diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Breakout.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Breakout.java index 4a464f516c..f5106335d4 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Breakout.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Breakout.java @@ -18,4 +18,27 @@ public class Breakout { @JacksonXmlProperty(isAttribute = true) private String meetingId; + public void setParentMeetingId(String parentMeetingId) { + this.parentMeetingId = parentMeetingId; + } + + public String getParentMeetingId() { + return parentMeetingId; + } + + public void setSequence(int sequence) { + this.sequence = sequence; + } + + public int getSequence() { + return sequence; + } + + public void setMeetingId(String meetingId) { + this.meetingId = meetingId; + } + + public String getMeetingId() { + return meetingId; + } } diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/MeetingInfo.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/MeetingInfo.java new file mode 100755 index 0000000000..a2c14dd634 --- /dev/null +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/MeetingInfo.java @@ -0,0 +1,53 @@ +package org.bigbluebutton.api.domain; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; + +public class MeetingInfo { + /** + * + */ + @JacksonXmlProperty(isAttribute = true) + private String id; + + @JacksonXmlProperty(isAttribute = true) + private String externalId; + + @JacksonXmlProperty(isAttribute = true) + private String name; + + @JacksonXmlProperty(isAttribute = true) + private boolean breakout; + + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setExternalId(String externalId) { + this.externalId = externalId; + } + + public String getExternalId() { + return externalId; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setBreakout(boolean breakout) { + this.breakout = breakout; + } + + public boolean isBreakout() { + return breakout; + } +} diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Preview.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Preview.java index 8ee697b91e..ec19d53cb2 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Preview.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Preview.java @@ -8,11 +8,11 @@ public class Preview { @JacksonXmlProperty(localName = "image") private Image[] images; - // public void setImages(Image[] images) { - // this.images = images; - // } + public void setImages(Image[] images) { + this.images = images; + } - // public Image[] getImages() { - // return images; - // } + public Image[] getImages() { + return images; + } } diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadata.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadata.java index 8d86aa5fd0..47a3f6a553 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadata.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/RecordingMetadata.java @@ -48,6 +48,12 @@ public class RecordingMetadata { @JacksonXmlProperty(localName = "end_time") private String endTime; + @JacksonXmlProperty(localName = "participants") + private int participants; + + @JacksonXmlProperty(localName = "meeting") + private MeetingInfo meetingInfo; + private Breakout breakout; @JacksonXmlElementWrapper(localName = "breakoutRooms") @@ -98,6 +104,22 @@ public class RecordingMetadata { return endTime; } + public void setParticipants(int participants) { + this.participants = participants; + } + + public int getParticipants() { + return participants; + } + + public void setMeeting(MeetingInfo meetingInfo) { + this.meetingInfo = meetingInfo; + } + + public MeetingInfo getMeeting() { + return meetingInfo; + } + public void setBreakout(Breakout breakout) { this.breakout = breakout; } @@ -106,6 +128,14 @@ public class RecordingMetadata { return breakout; } + public void setBreakoutRooms(BreakoutRoom[] breakoutRooms) { + this.breakoutRooms = breakoutRooms; + } + + public BreakoutRoom[] getBreakoutRooms() { + return breakoutRooms; + } + public void setMeta(Metadata meta) { this.meta = meta; } diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java index f888d35853..8b1d942735 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java @@ -1,10 +1,14 @@ package org.bigbluebutton.api.util; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; +import com.fasterxml.jackson.dataformat.xml.JacksonXmlModule; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import org.bigbluebutton.api.domain.RecordingMetadata; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import java.io.*; public class RecordingMetadataReaderHelper { @@ -18,4 +22,30 @@ public class RecordingMetadataReaderHelper { br.close(); return sb.toString(); } + + public static RecordingMetadata getRecordingMetadata(File metadataXml) { + XMLInputFactory factory = XMLInputFactory.newInstance(); + + JacksonXmlModule module = new JacksonXmlModule(); + // and then configure, for example: + module.setDefaultUseWrapper(false); + + XmlMapper mapper = new XmlMapper(module); + + //Reading from xml file and creating XMLStreamReader + XMLStreamReader reader = null; + RecordingMetadata recMeta = null; + try { + reader = factory.createXMLStreamReader(new FileInputStream(metadataXml)); + recMeta = mapper.readValue(reader, RecordingMetadata.class); + } catch (XMLStreamException e) { + e.printStackTrace(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + return recMeta; + } } diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ResponseBuilder.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ResponseBuilder.java index 86685d86fd..37bb21c31a 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ResponseBuilder.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ResponseBuilder.java @@ -9,6 +9,7 @@ import java.io.StringWriter; import java.util.*; import freemarker.template.*; +import org.bigbluebutton.api.domain.RecordingMetadata; public class ResponseBuilder { Configuration cfg = new Configuration(Configuration.VERSION_2_3_23); @@ -94,4 +95,30 @@ public class ResponseBuilder { return xmlText.toString(); } + + public String buildGetRecordingsResponse(List recordings, String returnCode) { + + Template ftl = null; + try { + ftl = cfg.getTemplate("get-recordings.ftl"); + } catch (IOException e) { + e.printStackTrace(); + } + + StringWriter xmlText = new StringWriter(); + + Map root = new HashMap(); + root.put("returnCode", returnCode); + root.put("recordings", recordings); + + try { + ftl.process(root, xmlText); + } catch (TemplateException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + return xmlText.toString(); + } } diff --git a/bbb-common-web/src/test/resources/breakout-room-metadata.xml b/bbb-common-web/src/test/resources/breakout-room-metadata.xml index c3be8c7142..02afb1f41c 100755 --- a/bbb-common-web/src/test/resources/breakout-room-metadata.xml +++ b/bbb-common-web/src/test/resources/breakout-room-metadata.xml @@ -4,6 +4,8 @@ true 1489173065780 1489173199386 + 10 + 32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780 diff --git a/bbb-common-web/src/test/resources/get-recordings.ftl b/bbb-common-web/src/test/resources/get-recordings.ftl new file mode 100755 index 0000000000..33d908790b --- /dev/null +++ b/bbb-common-web/src/test/resources/get-recordings.ftl @@ -0,0 +1,68 @@ +<#-- GET_RECORDINGS FreeMarker XML template --> +<#compress> + + <#-- Where code is a 'SUCCESS' or 'FAILED' String --> + ${returnCode} + + <#list recordings as r> + + ${r.getId()} + ${r.getMeeting().getId()?html} + ${r.getMeeting().getExternalId()?html} + + ${r.getMeeting().isBreakout()?c} + ${r.getPublished()?string} + ${r.getState()?string} + <#if r.getStartTime()?? && r.getStartTime() != "">${r.getStartTime()} + <#if r.getEndTime()?? && r.getEndTime() != "">${r.getEndTime()} + <#if r.getParticipants()??>${r.getParticipants()} + <#if r.getBreakout()??> + <#assign breakout = r.getBreakout()> + + ${breakout.getParentMeetingId()} + ${breakout.getSequence()?c} + + + <#if r.getBreakoutRooms()??> + <#list r.getBreakoutRooms()> + + <#items as broom> + ${broom.getValue()} + + + + + <#assign m = r.getMeta().get()> + + <#list m?keys as prop> + <${prop}> + + + <#assign pb = r.getPlayback()> + + ${pb.getFormat()} + ${pb.getLink()} + ${pb.getProcessingTime()?c} + ${pb.getDuration()?c} + <#if pb.getExtensions()??> + + <#if pb.getExtensions().getPreview()??> + <#assign prev = pb.getExtensions().getPreview()> + + <#list prev.getImages()> + + <#items as image> + ${image.getAlt()}${image.getValue()} + + + + + + + + + + + + + \ No newline at end of file diff --git a/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/ResponseBuilderTest.scala b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/ResponseBuilderTest.scala index 996936ab67..4c6d1b2b1f 100755 --- a/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/ResponseBuilderTest.scala +++ b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/ResponseBuilderTest.scala @@ -3,7 +3,7 @@ package org.bigbluebutton.api.util import java.io.File import java.util -import org.bigbluebutton.api.domain.{Meeting, User} +import org.bigbluebutton.api.domain.{Meeting, RecordingMetadata, User} import org.scalatest._ class ResponseBuilderTest extends UnitSpec { @@ -182,4 +182,18 @@ class ResponseBuilderTest extends UnitSpec { assert(templateLoc.exists()) } + + it should "reply to getRecordings api call" in { + val templateLoc = new File("src/test/resources") + val builder = new ResponseBuilder(templateLoc) + + val metadataXml = new File("src/test/resources/breakout-room-metadata.xml") + val recMeta = RecordingMetadataReaderHelper.getRecordingMetadata(metadataXml); + val recList = new util.ArrayList[RecordingMetadata]() + recList.add(recMeta) + def response = builder.buildGetRecordingsResponse(recList, "success") + println(response) + + assert(templateLoc.exists()) + } } \ No newline at end of file diff --git a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy index 50e6a46615..6268d4448c 100755 --- a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy +++ b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy @@ -19,6 +19,7 @@ package org.bigbluebutton.web.controllers import com.google.gson.Gson +import org.bigbluebutton.api.domain.RecordingMetadata import org.bigbluebutton.api.util.ResponseBuilder import javax.servlet.ServletRequest; @@ -1735,6 +1736,8 @@ class ApiController { return } + log.debug request.getQueryString() + // Do we agree on the checksum? If not, complain. if (! paramsProcessorUtil.isChecksumSame(API_CALL, params.checksum, request.getQueryString())) { errors.checksumError() @@ -1764,8 +1767,12 @@ class ApiController { internalRecordIds = paramsProcessorUtil.convertToInternalMeetingId(externalMeetingIds); } - Map recs = meetingService.getRecordings(internalRecordIds, states); - recs = meetingService.filterRecordingsByMetadata(recs, ParamsProcessorUtil.processMetaParam(params)); + for(String intRecId : internalRecordIds){ + log.debug intRecId + } + + List recsList = meetingService.getRecordingsMetadata(internalRecordIds, states); + Listrecs = meetingService.filterRecordingsByMetadata(recsList, ParamsProcessorUtil.processMetaParam(params)); if (recs.isEmpty()) { response.addHeader("Cache-Control", "no-cache") @@ -1784,20 +1791,13 @@ class ApiController { return; } + def templateLoc = getServletContext().getRealPath("/WEB-INF/freemarker") + ResponseBuilder responseBuilder = new ResponseBuilder(new File(templateLoc)) - - def cfg = new Configuration() - - // Load the XML template - // TODO: Maybe there is a better way to define the templates path - def wtl = new WebappTemplateLoader(getServletContext(), "/WEB-INF/freemarker") - cfg.setTemplateLoader(wtl) - def ftl = cfg.getTemplate("get-recordings.ftl") - def xmlText = new StringWriter() - ftl.process([code:RESP_CODE_SUCCESS, recs:recs.values()], xmlText) + def xmlText = responseBuilder.buildGetRecordingsResponse(recsList, "success") withFormat { xml { - render(text: xmlText.toString(), contentType: "text/xml") + render(text: xmlText, contentType: "text/xml") } } } diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java index f1ee969752..b2bb9e1fd1 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java @@ -37,11 +37,8 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; -import org.bigbluebutton.api.domain.Meeting; -import org.bigbluebutton.api.domain.Playback; -import org.bigbluebutton.api.domain.Recording; -import org.bigbluebutton.api.domain.User; -import org.bigbluebutton.api.domain.UserSession; + +import org.bigbluebutton.api.domain.*; import org.bigbluebutton.api.messaging.MessageListener; import org.bigbluebutton.api.messaging.MessagingService; import org.bigbluebutton.api.messaging.messages.CreateBreakoutRoom; @@ -425,18 +422,30 @@ public class MeetingService implements MessageListener { return null; } + public List getRecordingsMetadata(List idList, List states) { + List recsList = recordingService.getRecordingsMetadata(idList, states); + return recsList; + } + + public Map getRecordings(List idList, List states) { List recsList = recordingService.getRecordings(idList, states); Map recs = reorderRecordings(recsList); return recs; } - public Map filterRecordingsByMetadata( - Map recordings, - Map metadataFilters) { + public List filterRecordingsByMetadata(List recsList, + Map metadataFilters) { + return recordingService.filterRecordingsByMetadata(recsList, metadataFilters); + } + + public Map filterRecordingsByMetadata(Map recordings, + Map metadataFilters) { return recordingService.filterRecordingsByMetadata(recordings, metadataFilters); } + + public Map reorderRecordings(List olds) { Map map = new HashMap(); for (Recording r : olds) { diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/RecordingService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/RecordingService.java index 8f0a2162e2..f3cff507fe 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/RecordingService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/RecordingService.java @@ -34,6 +34,8 @@ import java.util.Map; import java.util.Set; import org.bigbluebutton.api.domain.Recording; +import org.bigbluebutton.api.domain.RecordingMetadata; +import org.bigbluebutton.api.util.RecordingMetadataReaderHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,6 +66,38 @@ public class RecordingService { } } + public List getRecordingsMetadata(List recordIDs, List states) { + List recs = new ArrayList(); + + Map> allDirectories = getAllDirectories(states); + if (recordIDs.isEmpty()) { + for (Map.Entry> entry : allDirectories.entrySet()) { + recordIDs.addAll(getAllRecordingIds(entry.getValue())); + } + } + + for (String recordID : recordIDs) { + for (Map.Entry> entry : allDirectories.entrySet()) { + List _recs = getRecordingsForPath(recordID, entry.getValue()); + Iterator iterator = _recs.iterator(); + while (iterator.hasNext()) { + RecordingMetadata r = getRecordingMetadata(iterator.next()); + if (r != null) { + recs.add(r); + } + } + } + } + + return recs; + } + + private RecordingMetadata getRecordingMetadata(File dir) { + File file = new File(dir.getPath() + File.separatorChar + "metadata.xml"); + RecordingMetadata rec = RecordingMetadataReaderHelper.getRecordingMetadata(file); + return rec; + } + public List getRecordings(List recordIDs, List states) { List recs = new ArrayList(); @@ -119,6 +153,45 @@ public class RecordingService { return matchesMetadata; } + public boolean recordingMatchesMetadata(RecordingMetadata recording, Map metadataFilters) { + boolean matchesMetadata = true; + for (Map.Entry filter : metadataFilters.entrySet()) { + String metadataValue = recording.getMeta().get().get(filter.getKey()); + if ( metadataValue == null ) { + // The recording doesn't have metadata specified + matchesMetadata = false; + } else { + String filterValue = filter.getValue(); + if( filterValue.charAt(0) == '%' && filterValue.charAt(filterValue.length()-1) == '%' && metadataValue.contains(filterValue.substring(1, filterValue.length()-1)) ){ + // Filter value embraced by two wild cards + // AND the filter value is part of the metadata value + } else if( filterValue.charAt(0) == '%' && metadataValue.endsWith(filterValue.substring(1, filterValue.length())) ) { + // Filter value starts with a wild cards + // AND the filter value ends with the metadata value + } else if( filterValue.charAt(filterValue.length()-1) == '%' && metadataValue.startsWith(filterValue.substring(0, filterValue.length()-1)) ) { + // Filter value ends with a wild cards + // AND the filter value starts with the metadata value + } else if( metadataValue.equals(filterValue) ) { + // Filter value doesnt have wildcards + // AND the filter value is the same as metadata value + } else { + matchesMetadata = false; + } + } + } + return matchesMetadata; + } + + + public List filterRecordingsByMetadata(List recordings, Map metadataFilters) { + List resultRecordings = new ArrayList(); + for (RecordingMetadata entry : recordings) { + if (recordingMatchesMetadata(entry, metadataFilters)) + resultRecordings.add(entry); + } + return resultRecordings; + } + public Map filterRecordingsByMetadata(Map recordings, Map metadataFilters) { Map resultRecordings = new HashMap(); for (Map.Entry entry : recordings.entrySet()) { diff --git a/bigbluebutton-web/web-app/WEB-INF/freemarker/get-recordings.ftl b/bigbluebutton-web/web-app/WEB-INF/freemarker/get-recordings.ftl old mode 100644 new mode 100755 index 7ce104ffa2..33d908790b --- a/bigbluebutton-web/web-app/WEB-INF/freemarker/get-recordings.ftl +++ b/bigbluebutton-web/web-app/WEB-INF/freemarker/get-recordings.ftl @@ -2,62 +2,64 @@ <#compress> <#-- Where code is a 'SUCCESS' or 'FAILED' String --> - ${code} + ${returnCode} - <#-- Where recs is a String -> Recording HashMap --> - <#list recs as r> + <#list recordings as r> ${r.getId()} - <#if r.getMeetingID()?? && r.getMeetingID() != "">${r.getMeetingID()?html} - <#if r.getName()?? && r.getName() != ""> - ${r.isPublished()?string} + ${r.getMeeting().getId()?html} + ${r.getMeeting().getExternalId()?html} + + ${r.getMeeting().isBreakout()?c} + ${r.getPublished()?string} ${r.getState()?string} <#if r.getStartTime()?? && r.getStartTime() != "">${r.getStartTime()} <#if r.getEndTime()?? && r.getEndTime() != "">${r.getEndTime()} - <#if r.getNumParticipants()??>${r.getNumParticipants()} - <#assign m = r.getMetadata()> + <#if r.getParticipants()??>${r.getParticipants()} + <#if r.getBreakout()??> + <#assign breakout = r.getBreakout()> + + ${breakout.getParentMeetingId()} + ${breakout.getSequence()?c} + + + <#if r.getBreakoutRooms()??> + <#list r.getBreakoutRooms()> + + <#items as broom> + ${broom.getValue()} + + + + + <#assign m = r.getMeta().get()> <#list m?keys as prop> <${prop}> + <#assign pb = r.getPlayback()> - <#if r.getPlaybacks()??> - <#list r.getPlaybacks() as p> - <#if p?? && p.getFormat()??> - - ${p.getFormat()} - ${p.getUrl()} - ${p.getLength()} - <#if p.getExtensions()??> - <#list p.getExtensions() as extension> - <${extension.getType()}> - <#assign properties = extension.getProperties()> - <#if extension.getType() == "preview"> - <#list properties?keys as property> - <#if property == "images"> - <${property}> - <#if properties[property]["image"]?? && properties[property]["image"]?is_hash> - <#assign image = properties[property]["image"]> - width="${image["attributes"]["width"]}" <#if image["attributes"]?? && image["attributes"]["height"]??>height="${image["attributes"]["height"]}" <#if image["attributes"]?? && image["attributes"]["alt"]??>alt="<#escape x as x?xml>${image["attributes"]["alt"]}">${image["text"]} - <#elseif properties[property]["image"]?? && properties[property]["image"]?is_enumerable> - <#list properties[property]["image"] as image> - width="${image["attributes"]["width"]}" <#if image["attributes"]?? && image["attributes"]["height"]??>height="${image["attributes"]["height"]}" <#if image["attributes"]?? && image["attributes"]["alt"]??>alt="<#escape x as x?xml>${image["attributes"]["alt"]}">${image["text"]} - - - - <#else> - <${property} /> - - - - - - - - - - + ${pb.getFormat()} + ${pb.getLink()} + ${pb.getProcessingTime()?c} + ${pb.getDuration()?c} + <#if pb.getExtensions()??> + + <#if pb.getExtensions().getPreview()??> + <#assign prev = pb.getExtensions().getPreview()> + + <#list prev.getImages()> + + <#items as image> + ${image.getAlt()}${image.getValue()} + + + + + + + diff --git a/record-and-playback/core/lib/recordandplayback/events_archiver.rb b/record-and-playback/core/lib/recordandplayback/events_archiver.rb index 8f06be2284..562f94a4ab 100755 --- a/record-and-playback/core/lib/recordandplayback/events_archiver.rb +++ b/record-and-playback/core/lib/recordandplayback/events_archiver.rb @@ -191,6 +191,8 @@ module BigBlueButton MODULE = 'module' EVENTNAME = 'eventName' MEETINGID = 'meetingId' + MEETINGNAME = 'meetingName' + ISBREAKOUT = 'isBreakout' def initialize(redis) @redis = redis @@ -206,6 +208,7 @@ module BigBlueButton if (meeting_metadata != nil) xml.recording(:meeting_id => meeting_id, :bbb_version => version) { + xml.meeting(:id => meeting_id, :externalId => meeting_metadata[MEETINGID], :name => meeting_metadata[MEETINGNAME], :breakout => meeting_metadata[ISBREAKOUT]) xml.metadata(meeting_metadata) if (@redis.has_breakout_metadata_for(meeting_id)) diff --git a/record-and-playback/presentation/scripts/process/presentation.rb b/record-and-playback/presentation/scripts/process/presentation.rb index ac4f787b25..b07b2e1198 100755 --- a/record-and-playback/presentation/scripts/process/presentation.rb +++ b/record-and-playback/presentation/scripts/process/presentation.rb @@ -109,10 +109,15 @@ if not FileTest.directory?(target_dir) end_time = recording.at_xpath("end_time") end_time.content = real_end_time + if (meeting_xpath != nil) + recording << meeting_xpath + end + ## Copy the breakout and breakout rooms node from ## events.xml if present. breakout_xpath = @doc.xpath("//breakout") breakout_rooms_xpath = @doc.xpath("//breakoutRooms") + meeting_xpath = @doc.xpath("//meeting") if (breakout_xpath != nil) recording << breakout_xpath From 1ccf55fd9724988d6ed6afbca35a17ae7f5de79c Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Wed, 15 Mar 2017 14:28:29 +0000 Subject: [PATCH 10/16] - add sample recording --- .../sample-recording/foo/captions.json | 1 + .../sample-recording/foo/metadata.xml | 31 +++++++++++++++++++ .../sample-recording/foo/panzooms.xml | 12 +++++++ .../sample-recording/foo/slides_new.xml | 2 ++ 4 files changed, 46 insertions(+) create mode 100644 bbb-common-web/src/test/resources/sample-recording/foo/captions.json create mode 100644 bbb-common-web/src/test/resources/sample-recording/foo/metadata.xml create mode 100644 bbb-common-web/src/test/resources/sample-recording/foo/panzooms.xml create mode 100644 bbb-common-web/src/test/resources/sample-recording/foo/slides_new.xml diff --git a/bbb-common-web/src/test/resources/sample-recording/foo/captions.json b/bbb-common-web/src/test/resources/sample-recording/foo/captions.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/bbb-common-web/src/test/resources/sample-recording/foo/captions.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/bbb-common-web/src/test/resources/sample-recording/foo/metadata.xml b/bbb-common-web/src/test/resources/sample-recording/foo/metadata.xml new file mode 100644 index 0000000000..9463f90480 --- /dev/null +++ b/bbb-common-web/src/test/resources/sample-recording/foo/metadata.xml @@ -0,0 +1,31 @@ + + f3ffe06acedf425565cc024c8ebe89a6552e8782-1489435015784 + foo + false + 1489435015784 + 1489435076055 + + + + 32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780 + 721d83a3907548734d4a505992ebb94ec1454a91-1489173065780 + + + random-2810069 + random-2810069 + false + + + presentation + http://192.168.23.22/playback/presentation/0.9.0/playback.html?meetingId=f3ffe06acedf425565cc024c8ebe89a6552e8782-1489435015784 + 28627 + 32213 + + + + Welcome tohttp://192.168.23.22/presentation/32ee8bcccfad34f85c58a12f87fc4268130a4fd3-1489173065780/presentation/743dd59a958334b4cdcdaa302846d0c0eadcf9ff-1489173070800/thumbnails/thumb-1.png + + + + + diff --git a/bbb-common-web/src/test/resources/sample-recording/foo/panzooms.xml b/bbb-common-web/src/test/resources/sample-recording/foo/panzooms.xml new file mode 100644 index 0000000000..8bfa557aae --- /dev/null +++ b/bbb-common-web/src/test/resources/sample-recording/foo/panzooms.xml @@ -0,0 +1,12 @@ + + + + 0.0 0.0 1600.0 1200.0 + + + 0.0 0.0 1600.0 1200.0 + + + 0.0 0.0 1600.0 1200.0 + + diff --git a/bbb-common-web/src/test/resources/sample-recording/foo/slides_new.xml b/bbb-common-web/src/test/resources/sample-recording/foo/slides_new.xml new file mode 100644 index 0000000000..d623946f41 --- /dev/null +++ b/bbb-common-web/src/test/resources/sample-recording/foo/slides_new.xml @@ -0,0 +1,2 @@ + + From 058a4044d614a7bed2db9412a118fba3142f322b Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Wed, 15 Mar 2017 14:29:10 +0000 Subject: [PATCH 11/16] - publish/unpublish recording --- bbb-common-web/build.sbt | 3 + .../bigbluebutton/api/RecordingService.java | 162 ++++++++++++------ .../api/RecordingServiceHelper.java | 0 .../bigbluebutton/api/domain/Metadata.java | 8 + .../util/RecordingMetadataReaderHelper.java | 30 +++- .../RecordingMetadataReaderHelperTest.scala | 29 +++- .../api/util/RecordingServiceTest.scala | 74 ++++++++ .../web/controllers/ApiController.groovy | 4 +- .../org/bigbluebutton/api/MeetingService.java | 3 +- 9 files changed, 252 insertions(+), 61 deletions(-) rename {bigbluebutton-web/src => bbb-common-web/src/main}/java/org/bigbluebutton/api/RecordingService.java (75%) rename {bigbluebutton-web/src => bbb-common-web/src/main}/java/org/bigbluebutton/api/RecordingServiceHelper.java (100%) create mode 100755 bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingServiceTest.scala diff --git a/bbb-common-web/build.sbt b/bbb-common-web/build.sbt index ce48e9aad9..6b4d293c6f 100755 --- a/bbb-common-web/build.sbt +++ b/bbb-common-web/build.sbt @@ -32,11 +32,14 @@ libraryDependencies += "org.scala-lang" % "scala-library" % scalaV libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaV libraryDependencies += "commons-lang" % "commons-lang" % "2.5" +libraryDependencies += "commons-io" % "commons-io" % "2.4" libraryDependencies += "org.freemarker" % "freemarker" % "2.3.23" libraryDependencies += "com.fasterxml.jackson.dataformat" % "jackson-dataformat-xml" % "2.6.3" // https://mvnrepository.com/artifact/org.codehaus.woodstox/woodstox-core-asl libraryDependencies += "org.codehaus.woodstox" % "woodstox-core-asl" % "4.4.1" +libraryDependencies += "org.slf4j" % "slf4j-api" % "1.7.5" + libraryDependencies += "org.pegdown" % "pegdown" % "1.4.0" % "test" libraryDependencies += "junit" % "junit" % "4.12" % "test" libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test" diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/RecordingService.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/RecordingService.java similarity index 75% rename from bigbluebutton-web/src/java/org/bigbluebutton/api/RecordingService.java rename to bbb-common-web/src/main/java/org/bigbluebutton/api/RecordingService.java index f3cff507fe..1c9f23fb1d 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/RecordingService.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/RecordingService.java @@ -92,7 +92,7 @@ public class RecordingService { return recs; } - private RecordingMetadata getRecordingMetadata(File dir) { + private static RecordingMetadata getRecordingMetadata(File dir) { File file = new File(dir.getPath() + File.separatorChar + "metadata.xml"); RecordingMetadata rec = RecordingMetadataReaderHelper.getRecordingMetadata(file); return rec; @@ -262,7 +262,7 @@ public class RecordingService { return rec; } - private void deleteRecording(String id, String path) { + private static void deleteRecording(String id, String path) { String[] format = getPlaybackFormats(path); for (int i = 0; i < format.length; i++) { List recordings = getDirectories(path + File.separatorChar + format[i]); @@ -275,12 +275,12 @@ public class RecordingService { } } - private void createDirectory(File directory) { + private static void createDirectory(File directory) { if (!directory.exists()) directory.mkdirs(); } - private void deleteDirectory(File directory) { + private static void deleteDirectory(File directory) { /** * Go through each directory and check if it's not empty. We need to * delete files inside a directory before a directory can be deleted. @@ -297,7 +297,7 @@ public class RecordingService { directory.delete(); } - private List getDirectories(String path) { + private static List getDirectories(String path) { List files = new ArrayList(); try { DirectoryStream stream = Files.newDirectoryStream(FileSystems.getDefault().getPath(path)); @@ -313,7 +313,7 @@ public class RecordingService { return files; } - private String[] getPlaybackFormats(String path) { + private static String[] getPlaybackFormats(String path) { List dirs = getDirectories(path); String[] formats = new String[dirs.size()]; @@ -388,41 +388,94 @@ public class RecordingService { List recordings = getDirectories(path + File.separatorChar + format[i]); for (int f = 0; f < recordings.size(); f++) { if (recordings.get(f).getName().equalsIgnoreCase(recordingId)) { - Recording r = getRecordingInfo(recordings.get(f)); - if (r != null) { - File dest; - if (state.equals(Recording.STATE_PUBLISHED)) { - dest = new File(publishedDir + File.separatorChar + format[i]); - } else if (state.equals(Recording.STATE_UNPUBLISHED)) { - dest = new File(unpublishedDir + File.separatorChar + format[i]); - } else if (state.equals(Recording.STATE_DELETED)) { - dest = new File(deletedDir + File.separatorChar + format[i]); - } else { - log.debug(String.format("State: %s, is not supported", state)); - return; - } - if (!dest.exists()) - dest.mkdirs(); - boolean moved = recordings.get(f).renameTo(new File(dest, recordings.get(f).getName())); - if (moved) { - log.debug("Recording successfully moved!"); - r.setState(state); - r.setPublished(state.equals(Recording.STATE_PUBLISHED)); - if (state.equals(Recording.STATE_DELETED)) { - r.setPlaybackFormat(null); - deleteRecording(recordingId, deletedDir); - } - recordingServiceHelper.writeRecordingInfo(dest.getAbsolutePath() + File.separatorChar + recordings.get(f).getName(), r); - log.debug(String.format("Recording successfully %s!", state)); - } else { - log.debug("Recording was not moved"); - } + File dest; + if (state.equals(Recording.STATE_PUBLISHED)) { + dest = new File(publishedDir + File.separatorChar + format[i]); + RecordingService.publishRecording(dest, recordingId, recordings.get(f)); + } else if (state.equals(Recording.STATE_UNPUBLISHED)) { + dest = new File(unpublishedDir + File.separatorChar + format[i]); + RecordingService.unpublishRecording(dest, recordingId, recordings.get(f)); + } else if (state.equals(Recording.STATE_DELETED)) { + dest = new File(deletedDir + File.separatorChar + format[i]); + RecordingService.deleteRecording(dest, recordingId, recordings.get(f)); + } else { + log.debug(String.format("State: %s, is not supported", state)); + return; } } } } } + public static void publishRecording(File destDir, String recordingId, File recordingDir) { + File metadataXml = RecordingMetadataReaderHelper.getMetadataXmlLocation(recordingDir.getPath() + File.separatorChar + recordingId); + RecordingMetadata r = RecordingMetadataReaderHelper.getRecordingMetadata(metadataXml); + if (r != null) { + if (!destDir.exists()) destDir.mkdirs(); + + boolean moved = recordingDir.renameTo(destDir); + if (moved) { + log.debug("Recording successfully moved!"); + r.setState(Recording.STATE_PUBLISHED); + r.setPublished(true); + + File medataXmlFile = RecordingMetadataReaderHelper.getMetadataXmlLocation(destDir.getAbsolutePath() + File.separatorChar + recordingId); + // Process the changes by saving the recording into metadata.xml + RecordingMetadataReaderHelper.saveRecordingMetadata(medataXmlFile, r); + + log.debug(String.format("Published successfully %s!", recordingId)); + } else { + log.debug("Recording was not moved"); + } + } + } + + public static void unpublishRecording(File destDir, String recordingId, File recordingDir) { + File metadataXml = RecordingMetadataReaderHelper.getMetadataXmlLocation(recordingDir.getPath() + File.separatorChar + recordingId); + RecordingMetadata r = RecordingMetadataReaderHelper.getRecordingMetadata(metadataXml); + if (r != null) { + if (!destDir.exists()) destDir.mkdirs(); + + boolean moved = recordingDir.renameTo(destDir); + if (moved) { + r.setState(Recording.STATE_UNPUBLISHED); + r.setPublished(false); + + File medataXmlFile = RecordingMetadataReaderHelper.getMetadataXmlLocation(destDir.getAbsolutePath() + File.separatorChar + recordingId); + // Process the changes by saving the recording into metadata.xml + RecordingMetadataReaderHelper.saveRecordingMetadata(medataXmlFile, r); + + log.debug(String.format("Unpublished successfully %s!", recordingId)); + } else { + log.debug("Recording was not moved"); + } + } + } + + public static void deleteRecording(File destDir, String recordingId, File recordingDir) { + File metadataXml = RecordingMetadataReaderHelper.getMetadataXmlLocation(recordingDir.getPath() + File.separatorChar + recordingId); + RecordingMetadata r = RecordingMetadataReaderHelper.getRecordingMetadata(metadataXml); + if (r != null) { + if (!destDir.exists()) destDir.mkdirs(); + + boolean moved = recordingDir.renameTo(destDir); + if (moved) { + r.setState(Recording.STATE_DELETED); + r.setPublished(false); + deleteRecording(recordingId, destDir.getAbsolutePath()); + + File medataXmlFile = RecordingMetadataReaderHelper.getMetadataXmlLocation(destDir.getAbsolutePath() + File.separatorChar + recordingId); + // Process the changes by saving the recording into metadata.xml + RecordingMetadataReaderHelper.saveRecordingMetadata(medataXmlFile, r); + + log.debug(String.format("Unpublished successfully %s!", recordingId)); + } else { + log.debug("Recording was not moved"); + } + } + } + + private List getAllDirectories(String state) { List allDirectories = new ArrayList(); @@ -488,22 +541,8 @@ public class RecordingService { Map recsIndexed = indexRecordings(recs); if ( recsIndexed.containsKey(recordID) ) { File recFile = recsIndexed.get(recordID); - Recording rec = getRecordingInfo(recFile); - if (rec != null) { - for (Map.Entry meta : metaParams.entrySet()) { - if ( !"".equals(meta.getValue()) ) { - // As it has a value, if the meta parameter exists update it, otherwise add it - rec.updateMetadata(meta.getKey(), meta.getValue()); - } else { - // As it doesn't have a value, if it exists delete it - if ( rec.containsMetadata(meta.getKey()) ) { - rec.deleteMetadata(meta.getKey()); - } - } - } - // Process the changes by saving the recording into metadata.xml - recordingServiceHelper.writeRecordingInfo(recFile.getAbsolutePath(), rec); - } + File metadataXml = RecordingMetadataReaderHelper.getMetadataXmlLocation(recFile.getPath()); + updateRecordingMetadata(metadataXml, metaParams, metadataXml); } } } @@ -511,6 +550,27 @@ public class RecordingService { return; } + public static void updateRecordingMetadata(File srxMetadataXml, Map metaParams, File destMetadataXml) { + RecordingMetadata rec = RecordingMetadataReaderHelper.getRecordingMetadata(srxMetadataXml); + if (rec != null && rec.getMeta() != null) { + for (Map.Entry meta : metaParams.entrySet()) { + if ( !"".equals(meta.getValue()) ) { + // As it has a value, if the meta parameter exists update it, otherwise add it + rec.getMeta().set(meta.getKey(), meta.getValue()); + } else { + // As it doesn't have a value, if it exists delete it + if ( rec.getMeta().containsKey(meta.getKey()) ) { + rec.getMeta().remove(meta.getKey()); + } + } + } + + // Process the changes by saving the recording into metadata.xml + RecordingMetadataReaderHelper.saveRecordingMetadata(destMetadataXml, rec); + } + } + + private Map indexRecordings(List recs) { Map indexedRecs = new HashMap(); diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/RecordingServiceHelper.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/RecordingServiceHelper.java similarity index 100% rename from bigbluebutton-web/src/java/org/bigbluebutton/api/RecordingServiceHelper.java rename to bbb-common-web/src/main/java/org/bigbluebutton/api/RecordingServiceHelper.java diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Metadata.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Metadata.java index 5e23b1cd43..fd3a85538e 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Metadata.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Metadata.java @@ -20,4 +20,12 @@ public class Metadata { public void set(String name, String value) { map.put(name, value); } + + public void remove(String key) { + map.remove(key); + } + + public boolean containsKey(String key) { + return map.containsKey(key); + } } diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java index 8b1d942735..41bdaa08ef 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java @@ -4,10 +4,9 @@ package org.bigbluebutton.api.util; import com.fasterxml.jackson.dataformat.xml.JacksonXmlModule; import com.fasterxml.jackson.dataformat.xml.XmlMapper; import org.bigbluebutton.api.domain.RecordingMetadata; +import com.fasterxml.jackson.databind.SerializationFeature; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.*; import java.io.*; public class RecordingMetadataReaderHelper { @@ -48,4 +47,29 @@ public class RecordingMetadataReaderHelper { return recMeta; } + + public static File getMetadataXmlLocation(String destDir) { + return new File(destDir + File.separatorChar + "metadata.xml"); + } + + public static void saveRecordingMetadata(File metadataXml, RecordingMetadata recordingMetadata) { + + //XMLOutputFactory factory = XMLOutputFactory.newInstance(); + JacksonXmlModule module = new JacksonXmlModule(); + module.setDefaultUseWrapper(false); + + XmlMapper mapper = new XmlMapper(module); + + //Reading from xml file and creating XMLStreamReader + //XMLStreamWriter writer = null; + try { + //writer = factory.createXMLStreamWriter(new FileOutputStream(metadataXml)); + mapper.enable(SerializationFeature.INDENT_OUTPUT); + mapper.writeValue(metadataXml, recordingMetadata); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } } diff --git a/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingMetadataReaderHelperTest.scala b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingMetadataReaderHelperTest.scala index 8dc4f13c07..0c280bf494 100755 --- a/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingMetadataReaderHelperTest.scala +++ b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingMetadataReaderHelperTest.scala @@ -6,9 +6,6 @@ import javax.xml.stream.{XMLInputFactory, XMLStreamReader} import com.fasterxml.jackson.dataformat.xml.{JacksonXmlModule, XmlMapper} import org.bigbluebutton.api.domain.{RecordingMetadata, RecordingMetadataPlayback} -/** - * Created by ralam on 3/10/2017. - */ class RecordingMetadataReaderHelperTest extends UnitSpec { it should "deserialize playback part of metadata.xml" in { @@ -53,4 +50,30 @@ class RecordingMetadataReaderHelperTest extends UnitSpec { } + it should "save metadata.xml" in { + val factory: XMLInputFactory = XMLInputFactory.newInstance(); + + val xml = new File("src/test/resources/breakout-room-metadata.xml") + val module: JacksonXmlModule = new JacksonXmlModule(); + // and then configure, for example: + module.setDefaultUseWrapper(false); + + val mapper: XmlMapper = new XmlMapper(module) + + //Reading from xml file and creating XMLStreamReader + val reader: XMLStreamReader = factory.createXMLStreamReader(new FileInputStream(xml)) + + val recMeta: RecordingMetadata = mapper.readValue(reader, classOf[RecordingMetadata]) + + recMeta.getMeta().set("FOO", "BAR"); + + val metadataXml = RecordingMetadataReaderHelper.getMetadataXmlLocation("target") + if (metadataXml.exists()) metadataXml.delete() + + RecordingMetadataReaderHelper.saveRecordingMetadata(metadataXml, recMeta) + + assert(metadataXml.exists()) + + } + } diff --git a/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingServiceTest.scala b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingServiceTest.scala new file mode 100755 index 0000000000..10519166f0 --- /dev/null +++ b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingServiceTest.scala @@ -0,0 +1,74 @@ +package org.bigbluebutton.api.util + +import java.io.File +import java.util + +import org.apache.commons.io.FileUtils +import org.bigbluebutton.api.RecordingService + +class RecordingServiceTest extends UnitSpec { + + it should "deserialize playback part of metadata.xml" in { + val srcXml = new File("src/test/resources/breakout-room-metadata.xml") + val metaParams: util.Map[String, String] = new util.TreeMap[String, String]() + + metaParams.put("foo", "bar") + metaParams.put("bar", "baz") + + val destXml = new File("target/updated-metadata.xml") + RecordingService.updateRecordingMetadata(srcXml, metaParams, destXml) + } + + it should "publish recording" in { + // Make a copy of our sample recording + val destDir = new File("target/sample-recording/publish") + if (destDir.exists()) FileUtils.deleteDirectory(destDir) + + val srcDir = new File("src/test/resources/sample-recording") + FileUtils.copyDirectory(srcDir, destDir) + + val recordingId = "foo" + + val publishedDir = new File("target/published") + RecordingService.publishRecording(publishedDir, recordingId, destDir) + + assert(true) + } + + it should "unpublish recording" in { + // Make a copy of our sample recording + val destDir = new File("target/sample-recording/unpublish") + if (destDir.exists()) FileUtils.deleteDirectory(destDir) + + val srcDir = new File("src/test/resources/sample-recording") + FileUtils.copyDirectory(srcDir, destDir) + + val recordingId = "foo" + + val unpublishedDir = new File("target/unpublished") + if (unpublishedDir.exists()) FileUtils.deleteDirectory(unpublishedDir) + + RecordingService.unpublishRecording(unpublishedDir, recordingId, destDir) + + assert(unpublishedDir.exists()) + } + + it should "delete recording" in { + // Make a copy of our sample recording + val destDir = new File("target/sample-recording/delete") + if (destDir.exists()) FileUtils.deleteDirectory(destDir) + + val srcDir = new File("src/test/resources/sample-recording") + FileUtils.copyDirectory(srcDir, destDir) + + val recordingId = "foo" + + val deletedDir = new File("target/deleted") + if (deletedDir.exists()) FileUtils.deleteDirectory(deletedDir) + + RecordingService.deleteRecording(deletedDir, recordingId, destDir) + + assert(deletedDir.exists()) + } + +} diff --git a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy index 6268d4448c..c07ddd6861 100755 --- a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy +++ b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy @@ -1772,7 +1772,7 @@ class ApiController { } List recsList = meetingService.getRecordingsMetadata(internalRecordIds, states); - Listrecs = meetingService.filterRecordingsByMetadata(recsList, ParamsProcessorUtil.processMetaParam(params)); + List recs = meetingService.filterRecordingsByMetadata(recsList, ParamsProcessorUtil.processMetaParam(params)); if (recs.isEmpty()) { response.addHeader("Cache-Control", "no-cache") @@ -1794,7 +1794,7 @@ class ApiController { def templateLoc = getServletContext().getRealPath("/WEB-INF/freemarker") ResponseBuilder responseBuilder = new ResponseBuilder(new File(templateLoc)) - def xmlText = responseBuilder.buildGetRecordingsResponse(recsList, "success") + def xmlText = responseBuilder.buildGetRecordingsResponse(recs, "success") withFormat { xml { render(text: xmlText, contentType: "text/xml") diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java index b2bb9e1fd1..3491245c63 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/MeetingService.java @@ -517,8 +517,7 @@ public class MeetingService implements MessageListener { } } - public void updateRecordings(List idList, - Map metaParams) { + public void updateRecordings(List idList, Map metaParams) { recordingService.updateMetaParams(idList, metaParams); } From 3c397d8d7101d5991b6e540c8c718de31cc36212 Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Wed, 15 Mar 2017 09:45:58 -0700 Subject: [PATCH 12/16] - fix copying of meeting element into metadata.xml --- .../presentation/scripts/process/presentation.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/record-and-playback/presentation/scripts/process/presentation.rb b/record-and-playback/presentation/scripts/process/presentation.rb index b07b2e1198..7383520cff 100755 --- a/record-and-playback/presentation/scripts/process/presentation.rb +++ b/record-and-playback/presentation/scripts/process/presentation.rb @@ -109,16 +109,16 @@ if not FileTest.directory?(target_dir) end_time = recording.at_xpath("end_time") end_time.content = real_end_time - if (meeting_xpath != nil) - recording << meeting_xpath - end - ## Copy the breakout and breakout rooms node from ## events.xml if present. breakout_xpath = @doc.xpath("//breakout") breakout_rooms_xpath = @doc.xpath("//breakoutRooms") meeting_xpath = @doc.xpath("//meeting") + if (meeting_xpath != nil) + recording << meeting_xpath + end + if (breakout_xpath != nil) recording << breakout_xpath end From 1441c3e59ca5c4235e93436821d75336e59b01d0 Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Wed, 15 Mar 2017 09:47:26 -0700 Subject: [PATCH 13/16] - publish, unpublish, delete recording --- .../bigbluebutton/api/RecordingService.java | 43 ++++++++++++------- .../api/util/RecordingServiceTest.scala | 15 ++++--- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/RecordingService.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/RecordingService.java index 1c9f23fb1d..b9e8a032a9 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/RecordingService.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/RecordingService.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.apache.commons.io.FileUtils; import org.bigbluebutton.api.domain.Recording; import org.bigbluebutton.api.domain.RecordingMetadata; import org.bigbluebutton.api.util.RecordingMetadataReaderHelper; @@ -408,68 +409,78 @@ public class RecordingService { } public static void publishRecording(File destDir, String recordingId, File recordingDir) { - File metadataXml = RecordingMetadataReaderHelper.getMetadataXmlLocation(recordingDir.getPath() + File.separatorChar + recordingId); + File metadataXml = RecordingMetadataReaderHelper.getMetadataXmlLocation(recordingDir.getPath()); RecordingMetadata r = RecordingMetadataReaderHelper.getRecordingMetadata(metadataXml); if (r != null) { if (!destDir.exists()) destDir.mkdirs(); - boolean moved = recordingDir.renameTo(destDir); - if (moved) { + try { + FileUtils.moveDirectory(recordingDir, new File(destDir.getPath() + File.separatorChar + recordingId)); log.debug("Recording successfully moved!"); r.setState(Recording.STATE_PUBLISHED); r.setPublished(true); - File medataXmlFile = RecordingMetadataReaderHelper.getMetadataXmlLocation(destDir.getAbsolutePath() + File.separatorChar + recordingId); + File medataXmlFile = RecordingMetadataReaderHelper.getMetadataXmlLocation( + destDir.getAbsolutePath() + File.separatorChar + recordingId); + // Process the changes by saving the recording into metadata.xml RecordingMetadataReaderHelper.saveRecordingMetadata(medataXmlFile, r); log.debug(String.format("Published successfully %s!", recordingId)); - } else { + } catch (IOException e) { + e.printStackTrace(); log.debug("Recording was not moved"); } } } public static void unpublishRecording(File destDir, String recordingId, File recordingDir) { - File metadataXml = RecordingMetadataReaderHelper.getMetadataXmlLocation(recordingDir.getPath() + File.separatorChar + recordingId); + File metadataXml = RecordingMetadataReaderHelper.getMetadataXmlLocation(recordingDir.getPath()); + RecordingMetadata r = RecordingMetadataReaderHelper.getRecordingMetadata(metadataXml); if (r != null) { if (!destDir.exists()) destDir.mkdirs(); - boolean moved = recordingDir.renameTo(destDir); - if (moved) { + try { + FileUtils.moveDirectory(recordingDir, new File(destDir.getPath() + File.separatorChar + recordingId)); r.setState(Recording.STATE_UNPUBLISHED); r.setPublished(false); - File medataXmlFile = RecordingMetadataReaderHelper.getMetadataXmlLocation(destDir.getAbsolutePath() + File.separatorChar + recordingId); + File medataXmlFile = RecordingMetadataReaderHelper.getMetadataXmlLocation( + destDir.getAbsolutePath() + File.separatorChar + recordingId); + // Process the changes by saving the recording into metadata.xml RecordingMetadataReaderHelper.saveRecordingMetadata(medataXmlFile, r); log.debug(String.format("Unpublished successfully %s!", recordingId)); - } else { + } catch (IOException e) { + e.printStackTrace(); log.debug("Recording was not moved"); } } } public static void deleteRecording(File destDir, String recordingId, File recordingDir) { - File metadataXml = RecordingMetadataReaderHelper.getMetadataXmlLocation(recordingDir.getPath() + File.separatorChar + recordingId); + File metadataXml = RecordingMetadataReaderHelper.getMetadataXmlLocation(recordingDir.getPath()); + RecordingMetadata r = RecordingMetadataReaderHelper.getRecordingMetadata(metadataXml); if (r != null) { if (!destDir.exists()) destDir.mkdirs(); - boolean moved = recordingDir.renameTo(destDir); - if (moved) { + try { + FileUtils.moveDirectory(recordingDir, new File(destDir.getPath() + File.separatorChar + recordingId)); r.setState(Recording.STATE_DELETED); r.setPublished(false); - deleteRecording(recordingId, destDir.getAbsolutePath()); - File medataXmlFile = RecordingMetadataReaderHelper.getMetadataXmlLocation(destDir.getAbsolutePath() + File.separatorChar + recordingId); + File medataXmlFile = RecordingMetadataReaderHelper.getMetadataXmlLocation( + destDir.getAbsolutePath() + File.separatorChar + recordingId); + // Process the changes by saving the recording into metadata.xml RecordingMetadataReaderHelper.saveRecordingMetadata(medataXmlFile, r); log.debug(String.format("Unpublished successfully %s!", recordingId)); - } else { + } catch (IOException e) { + e.printStackTrace(); log.debug("Recording was not moved"); } } diff --git a/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingServiceTest.scala b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingServiceTest.scala index 10519166f0..4f9af47d02 100755 --- a/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingServiceTest.scala +++ b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/RecordingServiceTest.scala @@ -28,9 +28,10 @@ class RecordingServiceTest extends UnitSpec { FileUtils.copyDirectory(srcDir, destDir) val recordingId = "foo" + val recordingDir = new File(destDir.getPath() + File.separatorChar + recordingId) - val publishedDir = new File("target/published") - RecordingService.publishRecording(publishedDir, recordingId, destDir) + val publishedDir = new File("target/recording/published") + RecordingService.publishRecording(publishedDir, recordingId, recordingDir) assert(true) } @@ -44,11 +45,12 @@ class RecordingServiceTest extends UnitSpec { FileUtils.copyDirectory(srcDir, destDir) val recordingId = "foo" + val recordingDir = new File(destDir.getPath() + File.separatorChar + recordingId) - val unpublishedDir = new File("target/unpublished") + val unpublishedDir = new File("target/recording/unpublished") if (unpublishedDir.exists()) FileUtils.deleteDirectory(unpublishedDir) - RecordingService.unpublishRecording(unpublishedDir, recordingId, destDir) + RecordingService.unpublishRecording(unpublishedDir, recordingId, recordingDir) assert(unpublishedDir.exists()) } @@ -62,11 +64,12 @@ class RecordingServiceTest extends UnitSpec { FileUtils.copyDirectory(srcDir, destDir) val recordingId = "foo" + val recordingDir = new File(destDir.getPath() + File.separatorChar + recordingId) - val deletedDir = new File("target/deleted") + val deletedDir = new File("target/recording/deleted") if (deletedDir.exists()) FileUtils.deleteDirectory(deletedDir) - RecordingService.deleteRecording(deletedDir, recordingId, destDir) + RecordingService.deleteRecording(deletedDir, recordingId, recordingDir) assert(deletedDir.exists()) } From 5e2c021c321ed7ddada52871d6c71e1f844b63c3 Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Wed, 15 Mar 2017 12:38:51 -0700 Subject: [PATCH 14/16] - log failures when processing recording api requests --- .../org/bigbluebutton/api/RecordingService.java | 15 ++++----------- .../api/util/RecordingMetadataReaderHelper.java | 13 ++++++++----- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/RecordingService.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/RecordingService.java index b9e8a032a9..fe3b28f3b8 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/RecordingService.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/RecordingService.java @@ -416,7 +416,7 @@ public class RecordingService { try { FileUtils.moveDirectory(recordingDir, new File(destDir.getPath() + File.separatorChar + recordingId)); - log.debug("Recording successfully moved!"); + r.setState(Recording.STATE_PUBLISHED); r.setPublished(true); @@ -426,10 +426,8 @@ public class RecordingService { // Process the changes by saving the recording into metadata.xml RecordingMetadataReaderHelper.saveRecordingMetadata(medataXmlFile, r); - log.debug(String.format("Published successfully %s!", recordingId)); } catch (IOException e) { - e.printStackTrace(); - log.debug("Recording was not moved"); + log.error("Failed to publish recording : " + recordingId, e); } } } @@ -452,10 +450,8 @@ public class RecordingService { // Process the changes by saving the recording into metadata.xml RecordingMetadataReaderHelper.saveRecordingMetadata(medataXmlFile, r); - log.debug(String.format("Unpublished successfully %s!", recordingId)); } catch (IOException e) { - e.printStackTrace(); - log.debug("Recording was not moved"); + log.error("Failed to unpublish recording : " + recordingId, e); } } } @@ -477,11 +473,8 @@ public class RecordingService { // Process the changes by saving the recording into metadata.xml RecordingMetadataReaderHelper.saveRecordingMetadata(medataXmlFile, r); - - log.debug(String.format("Unpublished successfully %s!", recordingId)); } catch (IOException e) { - e.printStackTrace(); - log.debug("Recording was not moved"); + log.error("Failed to delete recording : " + recordingId, e); } } } diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java index 41bdaa08ef..42730b9dc9 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/RecordingMetadataReaderHelper.java @@ -8,8 +8,11 @@ import com.fasterxml.jackson.databind.SerializationFeature; import javax.xml.stream.*; import java.io.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RecordingMetadataReaderHelper { + private static Logger log = LoggerFactory.getLogger(RecordingMetadataReaderHelper.class); public static String inputStreamToString(InputStream is) throws IOException { StringBuilder sb = new StringBuilder(); @@ -38,11 +41,11 @@ public class RecordingMetadataReaderHelper { reader = factory.createXMLStreamReader(new FileInputStream(metadataXml)); recMeta = mapper.readValue(reader, RecordingMetadata.class); } catch (XMLStreamException e) { - e.printStackTrace(); + log.error("Failed to read metadata xml for recording: " + metadataXml.getAbsolutePath(), e); } catch (FileNotFoundException e) { - e.printStackTrace(); + log.error("File not found: " + metadataXml.getAbsolutePath(), e); } catch (IOException e) { - e.printStackTrace(); + log.error("IOException on " + metadataXml.getAbsolutePath(), e); } return recMeta; @@ -67,9 +70,9 @@ public class RecordingMetadataReaderHelper { mapper.enable(SerializationFeature.INDENT_OUTPUT); mapper.writeValue(metadataXml, recordingMetadata); } catch (FileNotFoundException e) { - e.printStackTrace(); + log.error("File not found: " + metadataXml.getAbsolutePath(), e); } catch (IOException e) { - e.printStackTrace(); + log.error("IOException on " + metadataXml.getAbsolutePath(), e); } } } From 414d44a7996e6ff3cdde23254a5e956161fd637b Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Wed, 15 Mar 2017 13:47:10 -0700 Subject: [PATCH 15/16] - log exceptions --- .../api/util/ResponseBuilder.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ResponseBuilder.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ResponseBuilder.java index 37bb21c31a..5932b1742f 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ResponseBuilder.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ResponseBuilder.java @@ -10,8 +10,12 @@ import java.util.*; import freemarker.template.*; import org.bigbluebutton.api.domain.RecordingMetadata; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ResponseBuilder { + private static Logger log = LoggerFactory.getLogger(ResponseBuilder.class); + Configuration cfg = new Configuration(Configuration.VERSION_2_3_23); public ResponseBuilder(File templatesLoc) { @@ -37,7 +41,7 @@ public class ResponseBuilder { try { ftl = cfg.getTemplate("get-meeting-info.ftl"); } catch (IOException e) { - e.printStackTrace(); + log.error("Cannot find get-meeting-info.ftl template for meeting : " + meeting.getInternalId(), e); } StringWriter xmlText = new StringWriter(); @@ -50,9 +54,9 @@ public class ResponseBuilder { try { ftl.process(root, xmlText); } catch (TemplateException e) { - e.printStackTrace(); + log.error("Template exception for meeting : " + meeting.getInternalId(), e); } catch (IOException e) { - e.printStackTrace(); + log.error("IO exception for meeting : " + meeting.getInternalId(), e); } return xmlText.toString(); @@ -72,7 +76,7 @@ public class ResponseBuilder { try { ftl = cfg.getTemplate("get-meetings.ftl"); } catch (IOException e) { - e.printStackTrace(); + log.error("IO exception for get-meetings.ftl : ", e); } StringWriter xmlText = new StringWriter(); @@ -81,16 +85,12 @@ public class ResponseBuilder { root.put("returnCode", returnCode); root.put("meetingDetailsList", meetingResponseDetails); - for (MeetingResponseDetail details : (ArrayList)root.get("meetingDetailsList")) { - System.out.println(details.getMeeting().getName()); - } - try { ftl.process(root, xmlText); } catch (TemplateException e) { - e.printStackTrace(); + log.error("Template exception : ", e); } catch (IOException e) { - e.printStackTrace(); + log.error("IO exception for get-meetings.ftl : ", e); } return xmlText.toString(); @@ -102,7 +102,7 @@ public class ResponseBuilder { try { ftl = cfg.getTemplate("get-recordings.ftl"); } catch (IOException e) { - e.printStackTrace(); + log.error("IO exception for get-recordings.ftl : ", e); } StringWriter xmlText = new StringWriter(); @@ -114,9 +114,9 @@ public class ResponseBuilder { try { ftl.process(root, xmlText); } catch (TemplateException e) { - e.printStackTrace(); + log.error("Template exception : ", e); } catch (IOException e) { - e.printStackTrace(); + log.error("IO exception for get-meetings.ftl : ", e); } return xmlText.toString(); From b3787c411e7b6920d2f305ad962132e958b09896 Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Thu, 16 Mar 2017 20:34:44 +0000 Subject: [PATCH 16/16] - remove control chars from meeting name param on create api request - return an error if external meeting id contains comma (,) on create api request --- bbb-common-web/build.sbt | 5 +++- .../org/bigbluebutton/api/domain/Meeting.java | 2 +- .../bigbluebutton/api/util/ParamsUtil.java | 26 +++++++++++++++++++ .../api/util/ResponseBuilder.java | 2 +- .../api/util/ParamsUtilTest.scala | 23 ++++++++++++++++ bigbluebutton-web/build.gradle | 3 +-- .../java/org/bigbluebutton/api/ApiErrors.java | 4 +++ .../api/ParamsProcessorUtil.java | 14 ++++++++-- 8 files changed, 72 insertions(+), 7 deletions(-) create mode 100755 bbb-common-web/src/main/java/org/bigbluebutton/api/util/ParamsUtil.java create mode 100755 bbb-common-web/src/test/scala/org/bigbluebutton/api/util/ParamsUtilTest.scala diff --git a/bbb-common-web/build.sbt b/bbb-common-web/build.sbt index 6b4d293c6f..c702cefa29 100755 --- a/bbb-common-web/build.sbt +++ b/bbb-common-web/build.sbt @@ -31,7 +31,10 @@ libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaV libraryDependencies += "org.scala-lang" % "scala-library" % scalaV libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaV -libraryDependencies += "commons-lang" % "commons-lang" % "2.5" +// https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 +libraryDependencies += "org.apache.commons" % "commons-lang3" % "3.5" + + libraryDependencies += "commons-io" % "commons-io" % "2.4" libraryDependencies += "org.freemarker" % "freemarker" % "2.3.23" libraryDependencies += "com.fasterxml.jackson.dataformat" % "jackson-dataformat-xml" % "2.6.3" diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java index 733d18432e..bd3ac13acc 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/domain/Meeting.java @@ -22,7 +22,7 @@ package org.bigbluebutton.api.domain; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import org.apache.commons.lang.RandomStringUtils; +import org.apache.commons.lang3.RandomStringUtils; public class Meeting { diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ParamsUtil.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ParamsUtil.java new file mode 100755 index 0000000000..ddf8f183e7 --- /dev/null +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ParamsUtil.java @@ -0,0 +1,26 @@ +package org.bigbluebutton.api.util; + + +import org.apache.commons.lang3.StringUtils; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ParamsUtil { + private static final Pattern VALID_ID_PATTERN = Pattern.compile("[a-zA-Z][a-zA-Z0-9- ]*$"); + + public static final String invalidChars = ","; + + public static String stripControlChars(String text) { + return text.replaceAll("\\p{Cc}", ""); + } + + public static boolean isValidMeetingId(String meetingId) { + //return VALID_ID_PATTERN.matcher(meetingId).matches(); + return !containsChar(meetingId, invalidChars); + } + + public static boolean containsChar(String text, String chars) { + return StringUtils.containsAny(text, chars); + } +} diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ResponseBuilder.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ResponseBuilder.java index 5932b1742f..19d2eebf72 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ResponseBuilder.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/util/ResponseBuilder.java @@ -1,7 +1,7 @@ package org.bigbluebutton.api.util; import org.bigbluebutton.api.domain.Meeting; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import java.io.File; import java.io.IOException; diff --git a/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/ParamsUtilTest.scala b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/ParamsUtilTest.scala new file mode 100755 index 0000000000..6cf5716b06 --- /dev/null +++ b/bbb-common-web/src/test/scala/org/bigbluebutton/api/util/ParamsUtilTest.scala @@ -0,0 +1,23 @@ +package org.bigbluebutton.api.util + +import org.scalatest._ + +class ParamsUtilTest extends UnitSpec { + + it should "strip out control chars from text" in { + val text = "a\u0000b\u0007c\u008fd" + val cleaned = ParamsUtil.stripControlChars(text) + assert("abcd" == cleaned) + } + + it should "complain about invalid chars in meetingId" in { + val meetingId = "Demo , Meeting" + assert(ParamsUtil.isValidMeetingId(meetingId) == false) + } + + it should "accept valid chars in meetingId" in { + val meetingId = "Demo Meeting - 123" + assert(ParamsUtil.isValidMeetingId(meetingId) == true) + } + +} diff --git a/bigbluebutton-web/build.gradle b/bigbluebutton-web/build.gradle index 9ada920238..b9e226410c 100755 --- a/bigbluebutton-web/build.gradle +++ b/bigbluebutton-web/build.gradle @@ -16,8 +16,7 @@ dependencies { //redis compile 'redis.clients:jedis:2.7.2' compile 'org.apache.commons:commons-pool2:2.3' - - compile 'commons-lang:commons-lang:2.5' + compile 'commons-io:commons-io:2.4' compile 'commons-codec:commons-codec:1.10' compile 'com.google.code.gson:gson:1.7.1' diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/ApiErrors.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/ApiErrors.java index 1af0cc0ba3..8ebdae7537 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/ApiErrors.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/ApiErrors.java @@ -68,6 +68,10 @@ public class ApiErrors { errors.add(new String[] {"maxParticipantsReached", "The number of participants allowed for this meeting has been reached."}); } + public void addError(String[] error) { + errors.add(error); + } + public boolean hasErrors() { return errors.size() > 0; } diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java index c339f37e19..38a70c006f 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java @@ -37,6 +37,7 @@ import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang.RandomStringUtils; import org.apache.commons.lang.StringUtils; import org.bigbluebutton.api.domain.Meeting; +import org.bigbluebutton.api.util.ParamsUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.commons.httpclient.*; @@ -103,9 +104,14 @@ public class ParamsProcessorUtil { // Do we have a meeting id? If not, complain. if(!StringUtils.isEmpty(params.get("meetingID"))) { - if (StringUtils.isEmpty(StringUtils.strip(params.get("meetingID")))) { + String meetingId = StringUtils.strip(params.get("meetingID")); + if (StringUtils.isEmpty(meetingId)) { errors.missingParamError("meetingID"); - } + } else { + if (! ParamsUtil.isValidMeetingId(meetingId)) { + errors.addError(new String[] {"invalidFormat", "Meeting id contains invalid characters."}); + } + } } else { errors.missingParamError("meetingID"); } @@ -290,10 +296,14 @@ public class ParamsProcessorUtil { } public Meeting processCreateParams(Map params) { + String meetingName = params.get("name"); if (meetingName == null) { meetingName = ""; } + + meetingName = ParamsUtil.stripControlChars(meetingName); + String externalMeetingId = params.get("meetingID"); String viewerPass = processPassword(params.get("attendeePW"));