diff --git a/app/assets/javascripts/channels/meeting_updates.js b/app/assets/javascripts/channels/meeting_updates.js new file mode 100644 index 00000000..be34eaf4 --- /dev/null +++ b/app/assets/javascripts/channels/meeting_updates.js @@ -0,0 +1,39 @@ +(function() { + + var sessionStatusRefresh = function(url) { + $.get(url + "/session_status_refresh", function(html) { + $(".join-form-wrapper").html(html); + }); + } + + var initRooms = function() { + App.messages = App.cable.subscriptions.create({ + channel: 'MeetingUpdatesChannel', + username: getRoomName() + }, + { + received: function(data) { + if (data.action === 'moderator_joined') { + if (!Meeting.getInstance().getModJoined()) { + Meeting.getInstance().setModJoined(true); + if (Meeting.getInstance().getWaitingForMod()) { + loopJoin(); + } else { + sessionStatusRefresh($('.meeting-url').val()); + } + } + } else if (data.action === 'meeting_ended') { + sessionStatusRefresh($('.meeting-url').val()); + } + } + }); + }; + + $(document).on("turbolinks:load", function() { + if ($("body[data-controller=landing]").get(0)) { + if ($("body[data-action=rooms]").get(0)) { + initRooms(); + } + } + }); +}).call(this); diff --git a/app/assets/javascripts/channels/moderator_joins.js b/app/assets/javascripts/channels/moderator_joins.js deleted file mode 100644 index fdea002c..00000000 --- a/app/assets/javascripts/channels/moderator_joins.js +++ /dev/null @@ -1,27 +0,0 @@ -(function() { - - var initRooms = function() { - App.messages = App.cable.subscriptions.create({ - channel: 'ModeratorJoinsChannel', - username: getRoomName() - }, - { - received: function(data) { - if (!Meeting.getInstance().getModJoined()) { - Meeting.getInstance().setModJoined(true); - if (Meeting.getInstance().getWaitingForMod()) { - loopJoin(); - } - } - } - }); - }; - - $(document).on("turbolinks:load", function() { - if ($("body[data-controller=landing]").get(0)) { - if ($("body[data-action=rooms]").get(0)) { - initRooms(); - } - } - }); -}).call(this); diff --git a/app/assets/javascripts/landing.js b/app/assets/javascripts/landing.js index 32a2b18f..42e42055 100644 --- a/app/assets/javascripts/landing.js +++ b/app/assets/javascripts/landing.js @@ -15,7 +15,7 @@ var init = function() { - $('.meeting-join').click (function (event) { + $('.center-panel').on ('click', '.meeting-join', function (event) { var url = $('.meeting-url').val(); var name = $('.meeting-user-name').val(); Meeting.getInstance().setURL(url); @@ -34,7 +34,19 @@ }); }); - $('.meeting-url-copy').click (function (e) { + $('.center-panel').on ('click', '.meeting-end', function (event) { + var jqxhr = Meeting.getInstance().endMeeting(); + var btn = $(this); + btn.prop("disabled", true); + jqxhr.done(function(data) { + + }); + jqxhr.fail(function(xhr, status, error) { + console.info("meeting end failed"); + }); + }); + + $('.center-panel').on ('click', '.meeting-url-copy', function (event) { meetingURL = $('.meeting-url'); meetingURL.select(); document.execCommand("copy"); diff --git a/app/assets/javascripts/shared.js b/app/assets/javascripts/shared.js index b7d369a4..82e585d8 100644 --- a/app/assets/javascripts/shared.js +++ b/app/assets/javascripts/shared.js @@ -36,6 +36,13 @@ class Meeting { }); }; + endMeeting() { + return $.ajax({ + url: this.url + "/end", + type: 'DELETE' + }); + } + setURL(url) { this.url = url; } diff --git a/app/channels/meeting_updates_channel.rb b/app/channels/meeting_updates_channel.rb new file mode 100644 index 00000000..8e48fba0 --- /dev/null +++ b/app/channels/meeting_updates_channel.rb @@ -0,0 +1,5 @@ +class MeetingUpdatesChannel < ApplicationCable::Channel + def subscribed + stream_from "#{params[:username]}_meeting_updates_channel" + end +end diff --git a/app/channels/moderator_joins_channel.rb b/app/channels/moderator_joins_channel.rb deleted file mode 100644 index 1d5cd143..00000000 --- a/app/channels/moderator_joins_channel.rb +++ /dev/null @@ -1,5 +0,0 @@ -class ModeratorJoinsChannel < ApplicationCable::Channel - def subscribed - stream_from "moderator_#{params[:username]}_join_channel" - end -end diff --git a/app/controllers/bbb_controller.rb b/app/controllers/bbb_controller.rb index bc65542e..f4eee6e4 100644 --- a/app/controllers/bbb_controller.rb +++ b/app/controllers/bbb_controller.rb @@ -1,7 +1,8 @@ class BbbController < ApplicationController include BbbApi - before_action :authorize_owner_recording, only: [:update_recordings, :delete_recordings] + before_action :authorize_recording_owner!, only: [:update_recordings, :delete_recordings] + before_action :load_and_authorize_room_owner!, only: [:end] # GET /:resource/:id/join def join @@ -28,20 +29,29 @@ class BbbController < ApplicationController ) if bbb_res[:returncode] && current_user && current_user == user - ActionCable.server.broadcast "moderator_#{user.username}_join_channel", - moderator: "joined" + ActionCable.server.broadcast "#{user.username}_meeting_updates_channel", + action: 'moderator_joined', + moderator: 'joined' end render_bbb_response bbb_res, bbb_res[:response] end end + # DELETE /rooms/:id/end + def end + load_and_authorize_room_owner! + + bbb_res = bbb_end_meeting @user.username + if bbb_res[:returncode] + EndMeetingJob.perform_later(@user.username) + end + render_bbb_response bbb_res + end + # GET /rooms/:id/recordings def recordings - @user = User.find_by username: params[:id] - if !@user - render head(:not_found) && return - end + load_room! bbb_res = bbb_get_recordings @user.username render_bbb_response bbb_res, bbb_res[:recordings] @@ -64,18 +74,27 @@ class BbbController < ApplicationController private - def authorize_owner_recording - user = User.find_by username: params[:id] - if !user + def load_room! + @user = User.find_by username: params[:id] + if !@user render head(:not_found) && return - elsif !current_user || current_user != user + end + end + + def load_and_authorize_room_owner! + load_room! + + if !current_user || current_user != @user render head(:unauthorized) && return end + end + + def authorize_recording_owner! + load_and_authorize_room_owner! recordings = bbb_get_recordings(params[:id])[:recordings] recordings.each do |recording| if recording[:recordID] == params[:record_id] - @user = user return true end end @@ -87,6 +106,6 @@ class BbbController < ApplicationController @message = bbb_res[:message] @status = bbb_res[:status] @response = response - render status: @status && return + render status: @status end end diff --git a/app/controllers/landing_controller.rb b/app/controllers/landing_controller.rb index 80601fa9..e52e29c3 100644 --- a/app/controllers/landing_controller.rb +++ b/app/controllers/landing_controller.rb @@ -1,4 +1,5 @@ class LandingController < ApplicationController + include BbbApi def index if params[:resource] == 'meetings' @@ -14,6 +15,17 @@ class LandingController < ApplicationController render layout: false end + def session_status_refresh + @user = User.find_by(username: params[:id]) + if @user.nil? + render head(:not_found) && return + end + + @meeting_running = bbb_get_meeting_info(@user.username)[:returncode] + + render layout: false + end + def admin? @user && @user == current_user end @@ -29,11 +41,15 @@ class LandingController < ApplicationController def render_room params[:action] = 'rooms' + @user = User.find_by(username: params[:id]) if @user.nil? redirect_to root_path return end + + @meeting_running = bbb_get_meeting_info(@user.username)[:returncode] + render :action => 'rooms' end diff --git a/app/jobs/end_meeting_job.rb b/app/jobs/end_meeting_job.rb new file mode 100644 index 00000000..c0e6b718 --- /dev/null +++ b/app/jobs/end_meeting_job.rb @@ -0,0 +1,25 @@ +class EndMeetingJob < ApplicationJob + include BbbApi + + queue_as :default + + def perform(room) + tries = 0 + sleep_time = 2 + + while tries < 4 + bbb_res = bbb_get_meeting_info(room) + + if !bbb_res[:returncode] + ActionCable.server.broadcast "#{room}_meeting_updates_channel", + action: 'meeting_ended' + break + end + + sleep sleep_time + sleep_time = sleep_time * 2 + tries += 1 + end + + end +end diff --git a/app/lib/bbb_api.rb b/app/lib/bbb_api.rb index a6ba019e..890605de 100644 --- a/app/lib/bbb_api.rb +++ b/app/lib/bbb_api.rb @@ -11,6 +11,10 @@ module BbbApi @bbb ||= BigBlueButton::BigBlueButtonApi.new(bbb_endpoint + "api", bbb_secret, "0.8", true) end + def bbb_meeting_id(id) + Digest::SHA1.hexdigest(Rails.application.secrets[:secret_key_base]+id).to_s + end + def random_password(length) o = [('a'..'z'), ('A'..'Z')].map { |i| i.to_a }.flatten password = (0...length).map { o[rand(o.length)] }.join @@ -26,7 +30,7 @@ module BbbApi if !bbb return call_invalid_res else - meeting_id = (Digest::SHA1.hexdigest(Rails.application.secrets[:secret_key_base]+meeting_token)).to_s + meeting_id = bbb_meeting_id(meeting_token) # See if the meeting is running begin @@ -63,10 +67,17 @@ module BbbApi password = bbb_meeting_info[:attendeePW] end join_url = bbb.join_meeting_url(meeting_id, full_name, password ) - return success_res(join_url) + return success_join_res(join_url) end end + def bbb_get_meeting_info(id) + meeting_id = bbb_meeting_id(id) + response_data = bbb.get_meeting_info(meeting_id, nil) + rescue BigBlueButton::BigBlueButtonException => exc + response_data = bbb_exception_res exc + end + def bbb_get_recordings(meeting_id, record_id=nil) options={} if record_id @@ -124,6 +135,22 @@ module BbbApi res end + def bbb_end_meeting(id) + # get meeting info for moderator password + meeting_id = bbb_meeting_id(id) + bbb_meeting_info = bbb.get_meeting_info(meeting_id, nil) + + response_data = if bbb_meeting_info.is_a?(Hash) && bbb_meeting_info[:moderatorPW] + bbb.end_meeting(meeting_id, bbb_meeting_info[:moderatorPW]) + else + {} + end + response_data[:status] = :ok + response_data + rescue BigBlueButton::BigBlueButtonException => exc + response_data = bbb_exception_res exc + end + def bbb_update_recordings(id, published) bbb_safe_execute :publish_recordings, id, published end @@ -147,7 +174,7 @@ module BbbApi response_data end - def success_res(join_url) + def success_join_res(join_url) { returncode: true, messageKey: "ok", @@ -178,10 +205,19 @@ module BbbApi end def bbb_exception_res(exc) - { + res = { returncode: false, messageKey: 'BBB'+exc.key.capitalize.underscore, message: exc.message, + status: :unprocessable_entity + } + if res[:messageKey] == 'BBBnotfound' + res[:status] = :not_found + end + res + rescue + { + returncode: false, status: :internal_server_error } end diff --git a/app/views/bbb/end.jbuilder b/app/views/bbb/end.jbuilder new file mode 100644 index 00000000..0f12a451 --- /dev/null +++ b/app/views/bbb/end.jbuilder @@ -0,0 +1 @@ +json.partial! 'bbb', messageKey: @messageKey, message: @message, status: @status diff --git a/app/views/landing/meetings.html.erb b/app/views/landing/meetings.html.erb index 8e927a57..311c1595 100644 --- a/app/views/landing/meetings.html.erb +++ b/app/views/landing/meetings.html.erb @@ -25,7 +25,7 @@ <%= render layout: 'shared/center_panel' do %>