Merge branch 'meteor-rebuild-server' of https://github.com/antobinary/bigbluebutton into prototype-metor-client

This commit is contained in:
perroned 2014-06-18 10:54:03 -07:00
commit eab639e134
40 changed files with 887 additions and 306 deletions

View File

@ -37,8 +37,8 @@ public class MessagingConstants {
public static final String TO_PRESENTATION_CHANNEL = TO_BBB_APPS_CHANNEL + ":presentation";
public static final String TO_POLLING_CHANNEL = TO_BBB_APPS_CHANNEL + ":polling";
public static final String TO_USERS_CHANNEL = TO_BBB_APPS_CHANNEL + ":users";
public static final String TO_CHAT_CHANNEL = TO_BBB_APPS_CHANNEL + ":chat";
public static final String TO_CHAT_CHANNEL = TO_BBB_APPS_CHANNEL + ":chat";
public static final String TO_WHITEBOARD_CHANNEL = TO_BBB_APPS_CHANNEL + ":whiteboard";
public static final String DESTROY_MEETING_REQUEST_EVENT = "DestroyMeetingRequestEvent";
public static final String CREATE_MEETING_REQUEST_EVENT = "CreateMeetingRequestEvent";

View File

@ -0,0 +1,52 @@
package org.bigbluebutton.conference.service.whiteboard;
import org.bigbluebutton.conference.service.messaging.MessagingConstants;
import org.bigbluebutton.conference.service.messaging.redis.MessageHandler;
import org.bigbluebutton.core.api.IBigBlueButtonInGW;
import com.google.gson.JsonParser;
import com.google.gson.JsonObject;
public class WhiteboardListener implements MessageHandler{
private IBigBlueButtonInGW bbbInGW;
public void setBigBlueButtonInGW(IBigBlueButtonInGW bbbInGW) {
this.bbbInGW = bbbInGW;
}
@Override
public void handleMessage(String pattern, String channel, String message) {
if (channel.equalsIgnoreCase(MessagingConstants.TO_WHITEBOARD_CHANNEL)) {
System.out.println("AntonChannel=(whiteboard)" + channel);
JsonParser parser = new JsonParser();
JsonObject obj = (JsonObject) parser.parse(message);
JsonObject headerObject = (JsonObject) obj.get("header");
JsonObject payloadObject = (JsonObject) obj.get("payload");
String eventName = headerObject.get("name").toString().replace("\"", "");
if(eventName.equalsIgnoreCase("get_whiteboard_shapes_request")){
//more cases to follow
String roomName = payloadObject.get("meeting_id").toString().replace("\"", "");
if(eventName.equalsIgnoreCase("get_whiteboard_shapes_request")){
String requesterID = payloadObject.get("requester_id").toString().replace("\"", "");
if(payloadObject.get("whiteboard_id") != null){
String whiteboardID = payloadObject.get("whiteboard_id").toString().replace("\"", "");
System.out.println("\n FOUND A whiteboardID:" + whiteboardID + "\n");
bbbInGW.requestWhiteboardAnnotationHistory(roomName, requesterID, whiteboardID, requesterID);
}
else {
System.out.println("\n DID NOT FIND A whiteboardID \n");
}
System.out.println("\n\n\n user<" + requesterID + "> requested the shapes.\n\n");
}
}
}
}
}

View File

@ -36,60 +36,59 @@ class MeetingActor(val meetingID: String, meetingName: String, val recorded: Boo
}
def act() = {
loop {
react {
case "StartTimer" => handleStartTimer
case "Hello" => handleHello
case msg: ValidateAuthToken => handleValidateAuthToken(msg)
case msg: RegisterUser => handleRegisterUser(msg)
case msg: VoiceUserJoined => handleVoiceUserJoined(msg)
case msg: VoiceUserLeft => handleVoiceUserLeft(msg)
case msg: VoiceUserMuted => handleVoiceUserMuted(msg)
case msg: VoiceUserTalking => handleVoiceUserTalking(msg)
case msg: UserJoining => handleUserJoin(msg)
case msg: UserLeaving => handleUserLeft(msg)
case msg: AssignPresenter => handleAssignPresenter(msg)
case msg: GetUsers => handleGetUsers(msg)
case msg: ChangeUserStatus => handleChangeUserStatus(msg)
case msg: UserRaiseHand => handleUserRaiseHand(msg)
case msg: UserLowerHand => handleUserLowerHand(msg)
case msg: UserShareWebcam => handleUserShareWebcam(msg)
case msg: UserUnshareWebcam => handleUserunshareWebcam(msg)
case msg: MuteMeetingRequest => handleMuteMeetingRequest(msg)
case msg: IsMeetingMutedRequest => handleIsMeetingMutedRequest(msg)
case msg: MuteUserRequest => handleMuteUserRequest(msg)
case msg: LockUserRequest => handleLockUserRequest(msg)
case msg: EjectUserRequest => handleEjectUserRequest(msg)
case msg: SetLockSettings => handleSetLockSettings(msg)
case msg: InitLockSettings => handleInitLockSettings(msg)
case msg: LockUser => handleLockUser(msg)
case msg: LockAllUsers => handleLockAllUsers(msg)
case msg: GetLockSettings => handleGetLockSettings(msg)
case msg: IsMeetingLocked => handleIsMeetingLocked(msg)
case msg: GetChatHistoryRequest => handleGetChatHistoryRequest(msg)
case msg: SendPublicMessageRequest => handleSendPublicMessageRequest(msg)
case msg: SendPrivateMessageRequest => handleSendPrivateMessageRequest(msg)
case msg: UserConnectedToGlobalAudio => handleUserConnectedToGlobalAudio(msg)
case msg: UserDisconnectedFromGlobalAudio => handleUserDisconnectedFromGlobalAudio(msg)
case msg: GetCurrentLayoutRequest => handleGetCurrentLayoutRequest(msg)
case msg: LayoutLockSettings => handleLayoutLockSettings(msg)
case msg: SetLayoutRequest => handleSetLayoutRequest(msg)
case msg: LockLayoutRequest => handleLockLayoutRequest(msg)
case msg: UnlockLayoutRequest => handleUnlockLayoutRequest(msg)
case msg: InitializeMeeting => handleInitializeMeeting(msg)
case msg: ClearPresentation => handleClearPresentation(msg)
case msg: PresentationConversionUpdate => handlePresentationConversionUpdate(msg)
case msg: PresentationPageCountError => handlePresentationPageCountError(msg)
case msg: PresentationSlideGenerated => handlePresentationSlideGenerated(msg)
case msg: PresentationConversionCompleted => handlePresentationConversionCompleted(msg)
case msg: RemovePresentation => handleRemovePresentation(msg)
case msg: GetPresentationInfo => handleGetPresentationInfo(msg)
case msg: SendCursorUpdate => handleSendCursorUpdate(msg)
case msg: ResizeAndMoveSlide => handleResizeAndMoveSlide(msg)
case msg: GotoSlide => handleGotoSlide(msg)
case msg: SharePresentation => handleSharePresentation(msg)
case msg: GetSlideInfo => handleGetSlideInfo(msg)
case msg: PreuploadedPresentations => handlePreuploadedPresentations(msg)
loop {
react {
case "StartTimer" => handleStartTimer
case "Hello" => handleHello
case msg: ValidateAuthToken => handleValidateAuthToken(msg)
case msg: RegisterUser => handleRegisterUser(msg)
case msg: VoiceUserJoined => handleVoiceUserJoined(msg)
case msg: VoiceUserLeft => handleVoiceUserLeft(msg)
case msg: VoiceUserMuted => handleVoiceUserMuted(msg)
case msg: VoiceUserTalking => handleVoiceUserTalking(msg)
case msg: UserJoining => handleUserJoin(msg)
case msg: UserLeaving => handleUserLeft(msg)
case msg: AssignPresenter => handleAssignPresenter(msg)
case msg: GetUsers => handleGetUsers(msg)
case msg: ChangeUserStatus => handleChangeUserStatus(msg)
case msg: UserRaiseHand => handleUserRaiseHand(msg)
case msg: UserLowerHand => handleUserLowerHand(msg)
case msg: UserShareWebcam => handleUserShareWebcam(msg)
case msg: UserUnshareWebcam => handleUserunshareWebcam(msg)
case msg: MuteMeetingRequest => handleMuteMeetingRequest(msg)
case msg: IsMeetingMutedRequest => handleIsMeetingMutedRequest(msg)
case msg: MuteUserRequest => handleMuteUserRequest(msg)
case msg: LockUserRequest => handleLockUserRequest(msg)
case msg: EjectUserRequest => handleEjectUserRequest(msg)
case msg: SetLockSettings => handleSetLockSettings(msg)
case msg: InitLockSettings => handleInitLockSettings(msg)
case msg: LockUser => handleLockUser(msg)
case msg: LockAllUsers => handleLockAllUsers(msg)
case msg: GetLockSettings => handleGetLockSettings(msg)
case msg: IsMeetingLocked => handleIsMeetingLocked(msg)
case msg: GetChatHistoryRequest => handleGetChatHistoryRequest(msg)
case msg: SendPublicMessageRequest => handleSendPublicMessageRequest(msg)
case msg: SendPrivateMessageRequest => handleSendPrivateMessageRequest(msg)
case msg: UserConnectedToGlobalAudio => handleUserConnectedToGlobalAudio(msg)
case msg: UserDisconnectedFromGlobalAudio => handleUserDisconnectedFromGlobalAudio(msg)
case msg: GetCurrentLayoutRequest => handleGetCurrentLayoutRequest(msg)
case msg: SetLayoutRequest => handleSetLayoutRequest(msg)
case msg: LockLayoutRequest => handleLockLayoutRequest(msg)
case msg: UnlockLayoutRequest => handleUnlockLayoutRequest(msg)
case msg: InitializeMeeting => handleInitializeMeeting(msg)
case msg: ClearPresentation => handleClearPresentation(msg)
case msg: PresentationConversionUpdate => handlePresentationConversionUpdate(msg)
case msg: PresentationPageCountError => handlePresentationPageCountError(msg)
case msg: PresentationSlideGenerated => handlePresentationSlideGenerated(msg)
case msg: PresentationConversionCompleted => handlePresentationConversionCompleted(msg)
case msg: RemovePresentation => handleRemovePresentation(msg)
case msg: GetPresentationInfo => handleGetPresentationInfo(msg)
case msg: SendCursorUpdate => handleSendCursorUpdate(msg)
case msg: ResizeAndMoveSlide => handleResizeAndMoveSlide(msg)
case msg: GotoSlide => handleGotoSlide(msg)
case msg: SharePresentation => handleSharePresentation(msg)
case msg: GetSlideInfo => handleGetSlideInfo(msg)
case msg: PreuploadedPresentations => handlePreuploadedPresentations(msg)
case msg: PreCreatedPoll => handlePreCreatedPoll(msg)
case msg: CreatePoll => handleCreatePoll(msg)
case msg: UpdatePoll => handleUpdatePoll(msg)
@ -103,22 +102,22 @@ class MeetingActor(val meetingID: String, meetingName: String, val recorded: Boo
case msg: RespondToPoll => handleRespondToPoll(msg)
case msg: HidePollResult => handleHidePollResult(msg)
case msg: ShowPollResult => handleShowPollResult(msg)
case msg: SendWhiteboardAnnotationRequest => handleSendWhiteboardAnnotationRequest(msg)
case msg: GetWhiteboardShapesRequest => handleGetWhiteboardShapesRequest(msg)
case msg: ClearWhiteboardRequest => handleClearWhiteboardRequest(msg)
case msg: UndoWhiteboardRequest => handleUndoWhiteboardRequest(msg)
case msg: EnableWhiteboardRequest => handleEnableWhiteboardRequest(msg)
case msg: IsWhiteboardEnabledRequest => handleIsWhiteboardEnabledRequest(msg)
case msg: SetRecordingStatus => handleSetRecordingStatus(msg)
case msg: GetRecordingStatus => handleGetRecordingStatus(msg)
case msg: VoiceRecording => handleVoiceRecording(msg)
case msg: EndMeeting => handleEndMeeting(msg)
case StopMeetingActor => exit
case _ => // do nothing
}
}
}
case msg: SendWhiteboardAnnotationRequest => handleSendWhiteboardAnnotationRequest(msg)
case msg: GetWhiteboardShapesRequest => handleGetWhiteboardShapesRequest(msg)
case msg: ClearWhiteboardRequest => handleClearWhiteboardRequest(msg)
case msg: UndoWhiteboardRequest => handleUndoWhiteboardRequest(msg)
case msg: EnableWhiteboardRequest => handleEnableWhiteboardRequest(msg)
case msg: IsWhiteboardEnabledRequest => handleIsWhiteboardEnabledRequest(msg)
case msg: SetRecordingStatus => handleSetRecordingStatus(msg)
case msg: GetRecordingStatus => handleGetRecordingStatus(msg)
case msg: VoiceRecording => handleVoiceRecording(msg)
case msg: EndMeeting => handleEndMeeting(msg)
case StopMeetingActor => exit
case _ => // do nothing
}
}
}
def hasMeetingEnded():Boolean = {
meetingEnded

View File

@ -80,9 +80,6 @@ trait UsersApp {
//send the presentation
this ! (new GetPresentationInfo(meetingID, msg.userId, replyTo))
//send the whiteboard
//this ! (new GetWhiteboardShapesNoIdRequest(meetingID, msg.userId, replyTo))
}
case None => outGW.send(new ValidateAuthTokenReply(meetingID, msg.userId, msg.token, false, msg.correlationId))
}

View File

@ -28,20 +28,20 @@ trait WhiteboardApp {
} else if ((WhiteboardKeyUtil.PENCIL_TYPE == shapeType)
&& (WhiteboardKeyUtil.DRAW_START_STATUS == status)) {
println("Received pencil draw start status")
wbModel.addAnnotation(wbId, shape)
wbModel.addAnnotation(wbId, shape)
} else if ((WhiteboardKeyUtil.DRAW_END_STATUS == status)
&& ((WhiteboardKeyUtil.RECTANGLE_TYPE == shapeType)
|| (WhiteboardKeyUtil.ELLIPSE_TYPE == shapeType)
|| (WhiteboardKeyUtil.TRIANGLE_TYPE == shapeType)
|| (WhiteboardKeyUtil.LINE_TYPE == shapeType))) {
|| (WhiteboardKeyUtil.TRIANGLE_TYPE == shapeType)
|| (WhiteboardKeyUtil.LINE_TYPE == shapeType))) {
println("Received [" + shapeType +"] draw end status")
wbModel.addAnnotation(wbId, shape)
wbModel.addAnnotation(wbId, shape)
} else if (WhiteboardKeyUtil.TEXT_TYPE == shapeType) {
println("Received [" + shapeType +"] modify text status")
wbModel.modifyText(wbId, shape)
} else {
println("Received UNKNOWN whiteboard shape!!!!. status=[" + status + "], shapeType=[" + shapeType + "]")
}
println("Received [" + shapeType +"] modify text status")
wbModel.modifyText(wbId, shape)
} else {
println("Received UNKNOWN whiteboard shape!!!!. status=[" + status + "], shapeType=[" + shapeType + "]")
}
wbModel.getWhiteboard(wbId) foreach {wb =>
println("WhiteboardApp::handleSendWhiteboardAnnotationRequest - num shapes [" + wb.shapes.length + "]")
@ -52,20 +52,20 @@ trait WhiteboardApp {
}
def handleGetWhiteboardShapesRequest(msg: GetWhiteboardShapesRequest) {
println("WB: Received page history [" + msg.whiteboardId + "]")
println("WB: Received page history [" + msg.whiteboardId + "]")
wbModel.history(msg.whiteboardId) foreach {wb =>
outGW.send(new GetWhiteboardShapesReply(meetingID, recorded,
msg.requesterID, wb.id, wb.shapes.toArray, msg.replyTo))
msg.requesterID, wb.id, wb.shapes.toArray, msg.replyTo))
}
}
}
def handleClearWhiteboardRequest(msg: ClearWhiteboardRequest) {
println("WB: Received clear whiteboard")
println("WB: Received clear whiteboard")
wbModel.clearWhiteboard(msg.whiteboardId)
wbModel.getWhiteboard(msg.whiteboardId) foreach {wb =>
outGW.send(new ClearWhiteboardEvent(meetingID, recorded,
msg.requesterID, wb.id))
}
msg.requesterID, wb.id))
}
}
def handleUndoWhiteboardRequest(msg: UndoWhiteboardRequest) {

View File

@ -39,5 +39,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<bean id="chat.service" class="org.bigbluebutton.conference.service.chat.ChatService">
<property name="chatApplication"> <ref local="chatApplication"/></property>
</bean>
<bean id="chatMessageListener" class="org.bigbluebutton.conference.service.chat.ChatMessageListener">
<property name="bigBlueButtonInGW" ref="bbbInGW" />
</bean>
</beans>

View File

@ -3,7 +3,7 @@
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
Copyright (c) 2014 BigBlueButton Inc. and by respective authors (see below).
This program is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
@ -51,12 +51,4 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<bean id="presentationMessageListener" class="org.bigbluebutton.conference.service.presentation.PresentationMessageListener">
<property name="conversionUpdatesProcessor" ref="conversionUpdatesProcessor" />
</bean>
<bean id="chatMessageListener" class="org.bigbluebutton.conference.service.chat.ChatMessageListener">
<property name="bigBlueButtonInGW" ref="bbbInGW" />
</bean>
<bean id="participantsListener" class="org.bigbluebutton.conference.service.participants.ParticipantsListener">
<property name="bigBlueButtonInGW" ref="bbbInGW" />
</bean>
</beans>

View File

@ -3,7 +3,7 @@
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
Copyright (c) 2014 BigBlueButton Inc. and by respective authors (see below).
This program is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
@ -26,7 +26,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd
">
<bean id="participantsHandler" class="org.bigbluebutton.conference.service.participants.ParticipantsHandler">
<property name="participantsApplication"> <ref local="participantsApplication"/></property>
</bean>
@ -38,5 +38,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<bean id="participants.service" class="org.bigbluebutton.conference.service.participants.ParticipantsService">
<property name="participantsApplication"> <ref local="participantsApplication"/></property>
</bean>
<bean id="participantsListener" class="org.bigbluebutton.conference.service.participants.ParticipantsListener">
<property name="bigBlueButtonInGW" ref="bbbInGW" />
</bean>
</beans>

View File

@ -3,7 +3,7 @@
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
Copyright (c) 2014 BigBlueButton Inc. and by respective authors (see below).
This program is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
@ -26,13 +26,17 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd
">
<bean id="whiteboardApplication" class="org.bigbluebutton.conference.service.whiteboard.WhiteboardApplication">
<property name="bigBlueButtonInGW"> <ref bean="bbbInGW"/></property>
<property name="bigBlueButtonInGW"> <ref bean="bbbInGW"/></property>
</bean>
<bean id="whiteboard.service" class="org.bigbluebutton.conference.service.whiteboard.WhiteboardService">
<property name="whiteboardApplication"> <ref local="whiteboardApplication"/></property>
</bean>
<bean id="whiteboardListener" class="org.bigbluebutton.conference.service.whiteboard.WhiteboardListener">
<property name="bigBlueButtonInGW"> <ref bean="bbbInGW"/></property>
</bean>
</beans>

View File

@ -3,7 +3,7 @@
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
Copyright (c) 2014 BigBlueButton Inc. and by respective authors (see below).
This program is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
@ -26,35 +26,35 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd
">
<bean id="redisMessageSender" class="org.bigbluebutton.conference.service.messaging.redis.MessageSender"
init-method="start" destroy-method="stop">
<property name="redisPool"> <ref bean="redisPool"/></property>
</bean>
<property name="redisPool"> <ref bean="redisPool"/></property>
</bean>
<bean id="redisMessageReceiver" class="org.bigbluebutton.conference.service.messaging.redis.MessageReceiver"
init-method="start" destroy-method="stop">
<property name="redisPool"> <ref bean="redisPool"/></property>
<property name="messageHandler"> <ref local="redisMessageHandler"/> </property>
</bean>
<property name="redisPool"> <ref bean="redisPool"/></property>
<property name="messageHandler"> <ref local="redisMessageHandler"/> </property>
</bean>
<bean id="redisMessageHandler" class="org.bigbluebutton.conference.service.messaging.redis.ReceivedMessageHandler"
init-method="start" destroy-method="stop">
<property name="messageDistributor"><ref bean="redisMessageDistributor" /></property>
</bean>
<bean id="redisMessageDistributor" class="org.bigbluebutton.conference.service.messaging.redis.MessageDistributor">
<property name="messageHandler"> <ref local="redisMessageHandler"/> </property>
<property name="messageListeners">
<set>
<ref bean="presentationMessageListener" />
<ref bean="chatMessageListener" />
<ref bean="meetingMessageHandler" />
<ref bean="pollMessageHandler" />
<ref bean="participantsListener" />
</set>
</property>
</bean>
<bean id="redisMessageDistributor" class="org.bigbluebutton.conference.service.messaging.redis.MessageDistributor">
<property name="messageHandler"> <ref local="redisMessageHandler"/> </property>
<property name="messageListeners">
<set>
<ref bean="presentationMessageListener" />
<ref bean="chatMessageListener" />
<ref bean="meetingMessageHandler" />
<ref bean="pollMessageHandler" />
<ref bean="participantsListener" />
<ref bean="whiteboardListener" />
</set>
</property>
</bean>
</beans>

View File

@ -33,6 +33,7 @@ config.redis.channels.toBBBApps.pattern = "bigbluebutton:to-bbb-apps:*"
config.redis.channels.toBBBApps.chat = "bigbluebutton:to-bbb-apps:chat"
config.redis.channels.toBBBApps.meeting = "bigbluebutton:to-bbb-apps:meeting"
config.redis.channels.toBBBApps.users = "bigbluebutton:to-bbb-apps:users"
config.redis.channels.toBBBApps.whiteboard = "bigbluebutton:to-bbb-apps:whiteboard"
config.redis.internalChannels = {}
config.redis.internalChannels.receive = "html5-receive"
config.redis.internalChannels.reply = "html5-reply"

View File

@ -97,6 +97,7 @@ module.exports = class RedisPubSub
if message.header?.name is 'get_presentation_info_reply'
#filter for the current=true page on the server-side
currentPage = null
numCurrentPage = null
presentations = message.payload?.presentations
for presentation in presentations
@ -105,6 +106,23 @@ module.exports = class RedisPubSub
for page in pages
if page.current is true
currentPage = page
numCurrentPage = page.num
console.log "\n\n\n\n the message is: " + JSON.stringify message
console.log "\n" + message.payload?.presentations[0]?.id + "/" + numCurrentPage + "\n\n"
#request the whiteboard information
requestMessage = {
"payload": {
"meeting_id": message.payload?.meeting_id
"requester_id": message.payload?.requester_id
"whiteboard_id": message.payload?.presentations[0]?.id + "/" + numCurrentPage #not sure if always [0]
},
"header": {
"timestamp": new Date().getTime()
"name": "get_whiteboard_shapes_request"
}
}
@publishing(config.redis.channels.toBBBApps.whiteboard, requestMessage)
#strip off excess data, leaving only the current slide information
message.payload.currentPage = currentPage

View File

@ -5,7 +5,6 @@ define [
'cs!models/user'
], (_, Backbone, globals, UserModel) ->
# TODO: this class should actually store UserModel's, for now it is only trigerring events
UsersCollection = Backbone.Collection.extend
model: UserModel

View File

@ -19,12 +19,8 @@ define [
@username = @getUrlVars()["username"]
globals.meetingName = decodeURI(@getUrlVars()["meetingName"])
disconnect: =>
alert( " i go through disconnect")
if @socket?
#@socket.disconnect()
else
console.log "tried to disconnect but it's not connected"
disconnect: ->
alert( " i go through disconnect") # not used right now
connect: ->
console.log("user_id=" + @userId + " auth_token=" + @authToken + " meeting_id=" + @meetingId)
@ -58,12 +54,10 @@ define [
console.log "socket.io received: data"
globals.events.trigger("message", data)
# Immediately say we are connected
@socket.on "connect", =>
console.log "socket on: connect"
globals.events.trigger("connection:connected")
#@socket.emit "user connect" # tell the server we have a new user
message = {
"payload": {
@ -81,9 +75,11 @@ define [
if @authToken? and @userId? and @meetingId?
@socket.emit "message", message
# Received a list of users from bbb-apps
# param {object} message object
@socket.on "get_users_reply", (message) =>
requesterId = message.payload?.requester_id
if(requesterId is @userId)
users = []
for user in message.payload?.users
@ -91,6 +87,8 @@ define [
globals.events.trigger("connection:load_users", users)
# Received a the chat history for a meeting
# @param {object} message object
@socket.on "get_chat_history_reply", (message) =>
requesterId = message.payload?.requester_id
if(requesterId is @userId)
@ -111,83 +109,91 @@ define [
globals.events.trigger("connection:disconnected")
@socket = null
@socket.on "reconnect", ->
console.log "socket on: reconnect"
globals.events.trigger("connection:reconnect")
#@socket.on "reconnect", ->
# console.log "socket on: reconnect"
# globals.events.trigger("connection:reconnect")
@socket.on "reconnecting", ->
console.log "socket on: reconnecting"
globals.events.trigger("connection:reconnecting")
#@socket.on "reconnecting", ->
# console.log "socket on: reconnecting"
# globals.events.trigger("connection:reconnecting")
@socket.on "reconnect_failed", ->
console.log "socket on: reconnect_failed"
globals.events.trigger("connection:reconnect_failed")
#@socket.on "reconnect_failed", ->
# console.log "socket on: reconnect_failed"
# globals.events.trigger("connection:reconnect_failed")
# If an error occurs while not connected
# @param {string} reason Reason for the error.
@socket.on "error", (reason) ->
console.error "unable to connect socket.io", reason
#@socket.on "error", (reason) -> #TODO
# console.error "unable to connect socket.io", reason
# Received event to update all the slide images
# @param {Array} urls list of URLs to be added to the paper (after old images are removed)
@socket.on "all_slides", (allSlidesEventObject) =>
console.log "socket on: all_slides"
console.log "allSlidesEventObject: " + allSlidesEventObject
globals.events.trigger("connection:all_slides", allSlidesEventObject);
#@socket.on "all_slides", (allSlidesEventObject) =>
# console.log "socket on: all_slides"
# console.log "allSlidesEventObject: " + allSlidesEventObject
# globals.events.trigger("connection:all_slides", allSlidesEventObject);
# Received event to clear the whiteboard shapes
@socket.on "clrPaper",=>
console.log "socket on: clrPaper"
globals.events.trigger("connection:clrPaper")
#@socket.on "clrPaper",=>
# console.log "socket on: clrPaper"
# globals.events.trigger("connection:clrPaper")
# Received event to update all the shapes in the whiteboard
# @param {Array} shapes Array of shapes to be drawn
@socket.on "allShapes", (allShapesEventObject) =>
console.log "socket on: all_shapes" + allShapesEventObject
globals.events.trigger("connection:all_shapes", allShapesEventObject)
#@socket.on "allShapes", (allShapesEventObject) =>
# # check for the requester_id
# console.log "socket on: all_shapes" + allShapesEventObject
# globals.events.trigger("connection:all_shapes", allShapesEventObject)
# Received event to update all the shapes in the whiteboard
# @param {Array} shapes Array of shapes to be drawn
#@socket.on "get_whiteboard_shapes_reply", (object) =>
# if @userId is object.payload?.requester_id
# #alert("I am getting some shapes reply" + JSON.stringify object)
# for shape in object.payload?.shapes
# #alert("for a shape:")
# shape_type = shape.shape_type
# globals.events.trigger("connection:whiteboard_draw_event", shape_type, shape.shape) # TODO to change the name
# globals.events.trigger("connection:updShape", shape_type, shape.shape)
# Received event to update a shape being created
# @param {string} shape type of shape being updated
# @param {Array} data all information to update the shape
@socket.on "whiteboard_update_event", (data) =>
console.log "socket on: whiteboard_update_event"
shape = data.payload.shape_type
@socket.on "send_whiteboard_shape_message", (data) =>
alert "send_whiteboard_shape_message" + JSON.stringify data
shape = data.payload.shape.shape_type
for point in data.payload.shape.shape.points
point = point/100 #early attempt to scale down
globals.events.trigger("connection:whiteboard_draw_event", shape, data)
globals.events.trigger("connection:updShape", shape, data)
# Received event to create a shape on the whiteboard
# @param {string} shape type of shape being made
# @param {Array} data all information to make the shape
@socket.on "whiteboard_draw_event", (data) =>
console.log "socket on: whiteboard_draw_event"
shape = data.payload.shape_type
globals.events.trigger("connection:whiteboard_draw_event", shape, data)
# Pencil drawings are received as points from the server and painted as lines.
@socket.on "whiteboardDrawPen", (data) =>
console.log "socket on: whiteboardDrawPen"+ data
globals.events.trigger("connection:whiteboardDrawPen", data)
#@socket.on "whiteboardDrawPen", (data) =>
# console.log "socket on: whiteboardDrawPen"+ data
# globals.events.trigger("connection:whiteboardDrawPen", data)
# Received event to update the cursor coordinates
# @param {number} x x-coord of the cursor as a percentage of page width
# @param {number} y y-coord of the cursor as a percentage of page height
@socket.on "mvCur", (data) =>
x = data.cursor.x #TODO change to new json structure
y = data.cursor.y #TODO change to new json structure
console.log "socket on: mvCur"
globals.events.trigger("connection:mvCur", x, y)
#@socket.on "mvCur", (data) =>
# x = data.cursor.x #TODO change to new json structure
# y = data.cursor.y #TODO change to new json structure
# console.log "socket on: mvCur"
# globals.events.trigger("connection:mvCur", x, y)
# Received event to update the zoom or move the slide
# @param {number} x x-coord of the cursor as a percentage of page width
# @param {number} y y-coord of the cursor as a percentage of page height
@socket.on "move_and_zoom", (xOffset, yOffset, widthRatio, heightRatio) =>
console.log "socket on: move_and_zoom"
globals.events.trigger("connection:move_and_zoom", xOffset, yOffset, widthRatio, heightRatio)
#@socket.on "move_and_zoom", (xOffset, yOffset, widthRatio, heightRatio) =>
# console.log "socket on: move_and_zoom"
# globals.events.trigger("connection:move_and_zoom", xOffset, yOffset, widthRatio, heightRatio)
# Received event to update the slide image
# @param {string} url URL of image to show
@socket.on "changeslide", (url) =>
console.log "socket on: changeslide"
globals.events.trigger("connection:changeslide", url)
#@socket.on "changeslide", (url) =>
# console.log "socket on: changeslide"
# globals.events.trigger("connection:changeslide", url)
# Received event to update the viewBox value
# @param {string} xperc Percentage of x-offset from top left corner
@ -195,50 +201,50 @@ define [
# @param {string} wperc Percentage of full width of image to be displayed
# @param {string} hperc Percentage of full height of image to be displayed
# TODO: not tested yet
@socket.on "viewBox", (xperc, yperc, wperc, hperc) =>
console.log "socket on: viewBox"
globals.events.trigger("connection:viewBox", xperc, yperc, wperc, hperc)
#@socket.on "viewBox", (xperc, yperc, wperc, hperc) =>
# console.log "socket on: viewBox"
# globals.events.trigger("connection:viewBox", xperc, yperc, wperc, hperc)
# Received event to update the zoom level of the whiteboard.
# @param {number} delta amount of change in scroll wheel
@socket.on "zoom", (delta) ->
console.log "socket on: zoom"
globals.events.trigger("connection:zoom", delta)
#@socket.on "zoom", (delta) ->
# console.log "socket on: zoom"
# globals.events.trigger("connection:zoom", delta)
# Received event to update the whiteboard size and position
# @param {number} cx x-offset from top left corner as percentage of original width of paper
# @param {number} cy y-offset from top left corner as percentage of original height of paper
# @param {number} sw slide width as percentage of original width of paper
# @param {number} sh slide height as a percentage of original height of paper
@socket.on "paper", (cx, cy, sw, sh) ->
console.log "socket on: paper"
globals.events.trigger("connection:paper", cx, cy, sw, sh)
#@socket.on "paper", (cx, cy, sw, sh) ->
# console.log "socket on: paper"
# globals.events.trigger("connection:paper", cx, cy, sw, sh)
# Received event when the panning action finishes
@socket.on "panStop", ->
console.log "socket on: panStop"
globals.events.trigger("connection:panStop")
#@socket.on "panStop", ->
# console.log "socket on: panStop"
# globals.events.trigger("connection:panStop")
# Received event to denote when the text has been created
@socket.on "textDone", ->
console.log "socket on: textDone"
globals.events.trigger("connection:textDone")
#@socket.on "textDone", ->
# console.log "socket on: textDone"
# globals.events.trigger("connection:textDone")
# Received event to update the status of the upload progress
# @param {string} message update message of status of upload progress
# @param {boolean} fade true if you wish the message to automatically disappear after 3 seconds
@socket.on "uploadStatus", (message, fade) =>
console.log "socket on: uploadStatus"
globals.events.trigger("connection:uploadStatus", message, fade)
#@socket.on "uploadStatus", (message, fade) =>
# console.log "socket on: uploadStatus"
# globals.events.trigger("connection:uploadStatus", message, fade)
# Received event for a user list change
# @param {Array} users Array of names and publicIDs of connected users
# TODO: event name with spaces is bad
@socket.on "user list change", (users) =>
console.log "socket on: user list change"
globals.events.trigger("connection:user_list_change", users)
#@socket.on "user list change", (users) =>
# console.log "socket on: user list change"
# globals.events.trigger("connection:user_list_change", users)
# Received event for a new user
@socket.on "user_joined_message", (message) =>
@ -253,9 +259,9 @@ define [
# Received event to set the presenter to a user
# @param {string} userID publicID of the user that is being set as the current presenter
@socket.on "setPresenter", (userid) =>
console.log "socket on: setPresenter"
globals.events.trigger("connection:setPresenter", userid)
#@socket.on "setPresenter", (userid) =>
# console.log "socket on: setPresenter"
# globals.events.trigger("connection:setPresenter", userid)
# Received event to update all the messages in the chat box
# @param {Array} messages Array of messages in public chat box
@ -276,16 +282,12 @@ define [
@socket.emit "mvCur", x, y
# Requests the shapes from the server.
emitAllShapes: ->
@socket.emit "all_shapes"
#emitAllShapes: ->
# @socket.emit "all_shapes"
# Emit a message to the server
# @param {string} the message
# Emit a chat message to the server
# @param {string} the chat message
emitMsg: (msg) ->
console.log "emitting message: " + msg
object = {
"header": {
"name": "send_public_chat_message"
@ -312,28 +314,28 @@ define [
@socket.emit "message", object
# Emit the finish of a text shape
emitTextDone: ->
@socket.emit "textDone"
#emitTextDone: ->
# @socket.emit "textDone"
# Emit the creation of a shape
# @param {string} shape type of shape
# @param {Array} data all the data required to draw the shape on the client whiteboard
emitMakeShape: (shape, data) ->
@socket.emit "makeShape", shape, data
#emitMakeShape: (shape, data) ->
# @socket.emit "makeShape", shape, data
# Emit the update of a shape
# @param {string} shape type of shape
# @param {Array} data all the data required to update the shape on the client whiteboard
emitUpdateShape: (shape, data) ->
@socket.emit "updShape", shape, data
#emitUpdateShape: (shape, data) ->
# @socket.emit "updShape", shape, data
# Emit an update in the whiteboard position/size values
# @param {number} cx x-offset from top left corner as percentage of original width of paper
# @param {number} cy y-offset from top left corner as percentage of original height of paper
# @param {number} sw slide width as percentage of original width of paper
# @param {number} sh slide height as a percentage of original height of paper
emitPaperUpdate: (cx, cy, sw, sh) ->
@socket.emit "paper", cx, cy, sw, sh
#emitPaperUpdate: (cx, cy, sw, sh) ->
# @socket.emit "paper", cx, cy, sw, sh
# Update the zoom level for the clients
# @param {number} delta amount of change in scroll wheel
@ -341,12 +343,12 @@ define [
@socket.emit "zoom", delta
# Request the next slide
emitNextSlide: ->
@socket.emit "nextslide"
#emitNextSlide: ->
# @socket.emit "nextslide"
# Request the previous slide
emitPreviousSlide: ->
@socket.emit "prevslide"
#emitPreviousSlide: ->
# @socket.emit "prevslide"
# Logout of the meeting
emitLogout: ->
@ -363,37 +365,36 @@ define [
}
@socket.emit "message", message
@socket.disconnect()
#@disconnect()
#Utils.postToUrl "logout"
#window.location.replace "./"
# Emit panning has stopped
emitPanStop: ->
@socket.emit "panStop"
#emitPanStop: ->
# @socket.emit "panStop"
# Publish a shape to the server to be saved
# @param {string} shape type of shape to be saved
# @param {Array} data information about shape so that it can be recreated later
emitPublishShape: (shape, data) ->
@socket.emit "saveShape", shape, JSON.stringify(data)
#emitPublishShape: (shape, data) ->
# @socket.emit "saveShape", shape, JSON.stringify(data)
# Emit a change in the current tool
# @param {string} tool [description]
emitChangeTool: (tool) ->
@socket.emit "changeTool", tool
#emitChangeTool: (tool) ->
# @socket.emit "changeTool", tool
# Tell the server to undo the last shape
emitUndo: ->
@socket.emit "undo"
#emitUndo: ->
# @socket.emit "undo"
# Emit a change in the presenter
emitSetPresenter: (id) ->
@socket.emit "setPresenter", id
#emitSetPresenter: (id) ->
# @socket.emit "setPresenter", id
# Emit signal to clear the canvas
emitClearCanvas: (id) ->
@socket.emit "clrPaper", id
#emitClearCanvas: (id) ->
# @socket.emit "clrPaper", id
# Helper method to get the meeting_id, user_id and auth_token from the url
getUrlVars: ->

View File

@ -23,6 +23,7 @@ define [
# Container must be a DOM element
initialize: (@container) ->
alert("initializing the paper model")
# a WhiteboardCursorModel
@cursor = null
@ -278,7 +279,7 @@ define [
@cursor.undrag()
@currentLine = @_createTool(tool)
@cursor.drag(@currentLine.dragOnMove, @currentLine.dragOnStart, @currentLine.dragOnEnd)
when "rect"
when "rectangle"
@cursor.undrag()
@currentRect = @_createTool(tool)
@cursor.drag(@currentRect.dragOnMove, @currentRect.dragOnStart, @currentRect.dragOnEnd)
@ -351,6 +352,7 @@ define [
# Draws an array of shapes to the paper.
# @param {array} shapes the array of shapes to draw
drawListOfShapes: (shapes) ->
alert("drawListOfShapes" + shapes.length)
@currentShapesDefinitions = shapes
@currentShapes = @raphaelObj.set()
for shape in shapes
@ -392,6 +394,7 @@ define [
# Make a shape `shape` with the data in `data`.
makeShape: (shape, data) ->
console.log("shape=" + shape + " data=" + JSON.stringify data)
tool = null
switch shape
when "path", "line"
@ -409,6 +412,7 @@ define [
when "triangle"
@currentTriangle = @_createTool(shape)
toolModel = @currentTriangle
toolModel.draw(tool, data)
tool = @currentTriangle.make(data)
when "text"
@currentText = @_createTool(shape)
@ -417,7 +421,12 @@ define [
else
console.log "shape not recognized at makeShape", shape
if tool?
@currentShapes.push(tool)
alert("in currentShapes")
if @currentShapes? #rewrite TODO
@currentShapes.push(tool)
else
@currentShapes = []
@currentShapes.push(tool)
@currentShapesDefinitions.push(toolModel.getDefinition())
# Update the cursor position on screen
@ -515,6 +524,7 @@ define [
globals.events.on "connection:allShapes", (allShapesEventObject) =>
# TODO: a hackish trick for making compatible the shapes from redis with the node.js
alert("on connection:allShapes:" + JSON.stringify allShapesEventObject)
shapes = allShapesEventObject.shapes
for shape in shapes
properties = JSON.parse(shape.data)

View File

@ -24,16 +24,16 @@ define [
# @param {string} colour the colour of the object
# @param {number} thickness the thickness of the object's line(s)
make: (startingData) ->
console.log "make startingData"+ startingData
x = startingData.payload.data.coordinate.first_x
y = startingData.payload.data.coordinate.first_y
color = startingData.payload.data.line.color
thickness = startingData.payload.data.line.weight
console.log "make startingData"#+ JSON.stringify startingData
x = startingData.payload.shape.shape.points[0]
y = startingData.payload.shape.shape.points[1]
color = startingData.payload.shape.shape.color
thickness = startingData.payload.shape.shape.thickness
@obj = @paper.rect(x * @gw + @xOffset, y * @gh + @yOffset, 0, 0, 1)
@obj.attr Utils.strokeAndThickness(color, thickness)
@definition =
shape: "rect"
shape: "rectangle"
data: [x, y, 0, 0, @obj.attrs["stroke"], @obj.attrs["stroke-width"]]
@obj
@ -44,11 +44,11 @@ define [
# @param {number} y2 the y value of the bottom right corner
# @param {boolean} square (draw a square or not)
update: (startingData) ->
x1 = startingData.payload.data.coordinate.first_x
y1 = startingData.payload.data.coordinate.first_y
x2 = startingData.payload.data.coordinate.last_x
y2 = startingData.payload.data.coordinate.last_y
square = startingData.payload.data.square
x1 = startingData.payload.shape.shape.points[0]
y1 = startingData.payload.shape.shape.points[1]
x2 = startingData.payload.shape.shape.points[2]
y2 = startingData.payload.shape.shape.points[3]
square = startingData.payload.shape.shape.square
if @obj?
[x1, x2] = [x2, x1] if x2 < x1
[x1, x2] = [x2, x1] if x2 < x1

View File

@ -72,8 +72,6 @@ define [
# Removes all a user from the list #TODO - for now it does not remove but moves to the left hand side
_removeUserByID: (userID)->
@$("#user-"+userID).remove()
#@$("#user-"+userID).parent().context.hidden = "true"
#console.log @$el.children("ul")
# Marks a user as selected when clicked.
_userClicked: (e) ->

View File

@ -1,10 +1,10 @@
<li class="user-wrapper">
<div class="row">
<div class="user-role col-md-1" id="user-<%= userID %>"><i class="icon fa fa-user"></i></div>
<div class="user-name col-md-5"><%= username %></div>
<div class="user-video col-md-1"><i class="icon fa fa-video-camera"></i></div>
<div class="user-audio col-md-1"><i class="icon fa fa-microphone"></i></div>
<div class="user-kick col-md-1"><i class="icon fa fa-sign-out"></i></div>
<div class="clearfix"></div>
</div>
</li>
<li class="user-wrapper" id="user-<%= userID %>">
<div class="row">
<div class="user-role col-md-1"><i class="icon fa fa-user"></i></div>
<div class="user-name col-md-5"><%= username %></div>
<div class="user-video col-md-1"><i class="icon fa fa-video-camera"></i></div>
<div class="user-audio col-md-1"><i class="icon fa fa-microphone"></i></div>
<div class="user-kick col-md-1"><i class="icon fa fa-sign-out"></i></div>
<div class="clearfix"></div>
</div>
</li>

View File

@ -36,6 +36,8 @@ Versions:
- Now using Zend coding, naming and style conventions
- Refactored methods to accept standardized parameters & match BBB API structure
-- See included samples for usage examples
1.4 -- Updated by xaker1
(email : admin [a t ] xaker1 DOT ru)
*/
/* _______________________________________________________________________*/
@ -60,7 +62,7 @@ class BigBlueButton {
$this->_bbbServerBaseUrl = CONFIG_SERVER_BASE_URL;
}
private function _processXmlResponse($url){
private function _processXmlResponse($url, $xml = ''){
/*
A private utility method used by other public methods to process XML responses.
*/
@ -71,6 +73,16 @@ class BigBlueButton {
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $timeout);
if(!empty($xml)){
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-type: application/xml',
'Content-length: ' . strlen($xml)
));
}
$data = curl_exec( $ch );
curl_close( $ch );
@ -79,10 +91,13 @@ class BigBlueButton {
else
return false;
}
if(!empty($xml))
throw new Exception('Set xml, but curl does not installed.');
return (simplexml_load_file($url));
}
private function _requiredParam($param) {
private function _requiredParam($param, $name = '') {
/* Process required params and throw errors if we don't get values */
if ((isset($param)) && ($param != '')) {
return $param;
@ -91,7 +106,7 @@ class BigBlueButton {
throw new Exception('Missing parameter.');
}
else {
throw new Exception(''.$param.' is required.');
throw new Exception(''.$name.' is required.');
}
}
@ -119,8 +134,8 @@ class BigBlueButton {
USAGE:
(see $creationParams array in createMeetingArray method.)
*/
$this->_meetingId = $this->_requiredParam($creationParams['meetingId']);
$this->_meetingName = $this->_requiredParam($creationParams['meetingName']);
$this->_meetingId = $this->_requiredParam($creationParams['meetingId'], 'meetingId');
$this->_meetingName = $this->_requiredParam($creationParams['meetingName'], 'meetingName');
// Set up the basic creation URL:
$creationUrl = $this->_bbbServerBaseUrl."api/create?";
// Add params:
@ -144,7 +159,7 @@ class BigBlueButton {
return ( $creationUrl.$params.'&checksum='.sha1("create".$params.$this->_securitySalt) );
}
public function createMeetingWithXmlResponseArray($creationParams) {
public function createMeetingWithXmlResponseArray($creationParams, $xml = '') {
/*
USAGE:
$creationParams = array(
@ -162,8 +177,9 @@ class BigBlueButton {
'duration' => '0', -- Default = 0 which means no set duration in minutes. [number]
'meta_category' => '', -- Use to pass additional info to BBB server. See API docs to enable.
);
$xml = ''; -- Use to pass additional xml to BBB server. Example, use to Preupload Slides. See API docs.
*/
$xml = $this->_processXmlResponse($this->getCreateMeetingURL($creationParams));
$xml = $this->_processXmlResponse($this->getCreateMeetingURL($creationParams), $xml);
if($xml) {
if($xml->meetingID)
@ -204,9 +220,9 @@ class BigBlueButton {
'webVoiceConf' => '' -- OPTIONAL - string
);
*/
$this->_meetingId = $this->_requiredParam($joinParams['meetingId']);
$this->_username = $this->_requiredParam($joinParams['username']);
$this->_password = $this->_requiredParam($joinParams['password']);
$this->_meetingId = $this->_requiredParam($joinParams['meetingId'], 'meetingId');
$this->_username = $this->_requiredParam($joinParams['username'], 'username');
$this->_password = $this->_requiredParam($joinParams['password'], 'password');
// Establish the basic join URL:
$joinUrl = $this->_bbbServerBaseUrl."api/join?";
// Add parameters to the URL:
@ -231,8 +247,8 @@ class BigBlueButton {
'password' => 'mp' -- REQUIRED - The moderator password for the meeting
);
*/
$this->_meetingId = $this->_requiredParam($endParams['meetingId']);
$this->_password = $this->_requiredParam($endParams['password']);
$this->_meetingId = $this->_requiredParam($endParams['meetingId'], 'meetingId');
$this->_password = $this->_requiredParam($endParams['password'], 'password');
$endUrl = $this->_bbbServerBaseUrl."api/end?";
$params =
'meetingID='.urlencode($this->_meetingId).
@ -272,7 +288,7 @@ class BigBlueButton {
/* USAGE:
$meetingId = '1234' -- REQUIRED - The unique id for the meeting
*/
$this->_meetingId = $this->_requiredParam($meetingId);
$this->_meetingId = $this->_requiredParam($meetingId, 'meetingId');
$runningUrl = $this->_bbbServerBaseUrl."api/isMeetingRunning?";
$params =
'meetingID='.urlencode($this->_meetingId);
@ -363,8 +379,8 @@ class BigBlueButton {
'password' => 'mp' -- REQUIRED - The moderator password for the meeting
);
*/
$this->_meetingId = $this->_requiredParam($infoParams['meetingId']);
$this->_password = $this->_requiredParam($infoParams['password']);
$this->_meetingId = $this->_requiredParam($infoParams['meetingId'], 'meetingId');
$this->_password = $this->_requiredParam($infoParams['password'], 'password');
$infoUrl = $this->_bbbServerBaseUrl."api/getMeetingInfo?";
$params =
'meetingID='.urlencode($this->_meetingId).

1
labs/demos/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

View File

@ -1,7 +1,7 @@
{
"settings": {
"IP": "http://192.168.0.203",
"IP": "http://192.168.0.232",
"PORT": "4000",
"salt": "74a91f30f165423067bf3039722e33e0"
"salt": "c7faeb82a786bd71134b61833b0ec4af"
}
}
}

View File

@ -20,6 +20,8 @@ login = (req, resp) ->
#calling createapi
bbbapi.create(createParams, serverAndSecret, {}, (errorOuter, responseOuter, bodyOuter) ->
#console.log JSON.stringify(response)
console.log "\n\nouterXML=" + responseOuter.body
console.log "\nerrorOuter=" + JSON.stringify errorOuter
bbbapi.join(joinParams, serverAndSecret, {}, (error, response, body) ->
if error
console.log error
@ -39,7 +41,7 @@ login = (req, resp) ->
"\nuser_id = " + user_id +
"\nauth_token = " + auth_token
url = "#{configJson.settings.IP}:3000/html5.client?meeting_id=" + meeting_id + "&user_id=" +
url = "#{configJson.settings.IP}:3000/meeting_id=" + meeting_id + "&user_id=" +
user_id + "&auth_token=" + auth_token + "&username=" + joinParams.fullName + "&meetingName=" + joinParams.meetingID
json =

View File

@ -17,13 +17,13 @@ createParams.attendeePW = "ap"
createParams.moderatorPW = "mp"
createParams.record = false
createParams.voiceBridge = 70827
createParams.name = "Demo Meeting"
createParams.meetingID = "Demo Meeting"
createParams.name = "Demo Meeting9"
createParams.meetingID = "Demo Meeting9"
joinParams = {}
joinParams.password = "mp"
joinParams.fullName = "Richard"
joinParams.meetingID = "Demo Meeting"
joinParams.meetingID = "Demo Meeting9"
joinParams.redirect = false
serverAndSecret = {server: bbbServer, secret: sharedSecret}

1
labs/meteor-client/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
packages

View File

@ -6,8 +6,11 @@
standard-app-packages
autopublish
insecure
bootstrap
less
coffeescript
redis
npm
less
bootstrap-3
iron-router
router
bootstrap

View File

@ -1,8 +0,0 @@
Meteor.navigateTo = (path) ->
Router.go path
Router.configure layoutTemplate: 'layout'
Router.map ->
@route 'main',
path: '/'

View File

@ -23,5 +23,10 @@ Meteor.methods({
},
showUserId: function() {
throw new Meteor.Error(422, this.userId);
},
addToCollection: function(userid, meeting_id) {
var user = {userId: userid, meetingId: meeting_id};
var userId = Meteor.users.insert(user);
console.log("added user id=[" + userId + "] :" + JSON.stringify(user));
}
});

View File

@ -0,0 +1,54 @@
# # Global configurations file
config = {}
# Default global variables
config.appName = 'BigBlueButton HTML5 Client'
config.maxUsernameLength = 30
config.maxChatLength = 140
# the path in which an image of a presentation is stored
config.presentationImagePath = (meetingID, presentationID, filename) ->
"bigbluebutton/presentation/#{meetingID}/#{meetingID}/#{presentationID}/png/#{filename}"
## Application configurations
config.app = {}
# Generate a new secret with:
# $ npm install crypto
# $ coffee
# coffee> crypto = require 'crypto'
# coffee> crypto.randomBytes(32).toString('base64')
config.app.sessionSecret = "J7XSu96KC/B/UPyeGub3J6w6QFXWoUNABVgi9Q1LskE="
# Configs for redis
config.redis = {}
config.redis.host = "127.0.0.1"
config.redis.post = "6379"
config.redis.timeout = 5000
config.redis.channels = {}
config.redis.channels.fromBBBApps = "bigbluebutton:from-bbb-apps:*"
config.redis.channels.toBBBApps = {}
config.redis.channels.toBBBApps.pattern = "bigbluebutton:to-bbb-apps:*"
config.redis.channels.toBBBApps.chat = "bigbluebutton:to-bbb-apps:chat"
config.redis.channels.toBBBApps.meeting = "bigbluebutton:to-bbb-apps:meeting"
config.redis.channels.toBBBApps.users = "bigbluebutton:to-bbb-apps:users"
config.redis.channels.toBBBApps.whiteboard = "bigbluebutton:to-bbb-apps:whiteboard"
config.redis.internalChannels = {}
config.redis.internalChannels.receive = "html5-receive"
config.redis.internalChannels.reply = "html5-reply"
config.redis.internalChannels.publish = "html5-publish"
# Logging
config.log = {}
config.log.path = if process.env.NODE_ENV == "production"
"/var/log/bigbluebutton/bbbnode.log"
else
"./log/development.log"
# Global instance of Modules, created by `app.coffee`
config.modules = null
Meteor.config = config

View File

@ -0,0 +1,34 @@
Meteor.Router.configure layoutTemplate: 'layout'
Meteor.Router.add {
'/': 'main',
'*': (url)->
# Here we want to extract the meeting_id, user_id, auth_token, etc
# from the uri
if url.indexOf("meeting_id") > -1 # if the URL is /meeting_id=...&...
urlParts = url.split("&");
meetingId = urlParts[0]?.split("=")[1];
console.log "meetingId=" + meetingId
userId = urlParts[1]?.split("=")[1];
console.log "userId=" + userId
authToken = urlParts[2]?.split("=")[1];
console.log "authToken=" + authToken
userName = urlParts[3]?.split("=")[1];
console.log "userName=" + userName
if meetingId? and userId? and authToken? and userName?
redisPubSub = new Meteor.RedisPubSub
redisPubSub.sendValidateToken(meetingId, userId, authToken)
'main'
else
console.log "unable to extract from the URL some of {meetingId, userName, userId, authToken}"
else
console.log "unable to extract the required information for the meeting from the URL"
}

View File

@ -0,0 +1,11 @@
{
"request": "2.34.0",
"require": "0.5.0",
"bunyan": "0.22.2",
"socket.io": "1.0.4",
"crypto-js": "3.1.2-3",
"lodash": "1.3.1",
"postal": "0.10.0",
"events": "1.0.1",
"redis": "0.10.3"
}

View File

@ -0,0 +1,30 @@
###
if Meteor.isServer
console.log " I am in the server"
Meteor.startup ->
console.log "On startup in the server"
console.log Meteor.config.appName
#a = new Meteor.ClientProxy()
#b = new Meteor.RedisPubSub()
# Module to store the modules registered in the application
Meteor.config.modules = modules = new Meteor.Modules()
# Router
#config.modules.register "MainRouter", new MainRouter()
# Application modules
Meteor.config.modules.register "RedisPubSub", new Meteor.RedisPubSub()
#Meteor.config.modules.register "MessageBus", new Meteor.MessageBus()
#Meteor.config.modules.register "Controller", new Controller()
clientProxy = new Meteor.ClientProxy()
Meteor.config.modules.register "ClientProxy", clientProxy
###############clientProxy.listen(app)
###

View File

@ -0,0 +1,17 @@
###
bunyan = Meteor.require 'bunyan'
logger = bunyan.createLogger({
name: 'bbbnode',
streams: [
{
level: 'debug',
stream: process.stdout,
},
{
level: 'info',
path: Meteor.config.log.path
}
]
})
###

View File

@ -0,0 +1,93 @@
socketio = Meteor.require('socket.io')
MESSAGE = "message"
moduleDeps = ["Controller"]
class Meteor.ClientProxy
constructor: ->
Meteor.config.modules.wait moduleDeps, =>
@controller = Meteor.config.modules.get("Controller")
# Listens for events on the websocket and does something when they are triggered.
listen: (app) ->
@io = socketio.listen(app)
@io.set('log level', 1)
@io.sockets.on 'connection', (socket) =>
log.debug({ client: socket.id }, "Client has connected.")
socket.on 'message', (jsonMsg) =>
log.debug({ message: jsonMsg }, "Received message")
@_handleMessage(socket, jsonMsg)
socket.on 'disconnect', =>
@_handleClientDisconnected socket
# Sends a message in `data` to all the clients that should receive it.
sendToClients: (data, callback) ->
#log.debug({ data: data }, "Sending to client")
# the channel can be the user_id (send to one user only) or the meeting_id
# (send to everyone in the meeting)
channel = data?.payload?.user_id or data?.payload?.meeting_id
# if the data has "header":{"name":"some_event_name"} use that name
# otherwise look for "name":"some_event_name" in the top level of the data
eventName = data?.header?.name or data?.name
# clients = @io.sockets.clients(channel)
# console.log "Found", clients?.length, "clients for the channel", channel
#log.debug({ channel: channel, eventName: eventName, message: data, clientCount: clients?.length },
# "Sending message to websocket clients")
# TODO: if `channel` is undefined, it should not send the message,
# instead if is sending to all users
@io.sockets.in(channel).emit(eventName, data)
callback?()
_handleClientDisconnected: (socket) ->
console.log "\ntrying to disconnect"
#if socket.userId?
# log.info("User [#{socket.userId}] has disconnected.")
_handleMessage: (socket, message) ->
if message.header?.name?
@_handleValidMessage(socket, message)
else
log.error({ message: message }, "Invalid message.")
_handleValidMessage: (socket, message) =>
switch message.header.name
when 'validate_auth_token'
@_handleLoginMessage socket, message
when 'send_public_chat_message'
@controller.sendingChat message
when 'user_leaving_request'
@controller.sendingUsersMessage message
else
log.error({ message: message }, 'Unknown message name.')
_handleLoginMessage: (socket, data) ->
@controller.processAuthMessage(data, (err, result) ->
if err?
log.debug({ message: result }, "Sending authentication not OK to user and disconnecting socket")
sendMessageToClient(socket, result)
socket.disconnect()
else
log.debug({ userChannel: result.payload.user_id, meetingChannel: result.payload.meeting_id },
"Subscribing a user to his channels")
socket.join(result.payload.user_id)
socket.join(result.payload.meeting_id)
# assign the userId to this socket. This way we can
# locate this socket using the userId.
socket.userId = result?.payload?.user_id
log.debug({ message: result }, "Sending authentication OK reply to user")
sendMessageToClient(socket, result)
)
sendMessageToClient = (socket, message) ->
socket.emit(MESSAGE, message)

View File

@ -0,0 +1,39 @@
moduleDeps = ["MessageBus", "ClientProxy"]
class Meteor.Controller
constructor: ->
config.modules.wait moduleDeps, =>
@messageBus = config.modules.get("MessageBus")
@clientProxy = config.modules.get("ClientProxy")
@messageBus.receiveMessages (data) =>
@processReceivedMessage(data)
processReceivedMessage: (data, callback) ->
@clientProxy.sendToClients(data, callback)
# Processes a message requesting authentication
processAuthMessage: (data, callback) ->
log.info({ data: data }, "Sending an authentication request and waiting for reply")
@messageBus.sendAndWaitForReply data, (err, result) ->
if err?
log.error({ reason: err, result: result, original: data }, "Authentication failure")
callback(err, null)
else
if result.payload?.valid
log.info({ result: result }, "Authentication successful")
callback(null, result)
else
log.info({ result: result }, "Authentication failure")
callback(new Error("Authentication failure"), null)
# processEndMessage: (data, callback) ->
# @clientProxy.endMeeting()
sendingChat: (data) =>
@messageBus.sendingToRedis(config.redis.channels.toBBBApps.chat, data)
sendingUsersMessage: (data) =>
@messageBus.sendingToRedis(config.redis.channels.toBBBApps.users, data)

View File

@ -0,0 +1,40 @@
crypto = Meteor.require 'crypto'
postal = Meteor.require 'postal'
moduleDeps = ["RedisPubSub"]
class Meteor.MessageBus
constructor: ->
Meteor.config.modules.wait moduleDeps, =>
@pubSub = Meteor.config.modules.get("RedisPubSub")
receiveMessages: (callback) ->
postal.subscribe
channel: Meteor.config.redis.internalChannels.receive
topic: "broadcast"
callback: (msg, envelope) ->
callback(msg)
sendAndWaitForReply: (data, callback) ->
replyTo =
channel: Meteor.config.redis.internalChannels.reply
topic: 'get.' + crypto.randomBytes(16).toString('hex')
postal.subscribe(
channel: replyTo.channel
topic: replyTo.topic
callback: (msg, envelope) ->
callback(null, msg)
).once()
log.info({ message: data, replyTo: replyTo }, "Sending a message and waiting for reply")
postal.publish
channel: Meteor.config.redis.internalChannels.publish
topic: 'broadcast'
replyTo: replyTo
data: data
sendingToRedis: (channel, message) =>
@pubSub.publishing(channel, message)

View File

@ -0,0 +1,79 @@
_ = Meteor.require('lodash')
EventEmitter = Meteor.require('events').EventEmitter
# Helper class to register and store modules.
# It stores a list of objects in an object literal indexed by the name of the module.
# Includes methods for a class to ask for a module and wait until it is ready.
#
# This class is used to prevent errors when modules are required in an order that
# would generate a circular dependency. In these cases, the objects might not be loaded
# entirely.
# @see http://nodejs.org/api/modules.html#modules_cycles
#
# With this class, the class requiring a module can wait until the module is properly
# loaded. More than that, it will return an instanced object, not only a reference to
# a class (as usually done when using `require`).
#
# @example How to use it
# modules = new Modules()
# modules.wait ["db"], ->
# # this callback will only be called when the module "db" is registered
# db = config.modules.get("db")
# ...
# # calls to register a module can be made anywhere in the application
# modules.register "db", new Database()
#
class Meteor.Modules extends EventEmitter
constructor: ->
# the list of modules registered:
@modules = {}
# list of callbacks waiting for a module to be registered:
@callbacks = []
# Registers a new module with the name in `name` and the content in `object`.
# @param name [string] the name of the module
# @param object [string] the instance of the module
# @return [object] the same object in the parameter `object`
register: (name, object) ->
@modules[name] = object
@_checkCallbacks()
object
# Blocks until a list of modules is registered.
# @param names [Array] an array of strings with the names of all modules that should be waited for
# @param callback [Function] will be called when *all* modules in `names` are registered
wait: (names, callback) ->
names = [names] unless _.isArray(names)
@callbacks.push {modules: names, fn: callback}
@_checkCallbacks()
# Returns the module with the name in `name`.
# @param name [string] the name of the module to be returned
# @return [object] the module asked for
get: (name) ->
@modules[name]
# Returns the list of all modules registered.
# @return [Array] all modules registered
all: ->
@modules
# Run through the list of registered callbacks in `@callbacks` to see if any of
# them are ready to be called (if all modules waited for are available).
# @private
_checkCallbacks: () ->
toRemove = []
for cb in @callbacks
done = true
for name in cb.modules
unless @modules[name]?
done = false
break
if done
cb.fn()
toRemove.push cb
@callbacks = _.filter(@callbacks, (i) -> i not in toRemove)

View File

@ -0,0 +1,55 @@
class Meteor.RedisPubSub
constructor: () ->
console.log "constructor RedisPubSub"
@pubClient = redis.createClient()
@subClient = redis.createClient()
@subClient.on "psubscribe", Meteor.bindEnvironment(@_onSubscribe )
@subClient.on "pmessage", Meteor.bindEnvironment(@_onMessage)
#log.info
console.log("RPC: Subscribing message on channel: #{Meteor.config.redis.channels.fromBBBApps}")
@subClient.psubscribe(Meteor.config.redis.channels.fromBBBApps)
# Construct and send a message to bbb-web to validate the user
sendValidateToken: (meetingId, userId, authToken) ->
console.log "\n\n i am sending a validate_auth_token with " + userId + "" + meetingId
message = {
"payload": {
"auth_token": authToken
"userid": userId
"meeting_id": meetingId
},
"header": {
"timestamp": new Date().getTime()
"reply_to": meetingId + "/" + userId
"name": "validate_auth_token"
}
}
if authToken? and userId? and meetingId?
@pubClient.publish(Meteor.config.redis.channels.toBBBApps.meeting, JSON.stringify(message))
else
console.log "did not have enough information to send a validate_auth_token message"
_onSubscribe: (channel, count) ->
#Meteor.call("addToCollection");
console.log "Subscribed to #{channel}"
_onMessage: (pattern, channel, jsonMsg) =>
# TODO: this has to be in a try/catch block, otherwise the server will
# crash if the message has a bad format
message = JSON.parse(jsonMsg)
correlationId = message.payload?.reply_to or message.header?.reply_to
unless message.header?.name is "keep_alive_reply"
console.log "\nchannel=" + channel
console.log "correlationId=" + correlationId if correlationId?
console.log "eventType=" + message.header?.name + "\n"
#log.debug({ pattern: pattern, channel: channel, message: message}, "Received a message from redis")
console.log jsonMsg
if message.header?.name is "user_joined_message"
Meteor.call("addToCollection", message.payload.user.userid, message.payload.meeting_id);

View File

@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////////////
// Startup
//
/*
Meteor.startup(function () {
console.log('server start');
// cleanup collections
@ -22,4 +22,5 @@ Meteor.startup(function () {
// Set collection permissions
SetCollectionPermissions();
});
});
*/

View File

@ -1,6 +1,9 @@
{
"packages": {
"redis": {},
"npm": {},
"bootstrap-3": {},
"iron-router": {}
"iron-router": {},
"router": {}
}
}

View File

@ -2,10 +2,23 @@
"meteor": {},
"dependencies": {
"basePackages": {
"redis": {},
"npm": {},
"bootstrap-3": {},
"iron-router": {}
"iron-router": {},
"router": {}
},
"packages": {
"redis": {
"git": "https://github.com/stevemanuel/meteor-redis.git",
"tag": "v0.1.3",
"commit": "8af9aaab84f4a87358ccb4616592a373661f712b"
},
"npm": {
"git": "https://github.com/arunoda/meteor-npm.git",
"tag": "v0.2.6",
"commit": "177ab6118de5bf8cffb19481343d5762ff7a2aaf"
},
"bootstrap-3": {
"git": "https://github.com/mangasocial/meteor-bootstrap-3.git",
"tag": "v3.1.1-1",
@ -16,10 +29,25 @@
"tag": "v0.7.1",
"commit": "d1ffb3f06ea4c112132b030f2eb1a70b81675ecb"
},
"router": {
"git": "https://github.com/tmeasday/meteor-router.git",
"tag": "v0.6.1",
"commit": "8ec75fec7affdefc787d19f23b36fd6d1d44ef04"
},
"blaze-layout": {
"git": "https://github.com/EventedMind/blaze-layout.git",
"tag": "v0.2.4",
"commit": "b40e9b0612329288d75cf52ad14a7da64bb8618f"
},
"page-js-ie-support": {
"git": "https://github.com/tmeasday/meteor-page-js-ie-support.git",
"tag": "v1.3.5",
"commit": "b99ed8380aefd10b2afc8f18d9eed4dd0d8ea9cb"
},
"HTML5-History-API": {
"git": "https://github.com/tmeasday/meteor-HTML5-History-API.git",
"tag": "v4.1.2",
"commit": "b5fca79f9ae8936e5f748a04f475295f4fa1cc7a"
}
}
}