Merge branch 'meteor-new-auth' of github.com:perroned/bigbluebutton into meteor-new-auth

Conflicts:
	labs/meteor-client/app/client/globals.coffee
	labs/meteor-client/app/client/views/chat/chat_bar.coffee
	labs/meteor-client/app/collections/chat.coffee
	labs/meteor-client/app/collections/meetings.coffee
	labs/meteor-client/app/collections/presentations.coffee
	labs/meteor-client/app/collections/shapes.coffee
	labs/meteor-client/app/collections/slides.coffee
	labs/meteor-client/app/collections/users.coffee
This commit is contained in:
Anton Georgiev 2014-10-07 17:45:47 +00:00
commit 319a1247a1
28 changed files with 781 additions and 614 deletions

2
labs/demos/lib/testapi.coffee Normal file → Executable file
View File

@ -26,7 +26,7 @@ createParams.name = tempName
createParams.meetingID = tempName
joinParams = {}
joinParams.password = "ap"
joinParams.password = "mp"
joinParams.fullName = "Richard"
joinParams.meetingID = tempName
joinParams.redirect = false

View File

@ -20,12 +20,12 @@
meet?.meetingName
else null
@getTimeOfJoining = ->
Meteor.Users.findOne({"user.userid": getInSession("userId")})?.user?.time_of_joining
@getTime = -> # returns epoch in ms
(new Date).valueOf()
@getTimeOfJoining = ->
Meteor.Users.findOne(_id: getInSession "DBID")?.user?.time_of_joining
@getUsersName = ->
name = getInSession("userName") # check if we actually have one in the session
if name? then name # great return it, no database query
@ -58,6 +58,14 @@ Handlebars.registerHelper "getCurrentSlide", ->
Handlebars.registerHelper "getCurrentUser", =>
@window.getCurrentUserFromSession()
Handlebars.registerHelper "getHandRaiseStats", ->
numRaised = Meteor.Users.find({'user.raise_hand':true}).fetch().length
total = Meteor.Users.find().fetch().length
percentageRaised = numRaised/total
if numRaised > 0
"#{numRaised} Hands Raised (#{percentageRaised}% of Attendees)"
else false
# Allow access through all templates
Handlebars.registerHelper "getInSession", (k) -> SessionAmplify.get k
@ -134,7 +142,7 @@ Handlebars.registerHelper "messageFontSize", ->
Handlebars.registerHelper "pointerLocation", ->
currentPresentation = Meteor.Presentations.findOne({"presentation.current": true})
currentPresentation.pointer
currentPresentation?.pointer
Handlebars.registerHelper "setInSession", (k, v) -> SessionAmplify.set k, v

View File

@ -29,11 +29,10 @@ Template.header.events
# "click .settingsIcon": (event) ->
# alert "settings"
"click .raiseHand": (event) ->
console.log "navbar raise own hand from client"
Meteor.call('userRaiseHand', getInSession("meetingId"), getInSession("userId"))
"click .lowerHand": (event) ->
# loweredBy = @id # TODO! this must be the userid of the person lowering the hand - instructor/student
loweredBy = getInSession("userId")
Meteor.call('userLowerHand', getInSession("meetingId"), getInSession("userId"), loweredBy)
Meteor.call('userLowerHand', getInSession("meetingId"), getInSession("userId"), getInSession("userId"))
"click .whiteboardIcon": (event) ->
toggleWhiteBoard()
"mouseover #navbarMinimizedButton": (event) ->
@ -47,8 +46,8 @@ Template.recordingStatus.rendered = ->
$('button[rel=tooltip]').tooltip()
Template.main.helpers
setTitle: ->
document.title = "BigBlueButton HTML5"
setTitle: ->
document.title = "BigBlueButton #{window.getMeetingName() ? 'HTML5'}"
Template.makeButton.rendered = ->
$('button[rel=tooltip]').tooltip()

View File

@ -55,7 +55,7 @@
{{/if}}
{{#if isCurrentUserRaisingHand}}
{{> makeButton btn_class="lowerHand navbarIconToggleActive navbarButton" i_class="hand-up" rel="tooltip" data_placement="bottom" title="Lower your hand"}}
{{> makeButton btn_class="lowerHand navbarIconToggleActive navbarButton" i_class="hand-down" rel="tooltip" data_placement="bottom" title="Lower your hand"}}
{{else}}
{{> makeButton btn_class="raiseHand navbarButton" i_class="hand-up" rel="tooltip" data_placement="bottom" title="Raise your hand"}}
{{/if}}

View File

@ -66,6 +66,7 @@ bottomEntry{
width: 80%;
margin-top:6px;
padding:5px;
resize: none;
}
.optionsBar{
padding-top:15px;

View File

@ -1,3 +1,4 @@
#MuteAllUsers{}
#users {
margin-left: 0.5%;
/*min-width:230px;*/

View File

@ -6,12 +6,13 @@
chattingWith = getInSession('inChatWith')
if chattingWith isnt "PUBLIC_CHAT"
dest = Meteor.Users.findOne("userId": chattingWith)
# dest = Meteor.Users.findOne("userId": chattingWith)
dest = Meteor.Users.findOne(_id: chattingWith)
messageForServer = { # construct message for server
"message": message
"chat_type": if chattingWith is "PUBLIC_CHAT" then "PUBLIC_CHAT" else "PRIVATE_CHAT"
"from_userid": getInSession("userId")
"from_userid": getInSession("DBID")
"from_username": getUsersName()
"from_tz_offset": "240"
"to_username": if chattingWith is "PUBLIC_CHAT" then "public_chat_username" else dest.user.name
@ -22,7 +23,8 @@
# "from_color": "0x#{getInSession("messageColor")}"
}
Meteor.call "sendChatMessagetoServer", getInSession("meetingId"), messageForServer
console.log JSON.stringify messageForServer
Meteor.call "sendChatMessagetoServer", getInSession("meetingId"), messageForServer, getInSession("userId")
$('#newMessageInput').val '' # Clear message box
Template.chatInput.events
@ -31,7 +33,7 @@ Template.chatInput.events
'keypress #newMessageInput': (event) -> # user pressed a button inside the chatbox
if event.shiftKey and event.which is 13
$("#newMessageInput").append("\n")
$("#newMessageInput").append("\r") # Change newline character
return
if event.which is 13 # Check for pressing enter to submit message
@ -48,7 +50,7 @@ Template.chatbar.helpers
greeting = "Welcome to #{getMeetingName()}!\r\r
For help on using BigBlueButton see these (short) <a href='http://www.bigbluebutton.org/videos/' target='_blank'>tutorial videos</a>.\r\r
To join the audio bridge click the headset icon (upper-left hand corner). Use a headset to avoid causing background noise for others.\r\r\r
This server is running BigBlueButton #{getInSession 'bbbServerVersion'}."
This server is running BigBlueButton #{getInSession 'bbbServerVersion'}.\r\r"
# This method returns all messages for the user. It looks at the session to determine whether the user is in
#private or public chat. If true is passed, messages returned are from before the user joined. Else, the messages are from after the user joined
@ -178,7 +180,6 @@ Template.tabButtons.events
'click .gotUnreadMail': (event) ->
chatTabs.update({userId: @userId}, {$set: {gotMail: false}})
Template.tabButtons.helpers
makeTabButton: -> # create tab button for private chat or other such as options

View File

@ -1,6 +1,11 @@
Template.displayUserIcons.events
'click .raisedHandIcon': (event) ->
Meteor.call('userLowerHand', getInSession("meetingId"), @user?.userid, getInSession("userId"))
# the function to call 'userLowerHand'
# the meeting id
# the _id of the person whose land is to be lowered
# the userId of the person who is lowering the hand
console.log "lower hand- client click handler"
Meteor.call('userLowerHand', getInSession("meetingId"), @_id, getInSession("userId"))
'click .muteIcon': (event) ->
toggleMic @

View File

@ -32,13 +32,13 @@
<td>{{#if user.presenter}}<span class="userListSettingIcon glyphicon glyphicon-picture" rel="tooltip" data-placement="bottom" title="{{user.name}} is the presenter"></span>{{/if}}</td>
<td>
{{#if user.raise_hand}}
{{#if isCurrentUser userId}}
<!-- {{#if user.raise_hand}} -->
<!-- {{#if isCurrentUser userId}} -->
<span class="raisedHandIcon userListSettingIcon glyphicon glyphicon-hand-up" rel="tooltip" data-placement="bottom" title="Lower your hand"></span>
{{else}}
<span class="userListSettingIcon glyphicon glyphicon-hand-up" rel="tooltip" data-placement="bottom" title="{{user.name}} has raised their hand"></span>
{{/if}}
{{/if}}
<!-- {{else}} -->
<!-- <span class="userListSettingIcon glyphicon glyphicon-hand-up" rel="tooltip" data-placement="bottom" title="{{user.name}} has raised their hand"></span> -->
<!-- {{/if}} -->
<!-- {{/if}} -->
</td>
</template>

View File

@ -1,3 +1,8 @@
Template.usersList.helpers
getMeetingSize: -> # Retreieve the number of users in the chat, or "error" string
Meteor.Users.find().count()
getMeetingSize: -> # Retreieve the number of users in the chat, or "error" string
Meteor.Users.find().count()
Template.usersList.events
'click #MuteAllUsers': (event) ->
console.log "MuteAllUsers click handler"
Meteor.call 'MuteAllUsers', getInSession("userId")

View File

@ -1,18 +1,19 @@
<template name="usersList">
<div id="{{id}}" {{visibility name}} class="component">
<h3 class="title gradientBar"><span class="glyphicon glyphicon-user"></span> Participants: {{getMeetingSize}} Users</h3>
<div id="user-contents">
{{#Layout template="scrollWindow" id="publicUserScrollWindow"}}
{{#contentFor region="scrollContents"}}
{{#Layout template="moduleTable" tableClass="table table-hover" tbodyClass="user-entry"}}
{{#contentFor region="Contents"}}
{{#each getUsersInMeeting}}
<tr>{{>userItem}}</tr>
{{/each}}
{{/contentFor}}
{{/Layout}}
{{/contentFor}}
{{/Layout}}
</div>
</div>
<div id="{{id}}" {{visibility name}} class="component">
<h3 class="title gradientBar"><span class="glyphicon glyphicon-user"></span> Participants: {{getMeetingSize}} Users</h3>
<div id="user-contents">
{{#Layout template="scrollWindow" id="publicUserScrollWindow"}}
{{#contentFor region="scrollContents"}}
{{#Layout template="moduleTable" tableClass="table table-hover" tbodyClass="user-entry"}}
{{#contentFor region="Contents"}}
{{#each getUsersInMeeting}}
<tr>{{>userItem}}</tr>
{{/each}}
{{/contentFor}}
{{/Layout}}
{{/contentFor}}
{{/Layout}}
</div>
</div>
</template>

View File

@ -88,11 +88,11 @@ class @WhiteboardPaperModel
@raphaelObj.renderfix()
# initializing border around slide to cover up areas which shouldnt show
@borders = {}
for border in ['left', 'right', 'top', 'bottom']
@borders[border] = @raphaelObj.rect(0, 0, 0, 0)
@borders[border].attr("fill", "#ababab")
@borders[border].attr( {stroke:"#ababab"} )
# @borders = {}
# for border in ['left', 'right', 'top', 'bottom']
# @borders[border] = @raphaelObj.rect(0, 0, 0, 0)
# @borders[border].attr("fill", "#ababab")
# @borders[border].attr( {stroke:"#ababab"} )
@raphaelObj
@ -191,7 +191,7 @@ class @WhiteboardPaperModel
for url of @slides
if @slides.hasOwnProperty(url)
@raphaelObj.getById(@slides[url]?.getId())?.remove()
#@trigger('paper:image:removed', @slides[url].getId()) # TODO do we need this?
@trigger('paper:image:removed', @slides[url].getId()) # Removes the previous image preventing images from being redrawn over each other repeatedly
@slides = {}
@current.slide = null
@ -765,7 +765,7 @@ class @WhiteboardPaperModel
if url?.match(/http[s]?:/)
url
else
console.log "the url did not match the expected format:#{url}"
console.log "The url '#{url}'' did not match the expected format of: http/s"
#globals.presentationServer + url
#Changes the currently displayed page/slide (if any) with this one

View File

@ -1,46 +0,0 @@
Meteor.methods
addChatToCollection: (meetingId, messageObject) ->
# manually convert time from 1.408645053653E12 to 1408645053653 if necessary (this is the time_from that the Flash client outputs)
messageObject.from_time = (messageObject.from_time).toString().split('.').join("").split("E")[0]
entry =
meetingId: meetingId
message:
chat_type: messageObject.chat_type
message: messageObject.message
to_username: messageObject.to_username
from_tz_offset: messageObject.from_tz_offset
from_color: messageObject.from_color
to_userid: messageObject.to_userid
from_userid: messageObject.from_userid
from_time: messageObject.from_time
from_username: messageObject.from_username
from_lang: messageObject.from_lang
id = Meteor.Chat.insert(entry)
console.log "added chat id=[#{id}]:#{messageObject.message}. Chat.size is now
#{Meteor.Chat.find({meetingId: meetingId}).count()}"
sendChatMessagetoServer: (meetingId, chatObject) ->
# check if this is a private or a public chat message
eventName = ->
if chatObject.chat_type is "PRIVATE_CHAT"
"send_private_chat_message_request"
else "send_public_chat_message_request"
message =
header :
"timestamp": new Date().getTime()
"name": eventName()
payload:
"message" : chatObject
"meeting_id": meetingId
"requester_id": chatObject.from_userid
Meteor.call('publish', Meteor.config.redis.channels.toBBBApps.chat, message)
deletePrivateChatMessages: (user1, user2) ->
console.log "deleting chat conversation"
Meteor.Chat.remove({ # find all and remove private messages between the 2 users
'message.chat_type': 'PRIVATE_CHAT',
$or: [{'message.from_userid': user1, 'message.to_userid': user2},{'message.from_userid': user2, 'message.to_userid': user1}]
})

View File

@ -1,24 +0,0 @@
Meteor.methods
addMeetingToCollection: (meetingId, name, intendedForRecording, voiceConf, duration) ->
#check if the meeting is already in the collection
unless Meteor.Meetings.findOne({meetingId: meetingId})?
currentlyBeingRecorded = false # defaut value
id = Meteor.Meetings.insert(
meetingId: meetingId,
meetingName: name,
intendedForRecording: intendedForRecording,
currentlyBeingRecorded: currentlyBeingRecorded,
voiceConf: voiceConf,
duration: duration)
console.log "added meeting _id=[#{id}]:meetingId=[#{meetingId}]:name=[#{name}]:duration=[#{duration}]:voiceConf=[#{voiceConf}].
Meetings.size is now #{Meteor.Meetings.find().count()}"
removeMeetingFromCollection: (meetingId) ->
if Meteor.Meetings.findOne({meetingId: meetingId})?
if Meteor.Users.find({meetingId: meetingId}).count() isnt 0
console.log "\n!!!!!removing a meeting which has active users in it!!!!\n"
id = Meteor.Meetings.findOne({meetingId: meetingId})
if id?
Meteor.Meetings.remove(id._id)
console.log "removed from Meetings:#{meetingId} now there are only
#{Meteor.Meetings.find().count()} meetings running"

View File

@ -1,26 +0,0 @@
Meteor.methods
addPresentationToCollection: (meetingId, presentationObject) ->
#check if the presentation is already in the collection
unless Meteor.Presentations.findOne({meetingId: meetingId, 'presentation.id': presentationObject.id})?
entry =
meetingId: meetingId
presentation:
id: presentationObject.id
name: presentationObject.name
current: presentationObject.current
pointer: #initially we have no data about the cursor
x: 0.0
y: 0.0
id = Meteor.Presentations.insert(entry)
console.log "added presentation id =[#{id}]:#{presentationObject.id} in #{meetingId}. Presentations.size is now
#{Meteor.Presentations.find({meetingId: meetingId}).count()}"
removePresentationFromCollection: (meetingId, presentationId) ->
if meetingId? and presentationId? and Meteor.Presentations.findOne({meetingId: meetingId, "presentation.id": presentationId})?
id = Meteor.Presentations.findOne({meetingId: meetingId, "presentation.id": presentationId})
if id?
Meteor.Presentations.remove(id._id)
console.log "----removed presentation[" + presentationId + "] from " + meetingId

View File

@ -1,79 +0,0 @@
Meteor.methods
addShapeToCollection: (meetingId, whiteboardId, shapeObject) ->
console.log "shapeObject=" + JSON.stringify shapeObject
if shapeObject?.shape_type is "text" and shapeObject.status is "textPublished"
console.log "we are dealing with a text shape"
entry =
meetingId: meetingId
whiteboardId: whiteboardId
shape:
type: shapeObject.shape.type
textBoxHeight: shapeObject.shape.textBoxHeight
backgroundColor: shapeObject.shape.backgroundColor
fontColor: shapeObject.shape.fontColor
status: shapeObject.shape.status
dataPoints: shapeObject.shape.dataPoints
x: shapeObject.shape.x
textBoxWidth: shapeObject.shape.textBoxWidth
whiteboardId: shapeObject.shape.whiteboardId
fontSize: shapeObject.shape.fontSize
id: shapeObject.shape.id
y: shapeObject.shape.y
calcedFontSize: shapeObject.shape.calcedFontSize
text: shapeObject.shape.text
background: shapeObject.shape.background
id = Meteor.Shapes.insert(entry)
numShapesOnSlide = Meteor.Shapes.find({meetingId: meetingId, whiteboardId: whiteboardId}).fetch().length
console.log "added textShape id =[#{id}]:#{shapeObject.id} in #{meetingId} || now there are #{numShapesOnSlide} shapes on the slide"
else
# the mouse button was released - the drawing is complete
# TODO: pencil messages currently don't send draw_end and are labeled all as DRAW_START
if shapeObject?.status is "DRAW_END" or (shapeObject?.status is "DRAW_START" and shapeObject?.shape_type is "pencil")
entry =
meetingId: meetingId
whiteboardId: whiteboardId
shape:
wb_id: shapeObject.wb_id
shape_type: shapeObject.shape_type
status: shapeObject.status
id: shapeObject.id
shape:
type: shapeObject.shape.type
status: shapeObject.shape.status
points: shapeObject.shape.points
whiteboardId: shapeObject.shape.whiteboardId
id: shapeObject.shape.id
square: shapeObject.shape.square
transparency: shapeObject.shape.transparency
thickness: shapeObject.shape.thickness
color: shapeObject.shape.color
id = Meteor.Shapes.insert(entry)
numShapesOnSlide = Meteor.Shapes.find({meetingId: meetingId, whiteboardId: whiteboardId}).fetch().length
console.log "added shape id =[#{id}]:#{shapeObject.id} in #{meetingId} || now there are #{numShapesOnSlide} shapes on the slide"
removeAllShapesFromSlide: (meetingId, whiteboardId) ->
console.log "removeAllShapesFromSlide__" + whiteboardId
if meetingId? and whiteboardId? and Meteor.Shapes.find({meetingId: meetingId, whiteboardId: whiteboardId})?
shapesOnSlide = Meteor.Shapes.find({meetingId: meetingId, whiteboardId: whiteboardId}).fetch()
console.log "number of shapes:" + shapesOnSlide.length
for s in shapesOnSlide
console.log "shape=" + s.shape.id
id = Meteor.Shapes.findOne({meetingId: meetingId, whiteboardId: whiteboardId, "shape.id": s.shape.id})
if id?
Meteor.Shapes.remove(id._id)
console.log "----removed shape[" + s.shape.id + "] from " + whiteboardId
console.log "remaining shapes on the slide:" + Meteor.Shapes.find({meetingId: meetingId, whiteboardId: whiteboardId}).fetch().length
removeShapeFromSlide: (meetingId, whiteboardId, shapeId) ->
shapeToRemove = Meteor.Shapes.findOne({meetingId: meetingId, whiteboardId: whiteboardId, "shape.id": shapeId})
if meetingId? and whiteboardId? and shapeId? and shapeToRemove?
Meteor.Shapes.remove(shapeToRemove._id)
console.log "----removed shape[" + shapeId + "] from " + whiteboardId
console.log "remaining shapes on the slide:" + Meteor.Shapes.find({meetingId: meetingId, whiteboardId: whiteboardId}).count()

View File

@ -1,40 +0,0 @@
Meteor.methods
addSlideToCollection: (meetingId, presentationId, slideObject) ->
unless Meteor.Slides.findOne({meetingId: meetingId, "slide.id": slideObject.id})?
entry =
meetingId: meetingId
presentationId: presentationId
slide:
height_ratio: slideObject.height_ratio
y_offset: slideObject.y_offset
num: slideObject.num
x_offset: slideObject.x_offset
current: slideObject.current
png_uri: slideObject.png_uri
txt_uri: slideObject.txt_uri
id: slideObject.id
width_ratio: slideObject.width_ratio
swf_uri: slideObject.swf_uri
thumb_uri: slideObject.thumb_uri
id = Meteor.Slides.insert(entry)
console.log "added slide id =[#{id}]:#{slideObject.id} in #{meetingId}. Now there are
#{Meteor.Slides.find({meetingId: meetingId}).count()} slides in the meeting"
removeSlideFromCollection: (meetingId, slideId) ->
if meetingId? and slideId? and Meteor.Slides.findOne({meetingId: meetingId, "slide.id": slideId})?
id = Meteor.Slides.findOne({meetingId: meetingId, "slide.id": slideId})
if id?
Meteor.Slides.remove(id._id)
console.log "----removed slide[" + slideId + "] from " + meetingId
displayThisSlide: (meetingId, newSlideId, slideObject) ->
presentationId = newSlideId.split("/")[0] # grab the presentationId part of the slideId
# change current to false for the old slide
Meteor.Slides.update({presentationId: presentationId, "slide.current": true}, {$set: {"slide.current": false}})
# for the new slide: remove the version which came with presentation_shared_message from the Collection
# to avoid using old data (this message contains everything we need for the new slide)
Meteor.call("removeSlideFromCollection", meetingId, newSlideId)
# add the new slide to the collection
Meteor.call("addSlideToCollection", meetingId, presentationId, slideObject)

View File

@ -1,173 +0,0 @@
Meteor.methods
addUserToCollection: (meetingId, user) ->
userId = user.userid
#check if the user is already in the meeting
unless Meteor.Users.findOne({userId:userId, meetingId: meetingId})?
entry =
meetingId: meetingId
userId: userId
user:
userid: user.userid
presenter: user.presenter
name: user.name
phone_user: user.phone_user
raise_hand: user.raise_hand
has_stream: user.has_stream
role: user.role
listenOnly: user.listenOnly
extern_userid: user.extern_userid
permissions: user.permissions
locked: user.locked
time_of_joining: user.timeOfJoining
voiceUser:
web_userid: user.voiceUser.web_userid
callernum: user.voiceUser.callernum
userid: user.voiceUser.userid
talking: user.voiceUser.talking
joined: user.voiceUser.joined
callername: user.voiceUser.callername
locked: user.voiceUser.locked
muted: user.voiceUser.muted
webcam_stream: user.webcam_stream
id = Meteor.Users.insert(entry)
console.log "added user id=[#{id}]:#{user.name}. Users.size is now #{Meteor.Users.find({meetingId: meetingId}).count()}"
removeUserFromCollection: (meetingId, userId) ->
if meetingId? and userId? and Meteor.Users.findOne({meetingId: meetingId, userId: userId})?
id = Meteor.Users.findOne({meetingId: meetingId, userId: userId})
if id?
Meteor.Users.remove(id._id)
console.log "----removed user[" + userId + "] from " + meetingId
#
# Voice
userShareAudio: (meetingId, userId) ->
if meetingId? and userId?
Meteor.call('updateVoiceUser',meetingId, {web_userid: userId, talking:false, joined: true, muted:false})
#TODO should we also send a message to bbb-apps about it?
userStopAudio: (meetingId, userId) ->
console.log "publishing a user left voice request for #{userId} in #{meetingId}"
message =
"payload":
"userid": userId
"meeting_id": meetingId
"header":
"timestamp": new Date().getTime()
"name": "user_left_voice_request"
"version": "0.0.1"
if meetingId? and userId?
Meteor.call('publish', Meteor.config.redis.channels.toBBBApps.voice, message)
Meteor.call('updateVoiceUser',meetingId, {web_userid: userId, talking:false, joined: false, muted:false})
else
console.log "did not have enough information to send a mute_user_request"
#update a voiceUser - a helper method
updateVoiceUser: (meetingId, voiceUserObject) ->
console.log "I am updating the voiceUserObject with the following: " + JSON.stringify voiceUserObject
u = Meteor.Users.findOne({userId: voiceUserObject?.web_userid, meetingId: meetingId})
if u?
if voiceUserObject?.talking?
Meteor.Users.update({_id:u._id}, {$set: {'user.voiceUser.talking':voiceUserObject?.talking}})# talking
if voiceUserObject?.joined?
Meteor.Users.update({_id:u._id}, {$set: {'user.voiceUser.joined':voiceUserObject?.joined}})# joined
if voiceUserObject?.locked?
Meteor.Users.update({_id:u._id}, {$set: {'user.voiceUser.locked':voiceUserObject?.locked}})# locked
if voiceUserObject?.muted?
Meteor.Users.update({_id:u._id}, {$set: {'user.voiceUser.muted':voiceUserObject?.muted}})# muted
else
console.log "ERROR! did not find such voiceUser!"
publishMuteRequest: (meetingId, userId, requesterId, mutedBoolean) ->
console.log "publishing a user mute #{mutedBoolean} request for #{userId}"
message =
"payload":
"userid": userId
"meeting_id": meetingId
"mute": mutedBoolean
"requester_id": requesterId
"header":
"timestamp": new Date().getTime()
"name": "mute_user_request"
"version": "0.0.1"
if meetingId? and userId? and requesterId?
Meteor.call('publish', Meteor.config.redis.channels.toBBBApps.voice, message)
# modify the collection
Meteor.Users.update({userId:userId, meetingId: meetingId}, {$set:{'user.voiceUser.talking':false}})
numChanged = Meteor.Users.update({userId:userId, meetingId: meetingId}, {$set:{'user.voiceUser.muted':mutedBoolean}})
if numChanged isnt 1
console.log "\n\nSomething went wrong!! We were supposed to mute/unmute 1 user!!\n\n"
else
console.log "did not have enough information to send a mute_user_request"
# Raise & Lower hand
userLowerHand: (meetingId, userId, loweredBy) ->
console.log "publishing a userLowerHand event: #{userId}--by=#{loweredBy}"
if meetingId? and userId? and loweredBy?
message =
"payload":
"userid": userId
"meeting_id": meetingId
"raise_hand": false
"lowered_by": loweredBy
"header":
"timestamp": new Date().getTime()
"name": "user_lowered_hand_message"
"version": "0.0.1"
#publish to pubsub
Meteor.call('publish', Meteor.config.redis.channels.toBBBApps.users, message)
#update Users collection
Meteor.Users.update({userId:userId, meetingId: meetingId}, {$set: {'user.raise_hand': false}})
userRaiseHand: (meetingId, userId) ->
console.log "publishing a userRaiseHand event: #{userId}"
if meetingId? and userId?
message =
"payload":
"userid": userId
"meeting_id": meetingId
"raise_hand": true
"header":
"timestamp": new Date().getTime()
"name": "user_raised_hand_message"
"version": "0.0.1"
#publish to pubsub
Meteor.call('publish',Meteor.config.redis.channels.toBBBApps.users, message)
#update Users collection
Meteor.Users.update({userId:userId, meetingId: meetingId}, {$set: {'user.raise_hand': true}})
userLogout: (meetingId, userId) ->
console.log "a user is logging out:" + userId
#remove from the collection
Meteor.call("removeUserFromCollection", meetingId, userId)
#dispatch a message to redis
Meteor.call('sendUserLeavingRequest', meetingId, userId)
userKick: (meetingId, userId) ->
console.log "#{userId} is being kicked"
#remove from the collection
Meteor.call("removeUserFromCollection", meetingId, userId)
#dispatch a message to redis
Meteor.call('sendUserLeavingRequest', meetingId, userId)
sendUserLeavingRequest: (meetingId, userId) ->
console.log "\n\n sending a user_leaving_request for #{meetingId}:#{userId}"
message =
"payload":
"meeting_id": meetingId
"userid": userId
"header":
"timestamp": new Date().getTime()
"name": "user_leaving_request"
"version": "0.0.1"
if userId? and meetingId?
Meteor.call('publish', Meteor.config.redis.channels.toBBBApps.users, message)
else
console.log "did not have enough information to send a user_leaving_request"

View File

@ -14,10 +14,16 @@
Meteor.subscribe 'slides', getInSession('meetingId'), ->
Meteor.subscribe 'meetings', getInSession('meetingId'), ->
Meteor.subscribe 'presentations', getInSession('meetingId'), ->
# Obtain user info here. for testing. should be moved somewhere else later
Meteor.call "getMyInfo", getInSession("userId"), (error, result) ->
setInSession("DBID", result.DBID)
setInSession("userName", result.name)
self.redirect('/')
onBeforeAction: ()->
url = location.href
url = location.href
console.log "\n\nurl=#{url}\n\n"
#extract the meeting_id, user_id, auth_token, etc from the uri
if url.indexOf("meeting_id") > -1 # if the URL is /meeting_id=...&...
@ -33,17 +39,9 @@
console.log "authToken=" + authToken
if meetingId? and userId? and authToken?
# Here we need to check whether there is already a user using userId inside meetingId, if there is don't let this user log in, it is a duplicate
###
if Meteor.call("validateUserId", meetingId, userId)
continue
else
kick user out
###
Meteor.call("validateAuthToken", meetingId, userId, authToken)
Meteor.call('sendMeetingInfoToClient', meetingId, userId)
else
else
console.log "unable to extract from the URL some of {meetingId, userId, authToken}"
else
console.log "unable to extract the required information for the meeting from the URL"
@ -51,20 +49,17 @@
path: "/"
onBeforeAction: ->
self = @
# Have to check on the server whether the credentials the user has are valid on db, without being able to spam requests for credentials
Meteor.subscribe 'users', getInSession('meetingId'), -> # callback for after users have been loaded on client
if not validateCredentials() # Don't let user in if they are not valid
self.redirect("logout")
else
Meteor.subscribe 'chat', getInSession('meetingId'), getInSession("userId"), ->
Meteor.subscribe 'shapes', getInSession('meetingId'), ->
Meteor.subscribe 'slides', getInSession('meetingId'), ->
Meteor.subscribe 'meetings', getInSession('meetingId'), ->
Meteor.subscribe 'presentations', getInSession('meetingId')
Meteor.subscribe 'chat', getInSession('meetingId'), getInSession("userId"), ->
Meteor.subscribe 'shapes', getInSession('meetingId'), ->
Meteor.subscribe 'slides', getInSession('meetingId'), ->
Meteor.subscribe 'meetings', getInSession('meetingId'), ->
Meteor.subscribe 'presentations', getInSession('meetingId'), ->
# Obtain user info here. for testing. should be moved somewhere else later
Meteor.call "getMyInfo", getInSession("userId"), (error, result) ->
setInSession("DBID", result.DBID)
setInSession("userName", result.name)
@route "logout",
path: "logout"
@validateCredentials = ->
u = Meteor.Users.findOne({"userId":getInSession("userId")})
# return whether they are a valid user and still have credentials in the database
u? and u.meetingId? and u.user?.extern_userid and u.user?.userid #and (1 is 2) # makes validation fail

View File

@ -0,0 +1,87 @@
Meteor.methods
sendChatMessagetoServer: (meetingId, chatObject, requesterUserId) ->
# inside the chatObject, they store their _id as the sender
# and they pass their userId to this method as a param
transformedChatObject = chatObject
console.log "requesterUserId: #{requesterUserId} | from_userid: #{transformedChatObject.from_userid}"
requester = Meteor.Users.findOne({_id: transformedChatObject.from_userid, userId: requesterUserId})
forPublic = transformedChatObject.to_userid is 'public_chat_userid'
if requester? # User exists, and is valid
console.log "requester exists"
# check if this is a private or a public chat message
eventName = ->
if transformedChatObject.chat_type is "PRIVATE_CHAT"
"send_private_chat_message_request"
else "send_public_chat_message_request"
recipient = Meteor.Users.findOne(_id: transformedChatObject.to_userid)
if recipient? or forPublic
# translate _ids to userIds for flash
transformedChatObject.from_userid = requester.userId
transformedChatObject.to_userid = if forPublic then 'public_chat_userid' else recipient.userId
message =
header :
"timestamp": new Date().getTime()
"name": eventName()
payload:
"message" : transformedChatObject
"meeting_id": meetingId
"requester_id": transformedChatObject.from_userid
#
console.log JSON.stringify transformedChatObject
publish Meteor.config.redis.channels.toBBBApps.chat, message
#
else
console.log "requester no exists"
# --------------------------------------------------------------------------------------------
# Private methods on server
# --------------------------------------------------------------------------------------------
@deletePrivateChatMessages = (user1Id, user2Id) ->
Meteor.Chat.remove({ # find all and remove private messages between the 2 users
'message.chat_type': 'PRIVATE_CHAT',
$or: [{'message.from_userid': user1Id, 'message.to_userid': user2Id},{'message.from_userid': user2Id, 'message.to_userid': user1Id}]
})
# send a message to redis out about deleting conversation between these 2 users
#
@addChatToCollection = (meetingId, messageObject) ->
console.log "-------------addChatToCollection---------------------"
transformedChatObject = messageObject
# manually convert time from 1.408645053653E12 to 1408645053653 if necessary (this is the time_from that the Flash client outputs)
transformedChatObject.from_time = (transformedChatObject.from_time).toString().split('.').join("").split("E")[0]
fromUser = Meteor.Users.findOne({userId: transformedChatObject.from_userid})
toUser = Meteor.Users.findOne({userId: transformedChatObject.to_userid})
forPublic = transformedChatObject.to_userid is 'public_chat_userid'
if (fromUser? and toUser?) or forPublic
# translate ids from flash to html5
transformedChatObject.from_userid = fromUser._id
transformedChatObject.to_userid = if forPublic then 'public_chat_userid' else toUser._id
if transformedChatObject.from_userid? and transformedChatObject.to_userid?
entry =
meetingId: meetingId
message:
chat_type: transformedChatObject.chat_type
message: transformedChatObject.message
to_username: transformedChatObject.to_username
from_tz_offset: transformedChatObject.from_tz_offset
from_color: transformedChatObject.from_color
to_userid: transformedChatObject.to_userid
from_userid: transformedChatObject.from_userid
from_time: transformedChatObject.from_time
from_username: transformedChatObject.from_username
from_lang: transformedChatObject.from_lang
id = Meteor.Chat.insert(entry)
console.log "added chat id=[#{id}]:#{transformedChatObject.message}. Chat.size is now #{Meteor.Chat.find({meetingId: meetingId}).count()}"
# --------------------------------------------------------------------------------------------
# end Private methods on server
# --------------------------------------------------------------------------------------------

View File

@ -0,0 +1,27 @@
# --------------------------------------------------------------------------------------------
# Private methods on server
# --------------------------------------------------------------------------------------------
@addMeetingToCollection = (meetingId, name, intendedForRecording, voiceConf, duration) ->
#check if the meeting is already in the collection
unless Meteor.Meetings.findOne({meetingId: meetingId})?
currentlyBeingRecorded = false # defaut value
id = Meteor.Meetings.insert(
meetingId: meetingId,
meetingName: name,
intendedForRecording: intendedForRecording,
currentlyBeingRecorded: currentlyBeingRecorded,
voiceConf: voiceConf,
duration: duration)
console.log "added meeting _id=[#{id}]:meetingId=[#{meetingId}]:name=[#{name}]:duration=[#{duration}]:voiceConf=[#{voiceConf}].Meetings.size is now #{Meteor.Meetings.find().count()}"
@removeMeetingFromCollection = (meetingId) ->
if Meteor.Meetings.findOne({meetingId: meetingId})?
if Meteor.Users.find({meetingId: meetingId}).count() isnt 0
console.log "\n!!!!!removing a meeting which has active users in it!!!!\n"
id = Meteor.Meetings.findOne({meetingId: meetingId})
if id?
Meteor.Meetings.remove(id._id)
console.log "removed from Meetings:#{meetingId} now there are only #{Meteor.Meetings.find().count()} meetings running"
# --------------------------------------------------------------------------------------------
# end Private methods on server
# --------------------------------------------------------------------------------------------

View File

@ -0,0 +1,29 @@
# --------------------------------------------------------------------------------------------
# Private methods on server
# --------------------------------------------------------------------------------------------
@addPresentationToCollection = (meetingId, presentationObject) ->
#check if the presentation is already in the collection
unless Meteor.Presentations.findOne({meetingId: meetingId, 'presentation.id': presentationObject.id})?
entry =
meetingId: meetingId
presentation:
id: presentationObject.id
name: presentationObject.name
current: presentationObject.current
pointer: #initially we have no data about the cursor
x: 0.0
y: 0.0
id = Meteor.Presentations.insert(entry)
console.log "presentation added id =[#{id}]:#{presentationObject.id} in #{meetingId}. Presentations.size is now #{Meteor.Presentations.find({meetingId: meetingId}).count()}"
@removePresentationFromCollection = (meetingId, presentationId) ->
if meetingId? and presentationId? and Meteor.Presentations.findOne({meetingId: meetingId, "presentation.id": presentationId})?
id = Meteor.Presentations.findOne({meetingId: meetingId, "presentation.id": presentationId})
if id?
Meteor.Presentations.remove(id._id)
console.log "----removed presentation[" + presentationId + "] from " + meetingId
# --------------------------------------------------------------------------------------------
# end Private methods on server
# --------------------------------------------------------------------------------------------

View File

@ -0,0 +1,81 @@
# --------------------------------------------------------------------------------------------
# Private methods on server
# --------------------------------------------------------------------------------------------
@addShapeToCollection = (meetingId, whiteboardId, shapeObject) ->
console.log "shapeObject=" + JSON.stringify shapeObject
if shapeObject?.shape_type is "text" and shapeObject.status is "textPublished"
console.log "we are dealing with a text shape"
entry =
meetingId: meetingId
whiteboardId: whiteboardId
shape:
type: shapeObject.shape.type
textBoxHeight: shapeObject.shape.textBoxHeight
backgroundColor: shapeObject.shape.backgroundColor
fontColor: shapeObject.shape.fontColor
status: shapeObject.shape.status
dataPoints: shapeObject.shape.dataPoints
x: shapeObject.shape.x
textBoxWidth: shapeObject.shape.textBoxWidth
whiteboardId: shapeObject.shape.whiteboardId
fontSize: shapeObject.shape.fontSize
id: shapeObject.shape.id
y: shapeObject.shape.y
calcedFontSize: shapeObject.shape.calcedFontSize
text: shapeObject.shape.text
background: shapeObject.shape.background
id = Meteor.Shapes.insert(entry)
numShapesOnSlide = Meteor.Shapes.find({meetingId: meetingId, whiteboardId: whiteboardId}).fetch().length
console.log "added textShape id =[#{id}]:#{shapeObject.id} in #{meetingId} || now there are #{numShapesOnSlide} shapes on the slide"
else
# the mouse button was released - the drawing is complete
# TODO: pencil messages currently don't send draw_end and are labeled all as DRAW_START
if shapeObject?.status is "DRAW_END" or (shapeObject?.status is "DRAW_START" and shapeObject?.shape_type is "pencil")
entry =
meetingId: meetingId
whiteboardId: whiteboardId
shape:
wb_id: shapeObject.wb_id
shape_type: shapeObject.shape_type
status: shapeObject.status
id: shapeObject.id
shape:
type: shapeObject.shape.type
status: shapeObject.shape.status
points: shapeObject.shape.points
whiteboardId: shapeObject.shape.whiteboardId
id: shapeObject.shape.id
square: shapeObject.shape.square
transparency: shapeObject.shape.transparency
thickness: shapeObject.shape.thickness
color: shapeObject.shape.color
id = Meteor.Shapes.insert(entry)
numShapesOnSlide = Meteor.Shapes.find({meetingId: meetingId, whiteboardId: whiteboardId}).fetch().length
console.log "added shape id =[#{id}]:#{shapeObject.id} in #{meetingId} || now there are #{numShapesOnSlide} shapes on the slide"
@removeAllShapesFromSlide = (meetingId, whiteboardId) ->
console.log "removeAllShapesFromSlide__" + whiteboardId
if meetingId? and whiteboardId? and Meteor.Shapes.find({meetingId: meetingId, whiteboardId: whiteboardId})?
shapesOnSlide = Meteor.Shapes.find({meetingId: meetingId, whiteboardId: whiteboardId}).fetch()
console.log "number of shapes:" + shapesOnSlide.length
for s in shapesOnSlide
console.log "shape=" + s.shape.id
id = Meteor.Shapes.findOne({meetingId: meetingId, whiteboardId: whiteboardId, "shape.id": s.shape.id})
if id?
Meteor.Shapes.remove(id._id)
console.log "----removed shape[" + s.shape.id + "] from " + whiteboardId
console.log "remaining shapes on the slide:" + Meteor.Shapes.find({meetingId: meetingId, whiteboardId: whiteboardId}).fetch().length
@removeShapeFromSlide = (meetingId, whiteboardId, shapeId) ->
shapeToRemove = Meteor.Shapes.findOne({meetingId: meetingId, whiteboardId: whiteboardId, "shape.id": shapeId})
if meetingId? and whiteboardId? and shapeId? and shapeToRemove?
Meteor.Shapes.remove(shapeToRemove._id)
console.log "----removed shape[" + shapeId + "] from " + whiteboardId
console.log "remaining shapes on the slide:" + Meteor.Shapes.find({meetingId: meetingId, whiteboardId: whiteboardId}).count()
# --------------------------------------------------------------------------------------------
# end Private methods on server
# --------------------------------------------------------------------------------------------

View File

@ -0,0 +1,44 @@
# --------------------------------------------------------------------------------------------
# Private methods on server
# --------------------------------------------------------------------------------------------
@displayThisSlide = (meetingId, newSlideId, slideObject) ->
presentationId = newSlideId.split("/")[0] # grab the presentationId part of the slideId
# change current to false for the old slide
Meteor.Slides.update({presentationId: presentationId, "slide.current": true}, {$set: {"slide.current": false}})
# for the new slide: remove the version which came with presentation_shared_message from the Collection
# to avoid using old data (this message contains everything we need for the new slide)
Meteor.call("removeSlideFromCollection", meetingId, newSlideId)
# add the new slide to the collection
Meteor.call("addSlideToCollection", meetingId, presentationId, slideObject)
@addSlideToCollection = (meetingId, presentationId, slideObject) ->
unless Meteor.Slides.findOne({meetingId: meetingId, "slide.id": slideObject.id})?
entry =
meetingId: meetingId
presentationId: presentationId
slide:
height_ratio: slideObject.height_ratio
y_offset: slideObject.y_offset
num: slideObject.num
x_offset: slideObject.x_offset
current: slideObject.current
png_uri: slideObject.png_uri
txt_uri: slideObject.txt_uri
id: slideObject.id
width_ratio: slideObject.width_ratio
swf_uri: slideObject.swf_uri
thumb_uri: slideObject.thumb_uri
id = Meteor.Slides.insert(entry)
console.log "added slide id =[#{id}]:#{slideObject.id} in #{meetingId}. Now there are #{Meteor.Slides.find({meetingId: meetingId}).count()} slides in the meeting"
@removeSlideFromCollection = (meetingId, slideId) ->
if meetingId? and slideId? and Meteor.Slides.findOne({meetingId: meetingId, "slide.id": slideId})?
id = Meteor.Slides.findOne({meetingId: meetingId, "slide.id": slideId})
if id?
Meteor.Slides.remove(id._id)
console.log "----removed slide[" + slideId + "] from " + meetingId
# --------------------------------------------------------------------------------------------
# end Private methods on server
# --------------------------------------------------------------------------------------------

View File

@ -0,0 +1,235 @@
# --------------------------------------------------------------------------------------------
# Public methods on server
# All these method must first authenticate the user before it calls the private function counterpart below
# which sends the request to bbbApps. If the method is modifying the media the current user is sharing,
# you should perform the request before sending the request to bbbApps. This allows the user request to be performed
# immediately, since they do not require permission for things such as muting themsevles.
# --------------------------------------------------------------------------------------------
Meteor.methods
# I did not simply loop through all users and call the 'publishMuteRequest' because that function
# always validates the credentials of the requester. This is a waste of resources when applying it to every user.
# We can validate the muter first, then mute all users individually
# Perhaps there should be a way to send a mute request to bbbApps for several users, instead of an individual request for each user (bandwidth)
MuteAllUsers: (meetingId, requesterUserId, requester_id) ->
console.log "MuteAllUsers server method"
muter = Meteor.Users.findOne({'meetingId': meetingId, 'userId': requesterUserId, _id: requester_id})
if muter?.presenter? and muter.presenter # or if they are a moderator?
users = Meteor.Users.find({}).fetch()
for mutee in users
# check if user isnt muted, then continue. If they are already muted you can skip them
message =
"payload":
"userid": mutee.userId
"meeting_id": meetingId
"mute": mutedBoolean
"requester_id": muter.userId
"header":
"timestamp": new Date().getTime()
"name": "mute_user_request"
"version": "0.0.1"
publish Meteor.config.redis.channels.toBBBApps.voice, message
updateVoiceUser {'user_id': mutee._id, talking:false, muted:true}
userShareAudio: (meetingId, userId, user_id) ->
updateVoiceUser {'user_id': user_id, 'talking':false, 'joined': true, 'muted':false}
#TODO we need to send a message to bbb-apps about it
userStopAudio: (meetingId, userId, user_id, requesterUserId, requester_id) ->
console.log "publishing a user left voice request for #{userId} in #{meetingId}"
user = Meteor.Users.findOne({'meetingId': meetingId, 'userId': userId, '_id': user_id})
requester = Meteor.Users.findOne({'meetingId': meetingId, 'userId': requesterUserId, '_id': requester_id})
if user? and requester? and ((user._id is requester._id) or requester.presenter)
message =
"payload":
"userid": user.userId
"meeting_id": user.meetingId
"header":
"timestamp": new Date().getTime()
"name": "user_left_voice_request"
"version": "0.0.1"
publish Meteor.config.redis.channels.toBBBApps.voice, message
updateVoiceUser {'user_id': user_id, talking:false, joined: true, muted:false}
else
console.log "did not have enough information to send a mute_user_request"
# Verifies muter exists, provided proper credentials, and has permission to mute the user
publishMuteRequest: (meetingId, mutee_id, requesterUserId, requester_id, mutedBoolean) ->
console.log "publishing a user mute #{mutedBoolean} request for #{mutee_id}"
mutee = Meteor.Users.findOne({'meetingId': meetingId, _id: mutee_id})
muter = Meteor.Users.findOne({'meetingId': meetingId, 'userId': requesterUserId, _id: requester_id})
if mutee? and muter?
message =
"payload":
"userid": mutee.userId
"meeting_id": meetingId
"mute": mutedBoolean
"requester_id": muter.userId
"header":
"timestamp": new Date().getTime()
"name": "mute_user_request"
"version": "0.0.1"
publish Meteor.config.redis.channels.toBBBApps.voice, message
updateVoiceUser {'user_id': mutee._id, talking:false, muted:true}
#
else
console.log "did not have enough information to send a mute_user_request"
# meetingId: the meetingId which both users are in
# user_id: the _id of the user to have their hand lowered
# loweredByUserId: userId of person lowering
# loweredBy_id: _id of person lowering
userLowerHand: (meetingId, toLowerUser_Id, loweredByUserId, loweredBy_id) ->
requester = Meteor.Users.findOne({'meetingId': meetingId, 'userId': loweredByUserId, '_id': loweredBy_id})
if requester?
# Allow if person lowering the hand is the presenter, or they're lowering their own hand
unless requester.user.presenter or loweredBy_id is toLowerUser_Id or requester.role is "MODERATOR"
return
console.log "publishing a userLowerHand event: #{userId}--by=#{requester._id}"
toLower = Meteor.Users.findOne({'meetingId': meetingId, '_id': toLowerUser_Id})
if toLower?
message =
"payload":
"userid": toLower.userId
"meeting_id": meetingId
"raise_hand": false
"lowered_by": loweredByUserId
"header":
"timestamp": new Date().getTime()
"name": "user_lowered_hand_message"
"version": "0.0.1"
# publish to pubsub
publish Meteor.config.redis.channels.toBBBApps.users, message
# meetingId: the meetingId which both users are in
# user_id: the _id of the user to have their hand raised
# loweredByUserId: userId of person raising
# loweredBy_id: _id of person raising
userRaiseHand: (meetingId, user_id, raisedByUserId, raisedBy_id) ->
requester = Meteor.Users.findOne({'meetingId': meetingId, 'userId': raisedByUserId, '_id': raisedBy_id})
if requester?
# Allow if person raising the hand is the presenter, or they're raising their own hand
unless requester.user.presenter or raisedBy_id is user_id or requester.role is "MODERATOR"
return
console.log "publishing a userRaiseHand event: #{userId}--by=#{requester._id}"
toRaise = Meteor.Users.findOne({'meetingId': meetingId, '_id': user_id})
if toRaise?
message =
"payload":
"userid": toRaise.userId
"meeting_id": meetingId
"raise_hand": true
"raised_by": raisedByUserId
"header":
"timestamp": new Date().getTime()
"name": "user_raised_hand_message"
"version": "0.0.1"
# publish to pubsub
publish Meteor.config.redis.channels.toBBBApps.users, message
userLogout: (meetingId, userId, user_id) ->
console.log "a user is logging out:" + userId
u = Meteor.Users.findOne({meetingId: meetingId, _id: user_id, userId: userId})
if u?
#remove from the collection and dispatch a message to redis
removeUserFromMeeting meetingId, u.userId, u.user_id
# userToBeKicked: the _id of the user who was selected to be kicked
# kickerUserId: the userId of the user kicking another user
# kicker_id: the _id of the user kicking another user
userKick: (meetingId, userToBeKicked, kickerUserId, kicker_id) ->
kicker = Meteor.Users.findOne({meetingId: meetingId, _id: kicker_id, userId: kickerUserId})
toKick = Meteor.Users.findOne({meetingId: meetingId, _id: userToBeKicked})
if kicker? and toKick? and kicker.presenter
#remove from the collection and dispatch a message to redis
removeUserFromMeeting meetingId, toKick.userId, toKick.user_id
# --------------------------------------------------------------------------------------------
# Private methods on server
# --------------------------------------------------------------------------------------------
# Only callable from server
# You must need a user's public and private id
@removeUserFromMeeting = (meetingId, userId, user_id) ->
console.log "#{toKick.userId} is being kicked"
console.log "----removed user[" + toKick + "] from " + meetingId
u = Meteor.Users.findOne({'userId': userId, _id: user_id})
if u?
Meteor.Users.remove(user._id) # Should this only happen once we get the server's response?
console.log "\n\n sending a user_leaving_request for #{meetingId}:#{user._id}"
message =
"payload":
"meeting_id": meetingId
"userid": user.userId
"header":
"timestamp": new Date().getTime()
"name": "user_leaving_request"
"version": "0.0.1"
if user.userId? and meetingId?
publish Meteor.config.redis.channels.toBBBApps.users, message
else
console.log "did not have enough information to send a user_leaving_request"
#update a voiceUser - a helper method
@updateVoiceUser = (voiceUserObject) ->
if voiceUserObject?.user_id?
console.log "I am updating the voiceUserObject with the following: " + JSON.stringify voiceUserObject
u = Meteor.Users.findOne _id: voiceUserObject.user_id
if u?
if voiceUserObject.talking?
Meteor.Users.update({_id:voiceUserObject.user_id}, {$set: {'user.voiceUser.talking':voiceUserObject.talking}}, {multi: false}) # talking
if voiceUserObject.joined?
Meteor.Users.update({_id:voiceUserObject.user_id}, {$set: {'user.voiceUser.joined':voiceUserObject.joined}}, {multi: false}) # joined
if voiceUserObject.locked?
Meteor.Users.update({_id:voiceUserObject.user_id}, {$set: {'user.voiceUser.locked':voiceUserObject.locked}}, {multi: false}) # locked
if voiceUserObject.muted?
Meteor.Users.update({_id:voiceUserObject.user_id}, {$set: {'user.voiceUser.muted':voiceUserObject.muted}}, {multi: false}) # muted
if voiceUserObject.listenOnly?
Meteor.Users.update({_id:voiceUserObject.user_id}, {$set: {'user.listenOnly':voiceUserObject.listenOnly}}, {multi: false}) # muted
else
console.log "ERROR! did not find such voiceUser!"
@addUserToCollection = (meetingId, user) ->
userId = user.userid
#check if the user is already in the meeting
unless Meteor.Users.findOne({userId:userId, meetingId: meetingId})?
entry =
meetingId: meetingId
userId: userId
user:
userid: user.userid
presenter: user.presenter
name: user.name
phone_user: user.phone_user
raise_hand: user.raise_hand
has_stream: user.has_stream
role: user.role
listenOnly: user.listenOnly
extern_userid: user.extern_userid
permissions: user.permissions
locked: user.locked
time_of_joining: user.timeOfJoining
voiceUser:
web_userid: user.voiceUser.web_userid
callernum: user.voiceUser.callernum
userid: user.voiceUser.userid
talking: user.voiceUser.talking
joined: user.voiceUser.joined
callername: user.voiceUser.callername
locked: user.voiceUser.locked
muted: user.voiceUser.muted
webcam_stream: user.webcam_stream
id = Meteor.Users.insert(entry)
console.log "added user id=[#{id}]:#{user.name}. Users.size is now #{Meteor.Users.find({meetingId: meetingId}).count()}"

View File

@ -1,7 +1,7 @@
# Publish only the users that are in the particular meetingId
# On the client side we pass the meetingId parameter
Meteor.publish 'users', (meetingId) ->
Meteor.Users.find({meetingId: meetingId})
Meteor.Users.find({meetingId: meetingId}, {fields: { 'userId': 0, 'user.userid': 0, 'user.extern_userid': 0, 'user.voiceUser.userid': 0, 'user.voiceUser.web_userid': 0 }})
Meteor.publish 'chat', (meetingId, me) ->
Meteor.Chat.find({$or: [ {'message.chat_type': 'PUBLIC_CHAT', 'meetingId': meetingId},{'message.from_userid': me, 'meetingId': meetingId},{'message.to_userid': me, 'meetingId': meetingId}] })

View File

@ -1,4 +1,8 @@
Meteor.methods
#
# I dont know if this is okay to be server side. We need to call it from the router, but I don't know if any harm can be caused
# by the client calling this
#
# Construct and send a message to bbb-web to validate the user
validateAuthToken: (meetingId, userId, authToken) ->
@ -15,22 +19,10 @@ Meteor.methods
"name": "validate_auth_token"
if authToken? and userId? and meetingId?
Meteor.call('publish', Meteor.config.redis.channels.toBBBApps.meeting, message)
publish Meteor.config.redis.channels.toBBBApps.meeting, message
else
console.log "did not have enough information to send a validate_auth_token message"
# message should be an object
publish: (channel, message) ->
console.log "Publishing channel=#{channel}, message=#{JSON.stringify(message)}"
if Meteor.redisPubSub?
Meteor.redisPubSub.pubClient?.publish(channel, JSON.stringify(message), (err, res) ->
if err
console.log "err=" + err
)
else
console.log "\n ERROR!! Meteor.redisPubSub was undefined\n"
class Meteor.RedisPubSub
constructor: (callback) ->
console.log "constructor RedisPubSub"
@ -55,7 +47,7 @@ class Meteor.RedisPubSub
"header":
"name": "get_all_meetings_request"
"payload": {} # I need this, otherwise bbb-apps won't recognize the message
Meteor.call('publish', Meteor.config.redis.channels.toBBBApps.meeting, message)
publish Meteor.config.redis.channels.toBBBApps.meeting, message
_onMessage: (pattern, channel, jsonMsg) =>
# TODO: this has to be in a try/catch block, otherwise the server will
@ -72,173 +64,208 @@ class Meteor.RedisPubSub
"presentation_cursor_updated_message" # just because it's common. we handle it anyway
]
unless message.header?.name in ignoredEventTypes
console.log "eventType=" + message.header?.name #+ "\n"
console.log jsonMsg
if message?.header? and message?.payload?
unless message.header.name in ignoredEventTypes
console.log "eventType=" + message.header.name #+ "\n"
console.log jsonMsg
# handle voice events
if message.header?.name in ['user_left_voice_message', 'user_joined_voice_message', 'user_voice_talking_message', 'user_voice_muted_message']
voiceUser = message.payload?.user?.voiceUser
Meteor.call('updateVoiceUser', meetingId, voiceUser)
# handle voice events
if message.header.name in ['user_left_voice_message', 'user_joined_voice_message', 'user_voice_talking_message', 'user_voice_muted_message']
voiceUser = message.payload.user?.voiceUser
updateVoiceUser meetingId, voiceUser
# listen only
if message.header?.name is 'user_listening_only'
u = Meteor.Users.findOne({userId: message.payload?.userid, meetingId: meetingId})
Meteor.Users.update({_id:u._id}, {$set: {'user.listenOnly':message.payload?.listen_only}})
# most likely we don't need to ensure that the user's voiceUser's {talking, joined, muted, locked} are false by default #TODO?
# listen only
if message.header.name is 'user_listening_only'
u = Meteor.Users.findOne({userId: message.payload.userid, meetingId: meetingId})
updateVoiceUser {'user_id': u._id, 'listenOnly': message.payload.listen_only}
# most likely we don't need to ensure that the user's voiceUser's {talking, joined, muted, locked} are false by default #TODO?
return
if message.header?.name is "get_all_meetings_reply"
console.log "Let's store some data for the running meetings so that when an HTML5 client joins everything is ready!"
listOfMeetings = message.payload?.meetings
for meeting in listOfMeetings
# we currently do not have voice_conf or duration in this message.
Meteor.call("addMeetingToCollection", meeting.meetingID, meeting.meetingName, meeting.recorded, meeting.voiceBridge, meeting.duration)
if message.header.name is "get_all_meetings_reply"
console.log "Let's store some data for the running meetings so that when an HTML5 client joins everything is ready!"
listOfMeetings = message.payload.meetings
for meeting in listOfMeetings
# we currently do not have voice_conf or duration in this message.
addMeetingToCollection meeting.meetingID, meeting.meetingName, meeting.recorded, meeting.voiceBridge, meeting.duration
return
if message.header?.name is "get_users_reply" and message.payload?.requester_id is "nodeJSapp"
unless Meteor.Meetings.findOne({MeetingId: message.payload?.meeting_id})?
users = message.payload?.users
for user in users
user.timeOfJoining = message.header?.current_time # TODO this might need to be removed
Meteor.call("addUserToCollection", meetingId, user)
if message.header.name is "get_users_reply" and message.payload.requester_id is "nodeJSapp"
unless Meteor.Meetings.findOne({MeetingId: message.payload.meeting_id})?
users = message.payload.users
for user in users
user.timeOfJoining = message.header.current_time # TODO this might need to be removed
addUserToCollection meetingId, user
return
if message.header?.name is "user_joined_message"
user = message.payload.user
user.timeOfJoining = message.header?.current_time
Meteor.call("addUserToCollection", meetingId, user)
if message.header.name is "user_joined_message"
user = message.payload.user
user.timeOfJoining = message.header.current_time
addUserToCollection meetingId, user
return
if message.header?.name is "user_left_message"
userId = message.payload?.user?.userid
if userId? and meetingId?
Meteor.call("removeUserFromCollection", meetingId, userId)
if message.header.name is "user_left_message"
userId = message.payload.user?.userid
if userId? and meetingId?
removeUserFromMeeting meetingId, userId
return
if message.header?.name is "get_chat_history_reply" and message.payload?.requester_id is "nodeJSapp"
unless Meteor.Meetings.findOne({MeetingId: message.payload?.meeting_id})?
for chatMessage in message.payload?.chat_history
Meteor.call("addChatToCollection", meetingId, chatMessage)
if message.header.name is "get_chat_history_reply" and message.payload.requester_id is "nodeJSapp"
unless Meteor.Meetings.findOne({MeetingId: message.payload.meeting_id})?
for chatMessage in message.payload.chat_history
addChatToCollection meetingId, chatMessage
return
if message.header?.name is "send_public_chat_message" or message.header?.name is "send_private_chat_message"
messageObject = message.payload?.message
if message.header.name is "send_public_chat_message" or message.header.name is "send_private_chat_message"
messageObject = message.payload.message
# use current_time instead of message.from_time so that the chats from Flash and HTML5 have uniform times
messageObject.from_time = message.header.current_time
addChatToCollection meetingId, messageObject
return
# use current_time instead of message.from_time so that the chats from Flash and HTML5 have uniform times
messageObject.from_time = message.header?.current_time
if message.header.name is "meeting_created_message"
meetingName = message.payload.name
intendedForRecording = message.payload.recorded
voiceConf = message.payload.voice_conf
duration = message.payload.duration
addMeetingToCollection meetingId, meetingName, intendedForRecording, voiceConf, duration
return
Meteor.call("addChatToCollection", meetingId, messageObject)
if message.header.name is "presentation_shared_message"
presentationId = message.payload.presentation?.id
# change the currently displayed presentation to presentation.current = false
Meteor.Presentations.update({"presentation.current": true, meetingId: meetingId},{$set: {"presentation.current": false}})
if message.header?.name is "meeting_created_message"
meetingName = message.payload?.name
intendedForRecording = message.payload?.recorded
voiceConf = message.payload?.voice_conf
duration = message.payload?.duration
Meteor.call("addMeetingToCollection", meetingId, meetingName, intendedForRecording, voiceConf, duration)
#update(if already present) entirely the presentation with the fresh data
removePresentationFromCollection meetingId, presentationId
addPresentationToCollection meetingId, message.payload.presentation
if message.header?.name is "presentation_shared_message"
presentationId = message.payload?.presentation?.id
# change the currently displayed presentation to presentation.current = false
Meteor.Presentations.update({"presentation.current": true, meetingId: meetingId},{$set: {"presentation.current": false}})
for slide in message.payload.presentation?.pages
addSlideToCollection meetingId, message.payload.presentation?.id, slide
if slide.current
displayThisSlide meetingId, slide.id, slide
return
#update(if already present) entirely the presentation with the fresh data
Meteor.call("removePresentationFromCollection", meetingId, presentationId)
Meteor.call("addPresentationToCollection", meetingId, message.payload?.presentation)
if message.header.name is "get_presentation_info_reply" and message.payload.requester_id is "nodeJSapp"
for presentation in message.payload.presentations
addPresentationToCollection meetingId, presentation
for slide in message.payload?.presentation?.pages
Meteor.call("addSlideToCollection", meetingId, message.payload?.presentation?.id, slide)
if slide.current
Meteor.call("displayThisSlide", meetingId, slide.id, slide)
for page in presentation.pages
#add the slide to the collection
addSlideToCollection meetingId, presentation.id, page
if message.header?.name is "get_presentation_info_reply" and message.payload?.requester_id is "nodeJSapp"
for presentation in message.payload?.presentations
Meteor.call("addPresentationToCollection", meetingId, presentation)
#request for shapes
whiteboardId = "#{presentation.id}/#{page.num}" # d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1404411622872/1
console.log "the whiteboard_id here is:" + whiteboardId
for page in presentation.pages
#add the slide to the collection
Meteor.call("addSlideToCollection", meetingId, presentation.id, page)
message =
"payload":
"meeting_id": meetingId
"requester_id": "nodeJSapp"
"whiteboard_id": whiteboardId
"header":
"timestamp": new Date().getTime()
"name": "get_whiteboard_shapes_request"
"version": "0.0.1"
#request for shapes
whiteboardId = "#{presentation.id}/#{page.num}" # d2d9a672040fbde2a47a10bf6c37b6a4b5ae187f-1404411622872/1
console.log "the whiteboard_id here is:" + whiteboardId
if whiteboardId? and meetingId?
publish Meteor.config.redis.channels.toBBBApps.whiteboard, message
else
console.log "did not have enough information to send a user_leaving_request"
return
message =
"payload":
"meeting_id": meetingId
"requester_id": "nodeJSapp"
"whiteboard_id": whiteboardId
"header":
"timestamp": new Date().getTime()
"name": "get_whiteboard_shapes_request"
"version": "0.0.1"
if message.header.name is "presentation_page_changed_message"
newSlide = message.payload.page
displayThisSlide meetingId, newSlide?.id, newSlide
return
if whiteboardId? and meetingId?
Meteor.call('publish', Meteor.config.redis.channels.toBBBApps.whiteboard, message)
else
console.log "did not have enough information to send a user_leaving_request"
if message.header.name is "get_whiteboard_shapes_reply" and message.payload.requester_id is "nodeJSapp"
for shape in message.payload.shapes
whiteboardId = shape.wb_id
addShapeToCollection meetingId, whiteboardId, shape
return
if message.header?.name is "presentation_page_changed_message"
newSlide = message.payload?.page
Meteor.call("displayThisSlide", meetingId, newSlide?.id, newSlide)
if message.header.name is "send_whiteboard_shape_message"
shape = message.payload.shape
whiteboardId = shape?.wb_id
addShapeToCollection meetingId, whiteboardId, shape
return
if message.header?.name is "get_whiteboard_shapes_reply" and message.payload?.requester_id is "nodeJSapp"
for shape in message.payload.shapes
whiteboardId = shape.wb_id
Meteor.call("addShapeToCollection", meetingId, whiteboardId, shape)
if message.header.name is "presentation_cursor_updated_message"
x = message.payload.x_percent
y = message.payload.y_percent
Meteor.Presentations.update({"presentation.current": true, meetingId: meetingId},{$set: {"pointer.x": x, "pointer.y": y}})
return
if message.header?.name is "send_whiteboard_shape_message"
shape = message.payload?.shape
whiteboardId = shape?.wb_id
Meteor.call("addShapeToCollection", meetingId, whiteboardId, shape)
if message.header.name is "whiteboard_cleared_message"
whiteboardId = message.payload.whiteboard_id
removeAllShapesFromSlide meetingId, whiteboardId
return
if message.header?.name is "presentation_cursor_updated_message"
x = message.payload?.x_percent
y = message.payload?.y_percent
Meteor.Presentations.update({"presentation.current": true, meetingId: meetingId},{$set: {"pointer.x": x, "pointer.y": y}})
if message.header.name is "undo_whiteboard_request"
whiteboardId = message.payload.whiteboard_id
shapeId = message.payload.shape_id
removeShapeFromSlide meetingId, whiteboardId, shapeId
return
if message.header?.name is "whiteboard_cleared_message"
whiteboardId = message.payload?.whiteboard_id
Meteor.call("removeAllShapesFromSlide", meetingId, whiteboardId)
if message.header.name is "presenter_assigned_message"
newPresenterId = message.payload.new_presenter_id
if newPresenterId?
# reset the previous presenter
Meteor.Users.update({"user.presenter": true, meetingId: meetingId},{$set: {"user.presenter": false}})
# set the new presenter
Meteor.Users.update({"user.userid": newPresenterId, meetingId: meetingId},{$set: {"user.presenter": true}})
return
if message.header?.name is "undo_whiteboard_request"
whiteboardId = message.payload?.whiteboard_id
shapeId = message.payload?.shape_id
Meteor.call("removeShapeFromSlide", meetingId, whiteboardId, shapeId)
if message.header.name is "presentation_page_resized_message"
slideId = message.payload.page?.id
heightRatio = message.payload.page?.height_ratio
widthRatio = message.payload.page?.width_ratio
xOffset = message.payload.page?.x_offset
yOffset = message.payload.page?.y_offset
presentationId = slideId.split("/")[0]
Meteor.Slides.update({presentationId: presentationId, "slide.current": true}, {$set: {"slide.height_ratio": heightRatio, "slide.width_ratio": widthRatio, "slide.x_offset": xOffset, "slide.y_offset": yOffset}})
return
if message.header?.name is "presenter_assigned_message"
newPresenterId = message.payload?.new_presenter_id
if newPresenterId?
# reset the previous presenter
Meteor.Users.update({"user.presenter": true, meetingId: meetingId},{$set: {"user.presenter": false}})
# set the new presenter
Meteor.Users.update({"user.userid": newPresenterId, meetingId: meetingId},{$set: {"user.presenter": true}})
if message.header.name is "user_raised_hand_message"
userId = message.payload.userid
# update the user
Meteor.Users.update({"user.userid": userId, meetingId: meetingId},{$set: {"user.raise_hand": true}}) #not sure why but message.payload.raise_hand is awlays false
return
if message.header?.name is "presentation_page_resized_message"
slideId = message.payload?.page?.id
heightRatio = message.payload?.page?.height_ratio
widthRatio = message.payload?.page?.width_ratio
xOffset = message.payload?.page?.x_offset
yOffset = message.payload?.page?.y_offset
presentationId = slideId.split("/")[0]
Meteor.Slides.update({presentationId: presentationId, "slide.current": true},
{$set: {"slide.height_ratio": heightRatio, "slide.width_ratio": widthRatio, "slide.x_offset": xOffset, "slide.y_offset": yOffset}})
if message.header.name is "user_lowered_hand_message"
userId = message.payload.userid
# update the user
Meteor.Users.update({"user.userid": userId, meetingId: meetingId},{$set: {"user.raise_hand": false}}) #not sure why but message.payload.raise_hand is awlays false
return
if message.header?.name is "user_raised_hand_message"
userId = message.payload?.userid
# update the user
Meteor.Users.update({"user.userid": userId, meetingId: meetingId},{$set: {"user.raise_hand": true}}) #not sure why but message.payload?.raise_hand is awlays false
if message.header.name is "recording_status_changed_message"
intendedForRecording = message.payload.recorded
currentlyBeingRecorded = message.payload.recording
Meteor.Meetings.update({meetingId: meetingId, intendedForRecording: intendedForRecording}, {$set: {currentlyBeingRecorded: currentlyBeingRecorded}})
return
if message.header?.name is "user_lowered_hand_message"
userId = message.payload?.userid
# update the user
Meteor.Users.update({"user.userid": userId, meetingId: meetingId},{$set: {"user.raise_hand": false}}) #not sure why but message.payload?.raise_hand is awlays false
if message.header.name in ["meeting_ended_message", "meeting_destroyed_event",
"end_and_kick_all_message", "disconnect_all_users_message"]
if Meteor.Meetings.findOne({meetingId: meetingId})?
console.log "there are #{Meteor.Users.find({meetingId: meetingId}).count()} users in the meeting"
for user in Meteor.Users.find({meetingId: meetingId}).fetch()
removeUserFromMeeting meetingId, user.userId
#TODO should we clear the chat messages for that meeting?!
unless message.header.name is "disconnect_all_users_message"
removeMeetingFromCollection meetingId
return
if message.header?.name is "recording_status_changed_message"
intendedForRecording = message.payload?.recorded
currentlyBeingRecorded = message.payload?.recording
Meteor.Meetings.update({meetingId: meetingId, intendedForRecording: intendedForRecording}, {$set: {currentlyBeingRecorded: currentlyBeingRecorded}})
# --------------------------------------------------------------------------------------------
# Private methods on server
# --------------------------------------------------------------------------------------------
if message.header?.name in ["meeting_ended_message", "meeting_destroyed_event",
"end_and_kick_all_message", "disconnect_all_users_message"]
if Meteor.Meetings.findOne({meetingId: meetingId})?
console.log "there are #{Meteor.Users.find({meetingId: meetingId}).count()} users in the meeting"
for user in Meteor.Users.find({meetingId: meetingId}).fetch()
Meteor.call("removeUserFromCollection", meetingId, user.userId)
#TODO should we clear the chat messages for that meeting?!
unless message.header?.name is "disconnect_all_users_message"
Meteor.call("removeMeetingFromCollection", meetingId)
# message should be an object
@publish = (channel, message) ->
console.log "Publishing channel=#{channel}, message=#{JSON.stringify(message)}"
if Meteor.redisPubSub?
Meteor.redisPubSub.pubClient.publish(channel, JSON.stringify(message), (err, res) ->
if err
console.log "err=" + err
)
else
console.log "\n ERROR!! Meteor.redisPubSub was undefined\n"

View File

@ -11,6 +11,15 @@ Meteor.startup(function () {
});
###
Meteor.methods
getMyInfo: (uId) ->
u = Meteor.Users.findOne("userId": uId)
if u?
{
userId: u.userId
DBID: u._id
name: u.user.name
}
Meteor.startup ->
console.log "server start"