diff --git a/bigbluebutton-html5/app/client/globals.coffee b/bigbluebutton-html5/app/client/globals.coffee index d8d520acb2..6b6071e4ef 100755 --- a/bigbluebutton-html5/app/client/globals.coffee +++ b/bigbluebutton-html5/app/client/globals.coffee @@ -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'}" diff --git a/bigbluebutton-html5/app/client/stylesheets/style.less b/bigbluebutton-html5/app/client/stylesheets/style.less index 196f880796..c97d0e5a91 100755 --- a/bigbluebutton-html5/app/client/stylesheets/style.less +++ b/bigbluebutton-html5/app/client/stylesheets/style.less @@ -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%); +} diff --git a/bigbluebutton-html5/app/client/stylesheets/users.less b/bigbluebutton-html5/app/client/stylesheets/users.less index 3a48f5bd31..c5027d922f 100755 --- a/bigbluebutton-html5/app/client/stylesheets/users.less +++ b/bigbluebutton-html5/app/client/stylesheets/users.less @@ -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"; +} diff --git a/bigbluebutton-html5/app/client/stylesheets/whiteboard.less b/bigbluebutton-html5/app/client/stylesheets/whiteboard.less index c2dfd72766..938b262a4c 100755 --- a/bigbluebutton-html5/app/client/stylesheets/whiteboard.less +++ b/bigbluebutton-html5/app/client/stylesheets/whiteboard.less @@ -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%; + } +} diff --git a/bigbluebutton-html5/app/client/views/sharedTemplates.html b/bigbluebutton-html5/app/client/views/sharedTemplates.html index 48a9005bd5..33901af708 100755 --- a/bigbluebutton-html5/app/client/views/sharedTemplates.html +++ b/bigbluebutton-html5/app/client/views/sharedTemplates.html @@ -4,7 +4,7 @@ {{text}} {{else}} {{#if i_class}} - {{label}} + {{label}} {{/if}} {{/if}} diff --git a/bigbluebutton-html5/app/client/views/users/user_item.html b/bigbluebutton-html5/app/client/views/users/user_item.html index a0750f6ead..f1e7fabe01 100755 --- a/bigbluebutton-html5/app/client/views/users/user_item.html +++ b/bigbluebutton-html5/app/client/views/users/user_item.html @@ -9,16 +9,16 @@ {{else}} {{#if isCurrentUser userId}} {{#if isUserMuted userId}} - + {{else}} {{#if isCurrentUserTalking}} - + {{else}} - + {{/if}} diff --git a/bigbluebutton-html5/app/client/views/whiteboard/slide.coffee b/bigbluebutton-html5/app/client/views/whiteboard/slide.coffee index 883fafb1e7..cf53dcea56 100755 --- a/bigbluebutton-html5/app/client/views/whiteboard/slide.coffee +++ b/bigbluebutton-html5/app/client/views/whiteboard/slide.coffee @@ -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() diff --git a/bigbluebutton-html5/app/client/views/whiteboard/whiteboard.coffee b/bigbluebutton-html5/app/client/views/whiteboard/whiteboard.coffee index a09de921d9..a07c3cf596 100755 --- a/bigbluebutton-html5/app/client/views/whiteboard/whiteboard.coffee +++ b/bigbluebutton-html5/app/client/views/whiteboard/whiteboard.coffee @@ -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() diff --git a/bigbluebutton-html5/app/client/views/whiteboard/whiteboard.html b/bigbluebutton-html5/app/client/views/whiteboard/whiteboard.html index 330aa39dc6..8acdbb59b2 100755 --- a/bigbluebutton-html5/app/client/views/whiteboard/whiteboard.html +++ b/bigbluebutton-html5/app/client/views/whiteboard/whiteboard.html @@ -6,6 +6,9 @@
+ {{#if isMobile}} + {{> makeButton btn_class="fullscreenButton whiteboardFullscreenButton" i_class="ion-arrow-expand"}} + {{/if}}
{{#if isCurrentUserPresenter}}
diff --git a/bigbluebutton-html5/app/collections/collections.coffee b/bigbluebutton-html5/app/collections/collections.coffee index 49987e25c0..200fe01e36 100755 --- a/bigbluebutton-html5/app/collections/collections.coffee +++ b/bigbluebutton-html5/app/collections/collections.coffee @@ -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") \ No newline at end of file diff --git a/bigbluebutton-html5/app/lib/router.coffee b/bigbluebutton-html5/app/lib/router.coffee index 791efcb5ed..575f6371d2 100755 --- a/bigbluebutton-html5/app/lib/router.coffee +++ b/bigbluebutton-html5/app/lib/router.coffee @@ -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') diff --git a/bigbluebutton-html5/app/server/collection_methods/shapes.coffee b/bigbluebutton-html5/app/server/collection_methods/shapes.coffee index 2f73effb9d..d4c0e1607e 100755 --- a/bigbluebutton-html5/app/server/collection_methods/shapes.coffee +++ b/bigbluebutton-html5/app/server/collection_methods/shapes.coffee @@ -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 diff --git a/bigbluebutton-html5/app/server/publish.coffee b/bigbluebutton-html5/app/server/publish.coffee index ef7510d74d..b1500de47f 100755 --- a/bigbluebutton-html5/app/server/publish.coffee +++ b/bigbluebutton-html5/app/server/publish.coffee @@ -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}) diff --git a/bigbluebutton-html5/app/server/redispubsub.coffee b/bigbluebutton-html5/app/server/redispubsub.coffee index 3d81454bb1..07bebcf6fe 100755 --- a/bigbluebutton-html5/app/server/redispubsub.coffee +++ b/bigbluebutton-html5/app/server/redispubsub.coffee @@ -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 diff --git a/bigbluebutton-html5/app/server/server.coffee b/bigbluebutton-html5/app/server/server.coffee index 125492a669..e85cf7dd06 100755 --- a/bigbluebutton-html5/app/server/server.coffee +++ b/bigbluebutton-html5/app/server/server.coffee @@ -2,6 +2,7 @@ Meteor.startup -> Meteor.log.info "server start" #remove all data + Meteor.WhiteboardCleanStatus.remove({}) clearUsersCollection() clearChatCollection() clearMeetingsCollection() diff --git a/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java b/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java index 7f3fba3a61..eef9e30b16 100755 --- a/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java +++ b/bigbluebutton-web/src/java/org/bigbluebutton/api/ParamsProcessorUtil.java @@ -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);