diff --git a/Gemfile b/Gemfile index b5df803e..715bf098 100644 --- a/Gemfile +++ b/Gemfile @@ -89,3 +89,6 @@ gem 'yt', '~> 0.28.0' # Simple HTTP client. gem 'faraday' + +# For device detection to determine BigBlueButton client. +gem 'browser' diff --git a/Gemfile.lock b/Gemfile.lock index 148fa847..f542f067 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -46,6 +46,7 @@ GEM sass (~> 3.2) bootstrap-social-rails (4.12.0) railties (>= 3.1) + browser (2.5.1) builder (3.2.3) byebug (9.0.6) climate_control (0.2.0) @@ -279,6 +280,7 @@ DEPENDENCIES bigbluebutton-api-ruby bootstrap-sass (= 3.3.0.0) bootstrap-social-rails (~> 4.12) + browser byebug coffee-rails (~> 4.2) dotenv-rails diff --git a/app/assets/javascripts/channels/meeting_updates.js b/app/assets/javascripts/channels/meeting_updates.js index 505cf143..4580490e 100644 --- a/app/assets/javascripts/channels/meeting_updates.js +++ b/app/assets/javascripts/channels/meeting_updates.js @@ -55,6 +55,8 @@ body: I18n.user_waiting_body.replace(/%{user}/, data.user).replace(/%{meeting}/, '"'+data.meeting_name+'"') }); } + } else if(data.action === 'unable_to_join') { + showAlert(I18n.unable_to_join_mobile, 6000) } } }); diff --git a/app/assets/javascripts/landing.js b/app/assets/javascripts/landing.js index 92857f35..1f59002e 100644 --- a/app/assets/javascripts/landing.js +++ b/app/assets/javascripts/landing.js @@ -60,7 +60,7 @@ jqxhr.done(function(data) { if (data.messageKey === 'wait_for_moderator') { waitForModerator(Meeting.getInstance().getURL()); - } else { + } else if (data.messageKey === 'ok') { $(location).attr("href", data.response.join_url); } }); diff --git a/app/controllers/bbb_controller.rb b/app/controllers/bbb_controller.rb index 01491635..a87457f0 100644 --- a/app/controllers/bbb_controller.rb +++ b/app/controllers/bbb_controller.rb @@ -93,7 +93,10 @@ class BbbController < ApplicationController if bbb_res[:returncode] && current_user == user JoinMeetingJob.perform_later(user, params[:id], base_url) WaitingList.empty(options[:room_owner], options[:meeting_name]) - # user will be waiting for a moderator + # the user can't join because they are on mobile and HTML5 is not enabled. + elsif bbb_res[:messageKey] == 'unable_to_join' + NotifyUserCantJoinJob.perform_later(user.encrypted_id, params[:id], params[:name]) + # user will be waiting for a moderator else NotifyUserWaitingJob.perform_later(user.encrypted_id, params[:id], params[:name]) end diff --git a/app/helpers/landing_helper.rb b/app/helpers/landing_helper.rb index b2b3e74a..4c74f406 100644 --- a/app/helpers/landing_helper.rb +++ b/app/helpers/landing_helper.rb @@ -16,4 +16,8 @@ module LandingHelper + def html5_enabled? + Rails.configuration.html5_enabled + end + end diff --git a/app/jobs/notify_user_cant_join_job.rb b/app/jobs/notify_user_cant_join_job.rb new file mode 100644 index 00000000..69da5dfe --- /dev/null +++ b/app/jobs/notify_user_cant_join_job.rb @@ -0,0 +1,24 @@ +# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/. +# +# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below). +# +# This program is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free Software +# Foundation; either version 3.0 of the License, or (at your option) any later +# version. +# +# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with BigBlueButton; if not, see . + +class NotifyUserCantJoinJob < ApplicationJob + queue_as :default + + def perform(room, meeting, user) + payload = { action: 'unable_to_join', user: user, meeting_name: meeting } + ActionCable.server.broadcast "#{room}-#{meeting}_meeting_updates_channel", payload + end +end diff --git a/app/lib/bbb_api.rb b/app/lib/bbb_api.rb index b863ef1e..fccf0216 100644 --- a/app/lib/bbb_api.rb +++ b/app/lib/bbb_api.rb @@ -53,114 +53,109 @@ module BbbApi if !bbb return call_invalid_res else - meeting_id = bbb_meeting_id(meeting_token) + # Verify HTML5 is running. + html5_running = Rails.configuration.html5_enabled + + # Determine if the user is on mobile. + browser = Browser.new(request.user_agent) + mobile = browser.device.tablet? + + # If the user is on mobile and the BigBlueButton server isn't running the HTML5 client, + # they can't join or create meetings. + if mobile and !html5_running then + return unable_to_join_res + else + meeting_id = bbb_meeting_id(meeting_token) - # See if the meeting is running - begin - bbb_meeting_info = bbb.get_meeting_info(meeting_id, nil) - rescue BigBlueButton::BigBlueButtonException => exc - # This means that is not created + # See if the meeting is running + begin + bbb_meeting_info = bbb.get_meeting_info(meeting_id, nil) + rescue BigBlueButton::BigBlueButtonException => exc + # This means that is not created - if options[:wait_for_moderator] && !options[:user_is_moderator] + if options[:wait_for_moderator] && !options[:user_is_moderator] + return wait_moderator_res + end + + logger.info "Message for the log file #{exc.key}: #{exc.message}" + + # Prepare parameters for create + logout_url = options[:meeting_logout_url] || "#{request.base_url}" + moderator_password = random_password(12) + viewer_password = random_password(12) + meeting_options = { + record: options[:meeting_recorded].to_s, + logoutURL: logout_url, + moderatorPW: moderator_password, + attendeePW: viewer_password, + moderatorOnlyMessage: options[:moderator_message], + "meta_#{BbbApi::META_LISTED}": false, + "meta_#{BbbApi::META_TOKEN}": meeting_token + } + + meeting_options.merge!( + { "meta_#{BbbApi::META_HOOK_URL}": options[:hook_url] } + ) if options[:hook_url] + + # these parameters are used to filter recordings by room and meeting + meeting_options.merge!( + { "meta_room-id": options[:room_owner], + "meta_meeting-name": options[:meeting_name]} + ) if options[:room_owner] + + # Only register webhooks if they are enabled it's not a guest meeting. + if Rails.configuration.use_webhooks && params[:resource] == 'rooms' + webhook_register(options[:hook_url], meeting_id) + end + + # Create the meeting + begin + bbb.create_meeting(options[:meeting_name], meeting_id, meeting_options) + rescue BigBlueButton::BigBlueButtonException => exc + logger.info "BBB error on create #{exc.key}: #{exc.message}" + end + + # And then get meeting info + bbb_meeting_info = bbb.get_meeting_info( meeting_id, nil ) + end + + if options[:wait_for_moderator] && !options[:user_is_moderator] && bbb_meeting_info[:moderatorCount] <= 0 return wait_moderator_res end - logger.info "Message for the log file #{exc.key}: #{exc.message}" - - # Prepare parameters for create - logout_url = options[:meeting_logout_url] || "#{request.base_url}" - moderator_password = random_password(12) - viewer_password = random_password(12) - meeting_options = { - record: options[:meeting_recorded].to_s, - logoutURL: logout_url, - moderatorPW: moderator_password, - attendeePW: viewer_password, - moderatorOnlyMessage: options[:moderator_message], - "meta_#{BbbApi::META_LISTED}": false, - "meta_#{BbbApi::META_TOKEN}": meeting_token - } - - meeting_options.merge!( - { "meta_#{BbbApi::META_HOOK_URL}": options[:hook_url] } - ) if options[:hook_url] - - # these parameters are used to filter recordings by room and meeting - meeting_options.merge!( - { "meta_room-id": options[:room_owner], - "meta_meeting-name": options[:meeting_name]} - ) if options[:room_owner] - - # Only register webhooks if they are enabled it's not a guest meeting. - if Rails.configuration.use_webhooks && params[:resource] == 'rooms' - webhook_register(options[:hook_url], meeting_id) + # Get the join url + if (options[:user_is_moderator]) + password = bbb_meeting_info[:moderatorPW] + else + password = bbb_meeting_info[:attendeePW] end - - # Create the meeting - begin - bbb.create_meeting(options[:meeting_name], meeting_id, meeting_options) - rescue BigBlueButton::BigBlueButtonException => exc - logger.info "BBB error on create #{exc.key}: #{exc.message}" - end - - # And then get meeting info - bbb_meeting_info = bbb.get_meeting_info( meeting_id, nil ) - end - - if options[:wait_for_moderator] && !options[:user_is_moderator] && bbb_meeting_info[:moderatorCount] <= 0 - return wait_moderator_res - end - - # Get the join url - if (options[:user_is_moderator]) - password = bbb_meeting_info[:moderatorPW] - else - password = bbb_meeting_info[:attendeePW] - end - - # Determine which client to join as. - if current_user.nil? - use_html5 = Rails.configuration.use_html5_by_default - else - use_html5 = current_user.use_html5 - end - - # Verify HTML5 is running. - html5_running = Faraday.get(bbb_endpoint.gsub('bigbluebutton/', 'html5client/check')).status == 200 - - # Determine if the user is on mobile. - mobile = session[:mobile_param].nil? ? session[:mobile_param] == "1" : request.user_agent =~ /Mobile|webOS/ - - can_join = true - - # Restrict client if needed. - if mobile && html5_running - # Must use HTML5 because they are on mobile. - use_html5 = true - elsif mobile && !html5_running - # The user cannot join. - can_join = false - elsif !mobile && !html5_running - # HTML5 is not running, so must use Flash. - use_html5 = false - end - # Generate the join URL. - if can_join + # Determine which client to join as. + if current_user.nil? + use_html5 = Rails.configuration.use_html5_by_default + else + use_html5 = current_user.use_html5 + end + + # Restrict client if needed. + if mobile && html5_running + # Must use HTML5 because they are on mobile. + use_html5 = true + elsif !mobile && !html5_running + # HTML5 is not running, so must use Flash. + use_html5 = false + end + + # Generate the join URL. if use_html5 clientURL = bbb_endpoint.gsub('bigbluebutton/', 'html5client/join') join_url = bbb.join_meeting_url(meeting_id, full_name, password, {clientURL: clientURL}) else join_url = bbb.join_meeting_url(meeting_id, full_name, password) end - else - # This still creates the meeting, just doesn't let them join. - # TODO: Notify the user that the join failed. - #ActionCable.server.broadcast "#{current_user.encrypted_id}-#{meeting_id}_meeting_updates_channel", - # action: 'mobile_html5_join_fail' - end - return success_join_res(join_url) + return success_join_res(join_url) + end end end @@ -343,6 +338,15 @@ module BbbApi } } end + + def unable_to_join_res + { + returncode: false, + messageKey: "unable_to_join", + message: "Unable to join.", + status: :ok + } + end def wait_moderator_res { diff --git a/app/views/landing/preferences.html.erb b/app/views/landing/preferences.html.erb index d69c459b..3871cb75 100644 --- a/app/views/landing/preferences.html.erb +++ b/app/views/landing/preferences.html.erb @@ -35,9 +35,13 @@

<%= t('prefered_client') %>

-

<%= t('currently_joining_with', client: @user.use_html5 ? t('client_html5') : t('client_flash')) %>

- <%= form_for @user do |f| %> - <%= f.submit t('switch_clients'), class: 'btn btn-info' %> + <% if html5_enabled? %> +

<%= t('currently_joining_with', client: @user.use_html5 ? t('client_html5') : t('client_flash')) %>

+ <%= form_for @user do |f| %> + <%= f.submit t('switch_clients'), class: 'btn btn-info' %> + <% end %> + <% else %> +

<%= t('html5_not_enabled') %>

<% end %>
<% else %> diff --git a/config/initializers/html5_client.rb b/config/initializers/html5_client.rb new file mode 100644 index 00000000..37fdd8de --- /dev/null +++ b/config/initializers/html5_client.rb @@ -0,0 +1,19 @@ +# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/. +# +# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below). +# +# This program is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free Software +# Foundation; either version 3.0 of the License, or (at your option) any later +# version. +# +# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with BigBlueButton; if not, see . + +# Send a request to check if the HTML5 client is enabled on the BigBlueButton server. +res = Faraday.get(Rails.configuration.bigbluebutton_endpoint.gsub('bigbluebutton/', 'html5client/check')) +Rails.application.config.html5_enabled = res.status == 200 diff --git a/config/locales/en-us.yml b/config/locales/en-us.yml index 204a31a4..d7e2486e 100644 --- a/config/locales/en-us.yml +++ b/config/locales/en-us.yml @@ -70,6 +70,7 @@ en-US: recording_unpublished: Recording was unpublished share: Share successful_upload: Recording successfully uploaded to Youtube! + unable_to_join_mobile: This BigBlueButton server does not use the HTML5 client, which means you are unable to join on mobile. unpublish_recording: Hide recording unlisted: Unlisted unpublished: No one @@ -80,7 +81,7 @@ en-US: client_flash: Flash copied: Copied copy_error: Use Ctrl-c to copy - currently_joining_with: Currently joining with %{client} client. + currently_joining_with: Currently prefering the %{client} client. create_your_session: Create your own meeting date_recorded: Date disallowed_characters_msg: Characters not allowed in meeting name $&, @@ -97,6 +98,7 @@ en-US: hi_all: Hi Everyone home_page: Home page home_title: Welcome to BigBlueButton + html5_not_enabled: This server is not HTML5 enabled, so you must use the Flash client. invalid_file: You may only upload an image file (jpg, gif, png). invalid_login: Invalid log in credentials. invite: Invite