Merge branch 'master' of https://github.com/bigbluebutton/bigbluebutton into meteor-ui

This commit is contained in:
Oleksandr Zhurbenko 2015-06-10 08:52:26 -07:00
commit fd71fdfa5a
16 changed files with 212 additions and 108 deletions

View File

@ -210,6 +210,49 @@ Handlebars.registerHelper 'whiteboardSize', (section) ->
@toggleMenu = ->
setInSession 'display_menu', !getInSession 'display_menu'
@removeFullscreenStyles = ->
$('#whiteboard-paper').removeClass('verticallyCentered')
$('#chat').removeClass('invisible')
$('#users').removeClass('invisible')
$('#navbar').removeClass('invisible')
$('.fullscreenButton').removeClass('exitFullscreenButton')
$('.fullscreenButton').addClass('whiteboardFullscreenButton')
$('.fullscreenButton i').removeClass('ion-arrow-shrink')
$('.fullscreenButton i').addClass('ion-arrow-expand')
@enterWhiteboardFullscreen = ->
element = document.getElementById('whiteboard')
if element.requestFullscreen
element.requestFullscreen()
else if element.mozRequestFullScreen
element.mozRequestFullScreen()
$('.fullscreenButton').addClass('iconFirefox') # browser-specific icon sizing
else if element.webkitRequestFullscreen
element.webkitRequestFullscreen()
$('.fullscreenButton').addClass('iconChrome') # browser-specific icon sizing
else if element.msRequestFullscreen
element.msRequestFullscreen()
$('#chat').addClass('invisible')
$('#users').addClass('invisible')
$('#navbar').addClass('invisible')
$('.fullscreenButton').removeClass('whiteboardFullscreenButton')
$('.fullscreenButton').addClass('exitFullscreenButton')
$('.fullscreenButton i').removeClass('ion-arrow-expand')
$('.fullscreenButton i').addClass('ion-arrow-shrink')
$('#whiteboard-paper').addClass('verticallyCentered')
$('#whiteboard').bind 'webkitfullscreenchange', (e) ->
if document.webkitFullscreenElement is null
$('#whiteboard').unbind('webkitfullscreenchange')
$('.fullscreenButton').removeClass('iconChrome')
removeFullscreenStyles()
redrawWhiteboard()
$(document).bind 'mozfullscreenchange', (e) -> # target is always the document in Firefox
if document.mozFullScreenElement is null
$(document).unbind('mozfullscreenchange')
$('.fullscreenButton').removeClass('iconFirefox')
removeFullscreenStyles()
redrawWhiteboard()
@closePushMenus = ->
setInSession 'display_usersList', false
setInSession 'display_menu', false
@ -308,6 +351,7 @@ Handlebars.registerHelper 'whiteboardSize', (section) ->
else
setInSession 'display_usersList', false
setInSession 'display_menu', false
TimeSync.loggingEnabled = false # suppresses the log messages from timesync
@onLoadComplete = ->
document.title = "BigBlueButton #{BBB.getMeetingName() ? 'HTML5'}"

View File

@ -395,8 +395,7 @@ body {
}
#notificationArea {
bottom:0;
height: 50%;
height: auto;
left:0;
margin: auto;
margin-top: 0px;
@ -474,3 +473,24 @@ body {
.top-bar {
line-height: 0;
}
.btn {
@media @mobile-portrait-with-keyboard, @mobile-portrait {
padding-left: 10px;
padding-right: 10px;
}
i {
@media @desktop-portrait, @landscape {
font-size: 30px;
}
@media @mobile-portrait-with-keyboard, @mobile-portrait {
font-size: 80px;
}
}
}
.verticallyCentered {
position: relative;
top: 50%;
transform: translateY(-50%);
}

View File

@ -99,3 +99,14 @@
}
}
}
// changing mute/unmute icons on hover:
.muteIcon .ion-ios-mic-outline:hover:before {
content: "\f45f";
}
.muteIcon .ion-ios-mic:hover:before {
content: "\f45f";
}
.muteIcon .ion-ios-mic-off:hover:before {
content: "\f461";
}

View File

@ -69,6 +69,7 @@
}
#whiteboard-container {
position: relative; // makes the fullscreen button's absolute position work
@media @landscape {
display: -moz-flex;
display: -ms-flexbox;
@ -168,3 +169,59 @@
#presentationProgress {
font-size: 25px;
}
.whiteboardFullscreenButton {
position: absolute;
top: 0;
right: 0;
margin-bottom: 0;
padding: 0;
&, &:hover {
background-color: transparent;
}
i {
color: black;
}
@media @landscape {
height: 50px;
width: 50px;
}
@media @mobile-portrait-with-keyboard, @mobile-portrait {
height: 100px;
width: 10%;
}
}
.exitFullscreenButton {
position: absolute;
top: 0;
right: 0;
margin-bottom: 0;
padding: 0;
&, &:hover {
background-color: transparent;
}
i {
color: black;
}
@media @landscape {
height: 50px;
width: 50px;
}
@media @mobile-portrait-with-keyboard, @mobile-portrait {
height: 5%;
width: 10%;
}
}
.iconChrome {
i {
font-size: 200%;
}
}
.iconFirefox {
i {
font-size: 500%;
}
}

View File

@ -4,7 +4,7 @@
<span>{{text}}</span>
{{else}}
{{#if i_class}}
<i class="{{i_class}}" style="font-size:30px"></i><span>{{label}}</span>
<i class="{{i_class}}"></i><span>{{label}}</span>
{{/if}}
{{/if}}
</button>

View File

@ -9,16 +9,16 @@
{{else}}
{{#if isCurrentUser userId}}
{{#if isUserMuted userId}}
<span rel="tooltip" data-placement="bottom" title="Unmute yourself">
<span class="muteIcon" rel="tooltip" data-placement="bottom" title="Unmute yourself">
<i class="ion-ios-mic-off usericon"></i>
</span>
{{else}}
{{#if isCurrentUserTalking}}
<span rel="tooltip" data-placement="bottom" title="you are talking">
<span class="muteIcon" rel="tooltip" data-placement="bottom" title="you are talking">
<i class="ion-ios-mic usericon"></i>
</span>
{{else}}
<span rel="tooltip" data-placement="bottom" title="you are talking">
<span class="muteIcon" rel="tooltip" data-placement="bottom" title="you are talking">
<i class="ion-ios-mic-outline usericon"></i>
</span>
{{/if}}

View File

@ -29,6 +29,9 @@ Template.slide.rendered = ->
wpm.scale(adjustedDimensions.width, adjustedDimensions.height)
@manuallyDisplayShapes = ->
return if Meteor.WhiteboardCleanStatus.findOne({in_progress: true})?
currentSlide = getCurrentSlideDoc()
wpm = @whiteboardPaperModel
shapes = Meteor.Shapes.find({whiteboardId: currentSlide?.slide?.id}).fetch()

View File

@ -21,76 +21,22 @@ Template.whiteboard.helpers
return ''
Template.whiteboard.events
"click .previousSlide":(event) ->
'click .previousSlide':(event) ->
BBB.goToPreviousPage()
"click .nextSlide":(event) ->
'click .nextSlide':(event) ->
BBB.goToNextPage()
'click .switchSlideButton': (event) ->
$('.tooltip').hide()
"click .fullscreenWhiteboardButton": (event, template) ->
elem = document.getElementById("whiteboard")
if elem.requestFullscreen
elem.requestFullscreen()
else if elem.msRequestFullscreen
elem.msRequestFullscreen()
else if elem.mozRequestFullScreen
elem.mozRequestFullScreen()
else if elem.webkitRequestFullscreen
elem.webkitRequestFullscreen()
$('#whiteboard-paper').addClass('invisible')
$('#chat').addClass('invisible')
$('#users').addClass('invisible')
$('#footer').addClass('invisible')
$('#navbar').addClass('invisible')
$('#main').css('padding-top', '0px')
$('html').css('height', '100%')
$('html').css('width', '100%')
$('html').css('overflow', 'hidden')
$('body').css('height', '100%')
$('body').css('width', '100%')
$('body').css('overflow', 'hidden')
setTimeout () ->
redrawWhiteboard () ->
$('#whiteboard-paper').removeClass('invisible')
$('#whiteboard-paper').addClass('vertically-centered')
, 100
'click .whiteboardFullscreenButton': (event, template) ->
enterWhiteboardFullscreen()
# Listens for the fullscreen state change (user leaves fullscreen mode)
# Chrome
$('#whiteboard').bind 'webkitfullscreenchange', (e) ->
if document.webkitFullscreenElement is null
$('#whiteboard').unbind('webkitfullscreenchange')
$('#whiteboard-paper').removeClass('vertically-centered')
$('#chat').removeClass('invisible')
$('#users').removeClass('invisible')
$('#footer').removeClass('invisible')
$('#navbar').removeClass('invisible')
$('html').css('height', '')
$('html').css('width', '')
$('html').css('overflow', '')
$('body').css('height', '')
$('body').css('width', '')
$('body').css('overflow', '')
$('#main').css('padding-top', '')
redrawWhiteboard()
# Firefox
$(document).bind 'mozfullscreenchange', (e) -> # target is always the document in Firefox
if document.mozFullScreenElement is null
$(document).unbind('mozfullscreenchange')
$('#whiteboard-paper').removeClass('vertically-centered')
$('#chat').removeClass('invisible')
$('#users').removeClass('invisible')
$('#footer').removeClass('invisible')
$('#navbar').removeClass('invisible')
$('html').css('height', '')
$('html').css('width', '')
$('html').css('overflow', '')
$('body').css('height', '')
$('body').css('width', '')
$('body').css('overflow', '')
$('#main').css('padding-top', '')
redrawWhiteboard()
'click .exitFullscreenButton': (event, template) ->
if document.exitFullscreen
document.exitFullscreen()
else if document.mozCancelFullScreen
document.mozCancelFullScreen()
else if document.webkitExitFullscreen
document.webkitExitFullscreen()

View File

@ -6,6 +6,9 @@
<div id="whiteboard-container" class="{{whiteboardSize}}">
<div id="whiteboard-paper">
</div>
{{#if isMobile}}
{{> makeButton btn_class="fullscreenButton whiteboardFullscreenButton" i_class="ion-arrow-expand"}}
{{/if}}
</div>
{{#if isCurrentUserPresenter}}
<div id="controllers">

View File

@ -4,3 +4,5 @@ Meteor.Meetings = new Meteor.Collection("meetings")
Meteor.Presentations = new Meteor.Collection("presentations")
Meteor.Shapes = new Meteor.Collection("shapes")
Meteor.Slides = new Meteor.Collection("slides")
Meteor.WhiteboardCleanStatus = new Meteor.Collection("whiteboard-clean-status")

View File

@ -55,29 +55,30 @@
Meteor.subscribe 'meetings', meetingId, onReady: =>
Meteor.subscribe 'presentations', meetingId, onReady: =>
Meteor.subscribe 'users', meetingId, userId, authToken, onError: onErrorFunction, onReady: =>
# done subscribing
onLoadComplete()
Meteor.subscribe 'whiteboard-clean-status', meetingId, onReady: =>
# done subscribing
onLoadComplete()
handleLogourUrlError = () ->
alert "Error: could not find the logoutURL"
setInSession("logoutURL", document.location.hostname)
return
# obtain the logoutURL
a = $.ajax({dataType: 'json', url: '/bigbluebutton/api/enter'})
a.done (data) ->
if data.response.logoutURL? # for a meeting with 0 users
setInSession("logoutURL", data.response.logoutURL)
handleLogourUrlError = () ->
alert "Error: could not find the logoutURL"
setInSession("logoutURL", document.location.hostname)
return
else
if data.response.logoutUrl? # for a running meeting
setInSession("logoutURL", data.response.logoutUrl)
# obtain the logoutURL
a = $.ajax({dataType: 'json', url: '/bigbluebutton/api/enter'})
a.done (data) ->
if data.response.logoutURL? # for a meeting with 0 users
setInSession("logoutURL", data.response.logoutURL)
return
else
handleLogourUrlError()
if data.response.logoutUrl? # for a running meeting
setInSession("logoutURL", data.response.logoutUrl)
return
else
handleLogourUrlError()
a.fail (data, textStatus, errorThrown) ->
handleLogourUrlError()
a.fail (data, textStatus, errorThrown) ->
handleLogourUrlError()
@render('main')

View File

@ -67,15 +67,13 @@
@removeAllShapesFromSlide = (meetingId, whiteboardId) ->
Meteor.log.info "removeAllShapesFromSlide__" + whiteboardId
if meetingId? and whiteboardId? and Meteor.Shapes.find({meetingId: meetingId, whiteboardId: whiteboardId})?
shapesOnSlide = Meteor.Shapes.find({meetingId: meetingId, whiteboardId: whiteboardId}).fetch()
Meteor.log.info "number of shapes:" + shapesOnSlide.length
for s in shapesOnSlide
Meteor.log.info "shape=" + s.shape.id
id = Meteor.Shapes.findOne({meetingId: meetingId, whiteboardId: whiteboardId, "shape.id": s.shape.id})
if id?
Meteor.Shapes.remove(id._id)
Meteor.log.info "----removed shape[" + s.shape.id + "] from " + whiteboardId
Meteor.log.info "remaining shapes on the slide:" + Meteor.Shapes.find({meetingId: meetingId, whiteboardId: whiteboardId}).fetch().length
Meteor.Shapes.remove {meetingId: meetingId, whiteboardId: whiteboardId}, ->
Meteor.log.info "clearing all shapes from slide"
# After shapes are cleared, wait 1 second and set cleaning off
Meteor.setTimeout ->
Meteor.WhiteboardCleanStatus.update({meetingId: meetingId}, {$set: {in_progress: false}})
, 1000
@removeShapeFromSlide = (meetingId, whiteboardId, shapeId) ->
shapeToRemove = Meteor.Shapes.findOne({meetingId: meetingId, whiteboardId: whiteboardId, "shape.id": shapeId})
@ -88,9 +86,13 @@
# called on server start and meeting end
@clearShapesCollection = (meetingId) ->
if meetingId?
Meteor.Shapes.remove({meetingId: meetingId}, Meteor.log.info "cleared Shapes Collection (meetingId: #{meetingId}!")
Meteor.Shapes.remove {}, ->
Meteor.log.info "cleared Shapes Collection (meetingId: #{meetingId}!"
Meteor.WhiteboardCleanStatus.update({meetingId: meetingId}, {$set: {in_progress: false}})
else
Meteor.Shapes.remove({}, Meteor.log.info "cleared Shapes Collection (all meetings)!")
Meteor.Shapes.remove {}, ->
Meteor.log.info "cleared Shapes Collection (all meetings)!"
Meteor.WhiteboardCleanStatus.update({meetingId: meetingId}, {$set: {in_progress: false}})
# --------------------------------------------------------------------------------------------
# end Private methods on server

View File

@ -70,3 +70,7 @@ Meteor.publish 'meetings', (meetingId) ->
Meteor.publish 'presentations', (meetingId) ->
Meteor.log.info "publishing presentations for #{meetingId}"
Meteor.Presentations.find({meetingId: meetingId})
Meteor.publish 'whiteboard-clean-status', (meetingId) ->
Meteor.log.info "whiteboard clean status #{meetingId}"
Meteor.WhiteboardCleanStatus.find({meetingId: meetingId})

View File

@ -219,6 +219,11 @@ class Meteor.RedisPubSub
return
if message.header.name is "get_whiteboard_shapes_reply" and message.payload.requester_id is "nodeJSapp"
# Create a whiteboard clean status or find one for the current meeting
if not Meteor.WhiteboardCleanStatus.findOne({meetingId: meetingId})?
Meteor.WhiteboardCleanStatus.insert({meetingId: meetingId, in_progress: false})
for shape in message.payload.shapes
whiteboardId = shape.wb_id
addShapeToCollection meetingId, whiteboardId, shape
@ -238,6 +243,7 @@ class Meteor.RedisPubSub
if message.header.name is "whiteboard_cleared_message"
whiteboardId = message.payload.whiteboard_id
Meteor.WhiteboardCleanStatus.update({meetingId: meetingId}, {$set: {'in_progress': true}})
removeAllShapesFromSlide meetingId, whiteboardId
return

View File

@ -2,6 +2,7 @@ Meteor.startup ->
Meteor.log.info "server start"
#remove all data
Meteor.WhiteboardCleanStatus.remove({})
clearUsersCollection()
clearChatCollection()
clearMeetingsCollection()

View File

@ -542,18 +542,22 @@ public class ParamsProcessorUtil {
public boolean isChecksumSame(String apiCall, String checksum, String queryString) {
log.debug("checksum: [{}] ; query string: [{}]", checksum, queryString);
if (StringUtils.isEmpty(securitySalt)) {
log.warn("Security is disabled in this service. Make sure this is intentional.");
return true;
}
// handle either checksum as first or middle / end parameter
// TODO: this is hackish - should be done better
queryString = queryString.replace("&checksum=" + checksum, "");
queryString = queryString.replace("checksum=" + checksum + "&", "");
queryString = queryString.replace("checksum=" + checksum, "");
if( queryString == null ) {
queryString = "";
} else {
// handle either checksum as first or middle / end parameter
// TODO: this is hackish - should be done better
queryString = queryString.replace("&checksum=" + checksum, "");
queryString = queryString.replace("checksum=" + checksum + "&", "");
queryString = queryString.replace("checksum=" + checksum, "");
}
log.debug("query string after checksum removed: [{}]", queryString);
String cs = DigestUtils.shaHex(apiCall + queryString + securitySalt);
log.debug("our checksum: [{}], client: [{}]", cs, checksum);