diff --git a/app/assets/javascripts/channels/meeting_updates.js b/app/assets/javascripts/channels/meeting_updates.js index ddde60f8..0cf1ba32 100644 --- a/app/assets/javascripts/channels/meeting_updates.js +++ b/app/assets/javascripts/channels/meeting_updates.js @@ -62,13 +62,13 @@ }; $(document).on("turbolinks:load", function() { + // disable meeting updates if enabled from a previous page + if (App.meeting_update) { + disableMeetingUpdates(); + } if ($("body[data-controller=landing]").get(0)) { if ($("body[data-action=rooms]").get(0)) { - // disable meeting updates if enabled from a previous page - if (App.meeting_update) { - disableMeetingUpdates(); - } - if ($(".page-wrapper.rooms").data('main-room') === false) { + if (!$(".page-wrapper.rooms").data('main-room')) { enableMeetingUpdates(); } } diff --git a/app/assets/javascripts/channels/recording_update.js b/app/assets/javascripts/channels/recording_update.js index 2b517ccd..eb8a93c4 100644 --- a/app/assets/javascripts/channels/recording_update.js +++ b/app/assets/javascripts/channels/recording_update.js @@ -16,10 +16,11 @@ (function() { - var initRooms = function() { - App.messages = App.cable.subscriptions.create({ + var enableRecordingUpdates = function() { + App.recording_update = App.cable.subscriptions.create({ channel: 'RecordingUpdatesChannel', - encrypted_id: $(".page-wrapper").data('id') + admin_id: $(".page-wrapper.rooms").data('admin-id'), + meeting_id: $(".page-wrapper.rooms").data('id') }, { received: function(data) { @@ -57,10 +58,19 @@ }); }; + var disableRecordingUpdates = function() { + App.recording_update.unsubscribe(); + delete App.recording_update + }; + $(document).on("turbolinks:load", function() { + // disable recording updates if enabled from a previous page + if (App.recording_update) { + disableRecordingUpdates(); + } if ($("body[data-controller=landing]").get(0)) { if ($("body[data-action=rooms]").get(0)) { - initRooms(); + enableRecordingUpdates(); } } }); diff --git a/app/assets/javascripts/recordings.coffee b/app/assets/javascripts/recordings.coffee index af206b5f..25696d27 100644 --- a/app/assets/javascripts/recordings.coffee +++ b/app/assets/javascripts/recordings.coffee @@ -34,6 +34,7 @@ class @Recordings }, columns: [ { data: "start_time" }, + { data: "name", visible: $(".page-wrapper.rooms").data('main-room') }, { data: "previews", orderable: false }, { data: "duration", orderable: false }, { data: "playbacks", orderable: false }, @@ -55,7 +56,7 @@ class @Recordings return data }, { - targets: 1, + targets: 2, render: (data, type, row) -> if type == 'display' str = '' @@ -66,7 +67,7 @@ class @Recordings return data }, { - targets: 3, + targets: 4, render: (data, type, row) -> if type == 'display' str = '' @@ -157,7 +158,7 @@ class @Recordings # refresh the recordings from the server refresh: -> table_api = this.table.api() - $.get "/rooms/"+Meeting.getInstance().getAdminId()+"/recordings", (data) => + $.get @getRecordingsURL(), (data) => @setOwner(data.is_owner) if !@owner table_api.column(-1).visible(false) @@ -172,11 +173,12 @@ class @Recordings # setup click handlers for the action buttons setupActionHandlers: -> table_api = this.table.api() + recordingsObject = this @getTable().on 'click', '.recording-update', (event) -> btn = $(this) row = table_api.row($(this).closest('tr')).data() - url = $('.meeting-url').val() + url = recordingsObject.getRecordingsURL() id = row.id published = btn.data('visibility') == "unlisted" || @@ -189,7 +191,7 @@ class @Recordings data["meta_" + GreenLight.META_LISTED] = listed.toString(); $.ajax({ method: 'PATCH', - url: url+'/recordings/'+id, + url: url+'/'+id, data: data }).done((data) -> @@ -200,12 +202,12 @@ class @Recordings @getTable().on 'click', '.recording-delete', (event) -> btn = $(this) row = table_api.row($(this).closest('tr')).data() - url = $('.meeting-url').val() + url = recordingsObject.getRecordingsURL() id = row.id btn.prop('disabled', true) $.ajax({ method: 'DELETE', - url: url+'/recordings/'+id + url: url+'/'+id }).done((data) -> ).fail((data) -> @@ -218,6 +220,13 @@ class @Recordings getTable: -> @table + getRecordingsURL: -> + if $(".page-wrapper.rooms").data('main-room') + base_url = '/rooms/'+Meeting.getInstance().getAdminId() + else + base_url = $('.meeting-url').val() + base_url+'/recordings' + isOwner: -> @owner diff --git a/app/channels/recording_updates_channel.rb b/app/channels/recording_updates_channel.rb index 3e598a9f..f98bb617 100644 --- a/app/channels/recording_updates_channel.rb +++ b/app/channels/recording_updates_channel.rb @@ -16,6 +16,11 @@ class RecordingUpdatesChannel < ApplicationCable::Channel def subscribed - stream_from "#{params[:encrypted_id]}_recording_updates_channel" + full_id = if params[:meeting_id].present? + "#{params[:admin_id]}-#{params[:meeting_id]}" + else + params[:admin_id] + end + stream_from "#{full_id}_recording_updates_channel" end end diff --git a/app/controllers/bbb_controller.rb b/app/controllers/bbb_controller.rb index 1ef59478..2ee0f447 100644 --- a/app/controllers/bbb_controller.rb +++ b/app/controllers/bbb_controller.rb @@ -24,6 +24,7 @@ class BbbController < ApplicationController before_action :validate_checksum, only: :callback # GET /:resource/:id/join + # GET /:resource/:room_id/:id/join def join if params[:name].blank? return render_bbb_response( @@ -56,6 +57,7 @@ class BbbController < ApplicationController wait_for_moderator: true, meeting_recorded: true, meeting_name: meeting_name, + room_owner: params[:room_id], user_is_moderator: current_user == user } else @@ -102,6 +104,7 @@ class BbbController < ApplicationController end # DELETE /rooms/:id/end + # DELETE /rooms/:room_id/:id/end def end load_and_authorize_room_owner! @@ -113,30 +116,37 @@ class BbbController < ApplicationController end # GET /rooms/:id/recordings + # GET /rooms/:room_id/:id/recordings def recordings load_room! # bbb_res = bbb_get_recordings "#{@user.encrypted_id}-#{params[:id]}" - bbb_res = bbb_get_recordings "#{@user.encrypted_id}" + options = { "meta_room-id": @user.encrypted_id } + if params[:id] + options["meta_meeting-name"] = params[:id] + end + bbb_res = bbb_get_recordings(options) render_bbb_response bbb_res, bbb_res[:recordings] end # PATCH /rooms/:id/recordings/:record_id + # PATCH /rooms/:room_id/:id/recordings/:record_id def update_recordings published = params[:published] == 'true' metadata = params.select{ |k, v| k.match(/^meta_/) } bbb_res = bbb_update_recordings(params[:record_id], published, metadata) if bbb_res[:returncode] - RecordingUpdatesJob.perform_later(@user.encrypted_id, params[:record_id]) + RecordingUpdatesJob.perform_later(@user.encrypted_id, params[:record_id], params[:id]) end render_bbb_response bbb_res end # DELETE /rooms/:id/recordings/:record_id + # DELETE /rooms/:room_id/:id/recordings/:record_id def delete_recordings bbb_res = bbb_delete_recordings(params[:record_id]) if bbb_res[:returncode] - RecordingDeletesJob.perform_later(@user.encrypted_id, params[:record_id]) + RecordingDeletesJob.perform_later(@user.encrypted_id, params[:record_id], params[:id]) end render_bbb_response bbb_res end @@ -161,7 +171,7 @@ class BbbController < ApplicationController def authorize_recording_owner! load_and_authorize_room_owner! - recordings = bbb_get_recordings(params[:room_id])[:recordings] + recordings = bbb_get_recordings({recordID: params[:record_id]})[:recordings] recordings.each do |recording| if recording[:recordID] == params[:record_id] return true @@ -194,7 +204,7 @@ class BbbController < ApplicationController # the webhook event doesn't have all the data we need, so we need # to send a getRecordings anyway # TODO: if the webhooks included all data in the event we wouldn't need this - rec_info = bbb_get_recordings(token, record_id) + rec_info = bbb_get_recordings({recordID: record_id}) rec_info = rec_info[:recordings].first RecordingCreatedJob.perform_later(token, parse_recording_for_view(rec_info)) diff --git a/app/jobs/recording_deletes_job.rb b/app/jobs/recording_deletes_job.rb index 74df9abd..10a144e3 100644 --- a/app/jobs/recording_deletes_job.rb +++ b/app/jobs/recording_deletes_job.rb @@ -19,16 +19,21 @@ class RecordingDeletesJob < ApplicationJob queue_as :default - def perform(room, record_id) + def perform(room, record_id, meeting=nil) tries = 0 sleep_time = 2 while tries < 4 - bbb_res = bbb_get_recordings(nil, record_id) + bbb_res = bbb_get_recordings({recordID: record_id}) if !bbb_res[:recordings] || bbb_res[:messageKey] == 'noRecordings' + full_id = room + full_id += "-#{recording[:metadata][:"meeting-name"]}" ActionCable.server.broadcast "#{room}_recording_updates_channel", action: 'delete', id: record_id + ActionCable.server.broadcast "#{full_id}_recording_updates_channel", + action: 'delete', + id: record_id break end sleep sleep_time diff --git a/app/jobs/recording_updates_job.rb b/app/jobs/recording_updates_job.rb index 9dafbb2f..319d016c 100644 --- a/app/jobs/recording_updates_job.rb +++ b/app/jobs/recording_updates_job.rb @@ -19,13 +19,20 @@ class RecordingUpdatesJob < ApplicationJob queue_as :default - def perform(room, record_id) - bbb_res = bbb_get_recordings(nil, record_id) + def perform(room, record_id, meeting=nil) + bbb_res = bbb_get_recordings({recordID: record_id}) recording = bbb_res[:recordings].first + full_id = room + full_id += "-#{recording[:metadata][:"meeting-name"]}" ActionCable.server.broadcast "#{room}_recording_updates_channel", action: 'update', id: record_id, published: recording[:published], listed: bbb_is_recording_listed(recording) + ActionCable.server.broadcast "#{full_id}_recording_updates_channel", + action: 'update', + id: record_id, + published: recording[:published], + listed: bbb_is_recording_listed(recording) end end diff --git a/app/lib/bbb_api.rb b/app/lib/bbb_api.rb index 35f3c450..56a68606 100644 --- a/app/lib/bbb_api.rb +++ b/app/lib/bbb_api.rb @@ -47,6 +47,7 @@ module BbbApi options[:wait_for_moderator] ||= false options[:meeting_logout_url] ||= nil options[:meeting_name] ||= meeting_token + options[:room_owner] ||= nil if !bbb return call_invalid_res @@ -81,6 +82,12 @@ module BbbApi { "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] + if Rails.configuration.use_webhooks webhook_register(options[:hook_url], meeting_id) end @@ -114,13 +121,9 @@ module BbbApi response_data = bbb_exception_res exc end - def bbb_get_recordings(meeting_id, record_id=nil) - options={} - if record_id - options[:recordID] = record_id - end - if meeting_id - options[:meetingID] = bbb_meeting_id(meeting_id) + def bbb_get_recordings(options={}) + if options[:meetingID] + options[:meetingID] = bbb_meeting_id(options[:meetingID]) end res = bbb_safe_execute :get_recordings, options diff --git a/app/views/landing/rooms.html.erb b/app/views/landing/rooms.html.erb index 6184ad6f..ff613273 100644 --- a/app/views/landing/rooms.html.erb +++ b/app/views/landing/rooms.html.erb @@ -36,6 +36,7 @@ with BigBlueButton; if not, see . + diff --git a/config/locales/en-us.yml b/config/locales/en-us.yml index f63662a4..df7a651f 100644 --- a/config/locales/en-us.yml +++ b/config/locales/en-us.yml @@ -88,6 +88,7 @@ en-US: body: "You have been invited by %{user} to a meeting.\n\nPlease open the following page in your web browser: &&URL&&" subject: "%{user} invited you to a meeting" my_room: my room + name: Name no: No notification_mailer: recording_ready_email: diff --git a/config/routes.rb b/config/routes.rb index 8725dff3..5817bb61 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -28,18 +28,28 @@ Rails.application.routes.draw do # There are two resources [meetings|rooms] # meetings offer a landing page for NON authenticated users to create and join session in BigBlueButton # rooms offer a customized landing page for authenticated users to create and join session in BigBlueButton + + # recording routes for updating, deleting and viewing recordings + get '/rooms/:room_id/recordings', to: 'bbb#recordings', defaults: {format: 'json'} + patch '/rooms/:room_id/recordings/:record_id', to: 'bbb#update_recordings', defaults: {format: 'json'} + delete '/rooms/:room_id/recordings/:record_id', to: 'bbb#delete_recordings', defaults: {format: 'json'} + get '/rooms/:room_id/:id/recordings', to: 'bbb#recordings', defaults: {format: 'json'} + patch '/rooms/:room_id/:id/recordings/:record_id', to: 'bbb#update_recordings', defaults: {format: 'json'} + delete '/rooms/:room_id/:id/recordings/:record_id', to: 'bbb#delete_recordings', defaults: {format: 'json'} + + # room routes for joining, ending, waiting and refreshing authenticated meetings + get '/rooms/:room_id', to: 'landing#resource', resource: 'rooms' + get '/rooms/:room_id/:id', to: 'landing#resource', resource: 'rooms' + get '/rooms/:room_id/:id/join', to: 'bbb#join', resource: 'rooms', defaults: {format: 'json'} + delete '/rooms/:room_id/:id/end', to: 'bbb#end', defaults: {format: 'json'} + + # routes shared between meetings and rooms get '/:resource/:id', to: 'landing#resource', as: :resource get '/:resource/:id/join', to: 'bbb#join', as: :bbb_join, defaults: {format: 'json'} - - get '/rooms/:room_id/recordings', to: 'bbb#recordings', defaults: {format: 'json'} post '/:resource/:id/callback', to: 'bbb#callback' #, defaults: {format: 'json'} - patch '/rooms/:id/recordings/:record_id', to: 'bbb#update_recordings', defaults: {format: 'json'} - delete '/rooms/:id/recordings/:record_id', to: 'bbb#delete_recordings', defaults: {format: 'json'} + get '/:resource/:room_id/:id/wait', to: 'landing#wait_for_moderator' get '/:resource/:room_id/:id/session_status_refresh', to: 'landing#session_status_refresh' - delete '/rooms/:room_id/:id/end', to: 'bbb#end', defaults: {format: 'json'} - get '/rooms/:room_id/:id', to: 'landing#resource', resource: 'rooms' - get '/:resource/:room_id/:id/join', to: 'bbb#join', defaults: {format: 'json'} root to: 'landing#index', :resource => 'meetings'
<%= t('date_recorded') %><%= t('name') %> <%= t('thumbnails') %> <%= t('duration') %> <%= t('views') %>