diff --git a/app/assets/javascripts/channels/meeting_updates.js b/app/assets/javascripts/channels/meeting_updates.js index 87f6af15..0cf1ba32 100644 --- a/app/assets/javascripts/channels/meeting_updates.js +++ b/app/assets/javascripts/channels/meeting_updates.js @@ -23,10 +23,11 @@ }); }; - var initRooms = function() { - App.messages = App.cable.subscriptions.create({ + var enableMeetingUpdates = function() { + App.meeting_update = App.cable.subscriptions.create({ channel: 'MeetingUpdatesChannel', - encrypted_id: $(".page-wrapper").data('id') + admin_id: $(".page-wrapper.rooms").data('admin-id'), + meeting_id: $(".page-wrapper.rooms").data('id') }, { received: function(data) { @@ -55,10 +56,21 @@ }); }; + var disableMeetingUpdates = function() { + App.meeting_update.unsubscribe(); + delete App.meeting_update + }; + $(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)) { - initRooms(); + 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/landing.js b/app/assets/javascripts/landing.js index 7311db5f..81becb6f 100644 --- a/app/assets/javascripts/landing.js +++ b/app/assets/javascripts/landing.js @@ -41,8 +41,8 @@ // setup event handlers $('.center-panel-wrapper').on ('click', '.meeting-join', function (event) { var name = $('.meeting-user-name').val(); - Meeting.getInstance().setName(name); - Meeting.getInstance().setId($(".page-wrapper").data('id')); + Meeting.getInstance().setUserName(name); + Meeting.getInstance().setMeetingId($(".page-wrapper").data('id')); // a user name is set, join the user into the session if (name !== undefined && name !== null) { @@ -64,6 +64,10 @@ } }); + $('.center-panel-wrapper').on ('click', '.meeting-start', function (event) { + Turbolinks.visit(Meeting.getInstance().getURL()); + }); + $('.center-panel-wrapper').on ('keypress', '.meeting-user-name', function (event) { if (event.keyCode === 13) { event.preventDefault(); @@ -148,7 +152,9 @@ $(document).tooltip(options); // focus name input or join button - if ($('.meeting-user-name').is(':visible')) { + if ($('.meeting-name').is(':visible')) { + $('.meeting-name').focus(); + } else if ($('.meeting-user-name').is(':visible')) { $('.meeting-user-name').focus(); } else { $('.meeting-join').focus(); @@ -156,21 +162,25 @@ }; var initIndex = function() { - $('.generate-link').click (function (e) { - e.preventDefault(); - var newId = Math.trunc(Math.random() * 1000000000); - Meeting.getInstance().setId(newId); + + $('.center-panel-wrapper').on('keyup', '.meeting-name', function (event, msg) { + var newId = $(this).val(); + Meeting.getInstance().setMeetingId(newId); $(".page-wrapper.meetings").data('id', newId); $('.meeting-url').val(Meeting.getInstance().getURL()); + $('.join-meeting-title').html(I18n.join_title.replace(/%{id}/, newId)); + if (newId === '') { + $('.invite-join-wrapper').addClass('hidden'); + } else { + $('.invite-join-wrapper').removeClass('hidden'); + } }); - $('.generate-link').click(); - $('ul.previously-joined').empty(); var joinedMeetings = localStorage.getItem('joinedMeetings'); if (joinedMeetings && joinedMeetings.length > 0) { joinedMeetings = joinedMeetings.split(','); - $('.center-panel-wrapper .panel-footer').removeClass('hidden'); + $('.center-panel-wrapper .previously-joined-wrapper').removeClass('hidden'); for (var i = joinedMeetings.length - 1; i >= 0; i--) { $('ul.previously-joined').append('
  • '+joinedMeetings[i]+'
  • '); @@ -185,6 +195,39 @@ var initRooms = function() { displayRoomURL(); + $('.center-panel-wrapper').on('input', '.meeting-name', function (event, msg) { + var newId = $(this).val(); + Meeting.getInstance().setMeetingId(newId); + $('.meeting-url').val(Meeting.getInstance().getURL()); + $('.join-meeting-title').html(I18n.join_title.replace(/%{id}/, newId)); + if (newId === '') { + $('.invite-join-wrapper').addClass('hidden'); + } else { + $('.invite-join-wrapper').removeClass('hidden'); + } + }); + + if ($(".page-wrapper.rooms").data('main-room')) { + $('.center-panel-wrapper').on('click', '.fill-meeting-name', function (event, msg) { + var name = $(this).text(); + $('input.meeting-name').val(name).trigger('input'); + }); + + $('ul.previously-joined').empty(); + var joinedMeetings = localStorage.getItem('joinedRooms'); + if (joinedMeetings && joinedMeetings.length > 0) { + joinedMeetings = joinedMeetings.split(','); + $('.center-panel-wrapper .previously-joined-wrapper').removeClass('hidden'); + + for (var i = joinedMeetings.length - 1; i >= 0; i--) { + $('ul.previously-joined').append('
  • '+joinedMeetings[i]+'
  • '); + } + } + if ($('input.meeting-name').val() !== '') { + $('input.meeting-name').trigger('input'); + } + } + Recordings.getInstance().refresh(); Recordings.getInstance().setupActionHandlers(); }; diff --git a/app/assets/javascripts/meeting.coffee b/app/assets/javascripts/meeting.coffee index 7364a5ea..4e678031 100644 --- a/app/assets/javascripts/meeting.coffee +++ b/app/assets/javascripts/meeting.coffee @@ -19,24 +19,30 @@ _meetingInstance = null class @Meeting - constructor: (@id, @type, @name) -> + constructor: (@meetingId, @type, @userName, @adminId) -> # Gets the current instance or creates a new one @getInstance: -> if _meetingInstance return _meetingInstance - id = $(".page-wrapper").data('id') + meetingId = $(".page-wrapper").data('id') if (type = location.pathname.split('/')[1]) != 'rooms' type = 'meetings' name = $('.meeting-user-name').val() - _meetingInstance = new Meeting(id, type, name) + adminId = $(".page-wrapper").data('admin-id') + _meetingInstance = new Meeting(meetingId, type, name, adminId) return _meetingInstance @clear: -> _meetingInstance = null - @buildMeetingURL: (id, type) -> - return @buildFullDomainURL() + '/' + type + '/' + id + @buildMeetingURL: (meetingId, type, adminId) -> + fullId = '' + if adminId + fullId = encodeURIComponent(adminId) + '/' + encodeURIComponent(meetingId) + else + fullId = encodeURIComponent(meetingId) + return @buildFullDomainURL() + '/' + type + '/' + fullId @buildFullDomainURL: -> url = location.protocol + '//' + location.hostname @@ -56,28 +62,41 @@ class @Meeting # Returns a response object # The response object contains the URL to join the meeting getJoinMeetingResponse: -> - return $.get @getURL() + "/join?name=" + @name, (data) => - if data.messageKey == 'ok' && @type == 'meetings' - # update name used to join meeting - localStorage.setItem('lastJoinedName', @getName()) + return $.get @getURL() + "/join?name=" + @userName, (data) => + # update name used to join meeting + localStorage.setItem('lastJoinedName', @getUserName()) - # update previously joined meetings on client + if data.messageKey == 'ok' + key = '' + if @type == 'meetings' + key = 'joinedMeetings' + else if @type == 'rooms' + key = 'joinedRooms' + + # update previously joined meetings/rooms on client try - joinedMeetings = localStorage.getItem('joinedMeetings') || '' + joinedMeetings = localStorage.getItem(key) || '' joinedMeetings = joinedMeetings.split(',') - joinedMeetings = joinedMeetings.filter (item) => item != @id.toString() + joinedMeetings = joinedMeetings.filter (item) => item != @meetingId.toString() if joinedMeetings.length >= 5 joinedMeetings.splice(0, 1) - joinedMeetings.push(@id) - localStorage.setItem('joinedMeetings', joinedMeetings.join(',')) + joinedMeetings.push(@meetingId) + localStorage.setItem(key, joinedMeetings.join(',')) catch err - localStorage.setItem('joinedMeetings', @id) + localStorage.setItem(key, @meetingId) - getId: -> - return @id + getMeetingId: -> + return @meetingId - setId: (id) -> - @id = id + setMeetingId: (id) -> + @meetingId = id + return this + + getAdminId: -> + return @adminId + + setAdminId: (id) -> + @adminId = id return this getType: -> @@ -88,13 +107,13 @@ class @Meeting return this getURL: -> - return Meeting.buildMeetingURL(@id, @type) + return Meeting.buildMeetingURL(@meetingId, @type, @adminId) - getName: -> - return @name + getUserName: -> + return @userName - setName: (name) -> - @name = name + setUserName: (name) -> + @userName = name return this getModJoined: -> diff --git a/app/assets/javascripts/recordings.coffee b/app/assets/javascripts/recordings.coffee index a4c69396..701394a1 100644 --- a/app/assets/javascripts/recordings.coffee +++ b/app/assets/javascripts/recordings.coffee @@ -34,8 +34,10 @@ class @Recordings }, columns: [ { data: "start_time" }, + { data: "name", visible: $(".page-wrapper.rooms").data('main-room') }, { data: "previews", orderable: false }, { data: "duration", orderable: false }, + { data: "published" }, { data: "playbacks", orderable: false }, { data: "listed", visible: false }, { data: "id", orderable: false } @@ -55,7 +57,7 @@ class @Recordings return data }, { - targets: 1, + targets: 2, render: (data, type, row) -> if type == 'display' str = '' @@ -66,7 +68,22 @@ class @Recordings return data }, { - targets: 3, + targets: 4, + render: (data, type, row) -> + visibility = ['unpublished', 'unlisted', 'published'] + if row.published + if row.listed + state = visibility[2] + else + state = visibility[1] + else + state = visibility[0] + if type == 'display' + return I18n[state] + return state + } + { + targets: 5, render: (data, type, row) -> if type == 'display' str = '' @@ -80,7 +97,7 @@ class @Recordings targets: -1, render: (data, type, row) -> if type == 'display' - roomName = Meeting.getInstance().getId() + roomName = Meeting.getInstance().getMeetingId() recordingActions = $('.hidden-elements').find('.recording-actions') classes = ['recording-unpublished', 'recording-unlisted', 'recording-published'] if row.published @@ -151,13 +168,13 @@ class @Recordings draw: -> if !@isOwner() - @table.api().columns(4).search('true') + @table.api().columns(6).search('true') @table.api().columns.adjust().draw() # refresh the recordings from the server refresh: -> table_api = this.table.api() - $.get "/rooms/"+Meeting.getInstance().getId()+"/recordings", (data) => + $.get @getRecordingsURL(), (data) => @setOwner(data.is_owner) if !@owner table_api.column(-1).visible(false) @@ -172,11 +189,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 +207,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 +218,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 +236,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/assets/stylesheets/landing.scss b/app/assets/stylesheets/landing.scss index 06900393..79bf61f7 100644 --- a/app/assets/stylesheets/landing.scss +++ b/app/assets/stylesheets/landing.scss @@ -14,13 +14,16 @@ // You should have received a copy of the GNU Lesser General Public License along // with BigBlueButton; if not, see . +.previously-joined { + list-style-type: none; + margin:auto; + width: 200px; + padding: 0; + cursor: pointer; +} + .meetings { - .previously-joined { - list-style-type: none; - margin:auto; - width: 200px; - padding: 0; - } + } .rooms { @@ -46,6 +49,11 @@ } } + .meeting-url-button-group { + padding-top: 5px; + width: 100px; + } + } .img-thumbnail{ @@ -67,19 +75,6 @@ .meeting-url-group { position: relative; - - .generate-link { - position: absolute; - right: 12px; - top: 10px; - z-index: 9; - color: #999; - cursor: pointer; - - &:hover { - color: #000; - } - } } .recording-update-trigger { diff --git a/app/assets/stylesheets/shared.scss b/app/assets/stylesheets/shared.scss index 9b4f9e6e..d0a2e32f 100644 --- a/app/assets/stylesheets/shared.scss +++ b/app/assets/stylesheets/shared.scss @@ -26,6 +26,10 @@ html, body { background: #ffffff; } +.container-fluid { + height: 100%; +} + .background { height: 350px; width: 100%; @@ -68,12 +72,17 @@ body[data-controller=landing][data-action=rooms].app-background { .center-block { float: none; } +.center-panel-wrapper { + height: 100%; +} .center-panel { + height: 100%; .center-panel-size { - max-width: 900px + max-width: 1200px } .center-panel-content-size { - max-width: 800px; + height: 100%; + max-width: 1100px; } .input-spacing { @@ -157,3 +166,16 @@ a.signin-link { color: #444; } } + +.verticle-line { + // parent must be position relative to work + width: 1px; + background-color: lightgray; + height: 100%; + position: absolute; + left: 50%; +} + +.invite-join-wrapper { + position: relative; +} diff --git a/app/channels/meeting_updates_channel.rb b/app/channels/meeting_updates_channel.rb index 27be305d..127c18da 100644 --- a/app/channels/meeting_updates_channel.rb +++ b/app/channels/meeting_updates_channel.rb @@ -16,6 +16,12 @@ class MeetingUpdatesChannel < ApplicationCable::Channel def subscribed - stream_from "#{params[:encrypted_id]}_meeting_updates_channel" + + full_id = if params[:meeting_id].present? + "#{params[:admin_id]}-#{params[:meeting_id]}" + else + params[:admin_id] + end + stream_from "#{full_id}_meeting_updates_channel" end end 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 4c55f241..bff60339 100644 --- a/app/controllers/bbb_controller.rb +++ b/app/controllers/bbb_controller.rb @@ -24,17 +24,40 @@ 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? - render_bbb_response("missing_parameter", "user name was not included", :unprocessable_entity) + return render_bbb_response( + messageKey: "missing_parameter", + message: "user name was not included", + status: :unprocessable_entity + ) else - user = User.find_by encrypted_id: params[:id] + if params[:room_id] + user = User.find_by encrypted_id: params[:room_id] + if !user + return render_bbb_response( + messageKey: "not_found", + message: "User Not Found", + status: :not_found + ) + end + + meeting_id = "#{params[:room_id]}-#{params[:id]}" + meeting_name = params[:id] + meeting_path = "#{params[:room_id]}/#{params[:id]}" + else + user = User.find_by encrypted_id: params[:id] + meeting_id = params[:id] + meeting_path = meeting_id + end options = if user { wait_for_moderator: true, meeting_recorded: true, - meeting_name: user.name, + meeting_name: meeting_name, + room_owner: params[:room_id], user_is_moderator: current_user == user } else @@ -42,23 +65,26 @@ class BbbController < ApplicationController user_is_moderator: true } end - base_url = "#{request.base_url}/#{params[:resource]}/#{params[:id]}" + + base_url = "#{request.base_url}/#{params[:resource]}/#{meeting_path}" options[:meeting_logout_url] = base_url options[:hook_url] = "#{base_url}/callback" bbb_res = bbb_join_url( - params[:id], + meeting_id, params[:name], options ) # the user can join the meeting - if bbb_res[:returncode] && current_user && current_user == user - JoinMeetingJob.perform_later(params[:id]) + if bbb_res[:returncode] && user + if current_user == user + JoinMeetingJob.perform_later(user.encrypted_id, params[:id]) # user will be waiting for a moderator - else - NotifyUserWaitingJob.perform_later(params[:id], params[:name]) + else + NotifyUserWaitingJob.perform_later(user.encrypted_id, params[:id], params[:name]) + end end render_bbb_response bbb_res, bbb_res[:response] @@ -80,40 +106,49 @@ class BbbController < ApplicationController end # DELETE /rooms/:id/end + # DELETE /rooms/:room_id/:id/end def end load_and_authorize_room_owner! - bbb_res = bbb_end_meeting @user.encrypted_id + bbb_res = bbb_end_meeting "#{@user.encrypted_id}-#{params[:id]}" if bbb_res[:returncode] - EndMeetingJob.perform_later(@user.encrypted_id) + EndMeetingJob.perform_later(@user.encrypted_id, params[:id]) end render_bbb_response bbb_res end # GET /rooms/:id/recordings + # GET /rooms/:room_id/:id/recordings def recordings load_room! - bbb_res = bbb_get_recordings @user.encrypted_id + # bbb_res = bbb_get_recordings "#{@user.encrypted_id}-#{params[: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 @@ -121,7 +156,7 @@ class BbbController < ApplicationController private def load_room! - @user = User.find_by encrypted_id: params[:id] + @user = User.find_by encrypted_id: params[:room_id] if !@user render head(:not_found) && return end @@ -138,7 +173,7 @@ class BbbController < ApplicationController def authorize_recording_owner! load_and_authorize_room_owner! - recordings = bbb_get_recordings(params[:id])[:recordings] + recordings = bbb_get_recordings({recordID: params[:record_id]})[:recordings] recordings.each do |recording| if recording[:recordID] == params[:record_id] return true @@ -171,7 +206,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/controllers/landing_controller.rb b/app/controllers/landing_controller.rb index 249c8172..d6e63224 100644 --- a/app/controllers/landing_controller.rb +++ b/app/controllers/landing_controller.rb @@ -35,12 +35,13 @@ class LandingController < ApplicationController end def session_status_refresh - @user = User.find_by(encrypted_id: params[:id]) + @user = User.find_by(encrypted_id: params[:room_id]) if @user.nil? render head(:not_found) && return end - @meeting_running = bbb_get_meeting_info(@user.encrypted_id)[:returncode] + @meeting_id = params[:id] + @meeting_running = bbb_get_meeting_info("#{@user.encrypted_id}-#{params[:id]}")[:returncode] render layout: false end @@ -65,13 +66,15 @@ class LandingController < ApplicationController def render_room params[:action] = 'rooms' - @user = User.find_by(encrypted_id: params[:id]) + @user = User.find_by(encrypted_id: params[:room_id] || params[:id]) if @user.nil? redirect_to root_path return end - @meeting_running = bbb_get_meeting_info(@user.encrypted_id)[:returncode] + @meeting_id = params[:id] + @meeting_running = bbb_get_meeting_info("#{@user.encrypted_id}-#{@meeting_id}")[:returncode] + @main_room = @meeting_id.blank? || @meeting_id == @user.encrypted_id render :action => 'rooms' end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 928bf1c9..bacfc69a 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -47,10 +47,4 @@ module ApplicationHelper def on_room_or_meeting_page? params[:id].present? end - - def version - Greenlight::VERSION - rescue - 'development' - end end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index 19758ad6..7919773b 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -17,6 +17,6 @@ module UsersHelper def is_room_owner token = current_user ? current_user.encrypted_id : nil - token.present? && params[:id].present? && token == params[:id] + token.present? && params[:room_id].present? && token == params[:room_id] end end diff --git a/app/jobs/end_meeting_job.rb b/app/jobs/end_meeting_job.rb index 235c0f78..66711057 100644 --- a/app/jobs/end_meeting_job.rb +++ b/app/jobs/end_meeting_job.rb @@ -19,15 +19,15 @@ class EndMeetingJob < ApplicationJob queue_as :default - def perform(room) + def perform(room, meeting) tries = 0 sleep_time = 2 while tries < 4 - bbb_res = bbb_get_meeting_info(room) + bbb_res = bbb_get_meeting_info("#{room}-#{meeting}") if !bbb_res[:returncode] - ActionCable.server.broadcast "#{room}_meeting_updates_channel", + ActionCable.server.broadcast "#{room}-#{meeting}_meeting_updates_channel", action: 'meeting_ended' break end diff --git a/app/jobs/join_meeting_job.rb b/app/jobs/join_meeting_job.rb index feb4f303..02dc2db4 100644 --- a/app/jobs/join_meeting_job.rb +++ b/app/jobs/join_meeting_job.rb @@ -17,8 +17,8 @@ class JoinMeetingJob < ApplicationJob queue_as :default - def perform(room) - ActionCable.server.broadcast "#{room}_meeting_updates_channel", + def perform(room, meeting) + ActionCable.server.broadcast "#{room}-#{meeting}_meeting_updates_channel", action: 'moderator_joined', moderator: 'joined' end diff --git a/app/jobs/notify_user_waiting_job.rb b/app/jobs/notify_user_waiting_job.rb index 0211e42a..2437d840 100644 --- a/app/jobs/notify_user_waiting_job.rb +++ b/app/jobs/notify_user_waiting_job.rb @@ -17,8 +17,8 @@ class NotifyUserWaitingJob < ApplicationJob queue_as :default - def perform(room, user) - ActionCable.server.broadcast "#{room}_meeting_updates_channel", + def perform(room, meeting, user) + ActionCable.server.broadcast "#{room}-#{meeting}_meeting_updates_channel", { action: 'user_waiting', user: user } end end 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/_invite_join.html.erb b/app/views/landing/_invite_join.html.erb new file mode 100644 index 00000000..7f11b5b0 --- /dev/null +++ b/app/views/landing/_invite_join.html.erb @@ -0,0 +1,15 @@ + diff --git a/app/views/landing/_previously_joined.html.erb b/app/views/landing/_previously_joined.html.erb new file mode 100644 index 00000000..3270589b --- /dev/null +++ b/app/views/landing/_previously_joined.html.erb @@ -0,0 +1,6 @@ + diff --git a/app/views/landing/_rooms_center_panel.html.erb b/app/views/landing/_rooms_center_panel.html.erb index 47fa3252..404b59b9 100644 --- a/app/views/landing/_rooms_center_panel.html.erb +++ b/app/views/landing/_rooms_center_panel.html.erb @@ -18,10 +18,8 @@ with BigBlueButton; if not, see .

    <% if admin? && !@meeting_running %> <%= t('your_personal_room') %> - <% elsif !admin? %> - <%= t('join_session_user', name: @user.username) %> <% else %> - <%= t('join_session') %> + <%= t('user_person_room', name: @user.name) %> <% end %>

    @@ -30,10 +28,18 @@ with BigBlueButton; if not, see . <%= render layout: 'shared/center_panel' do %>
    <% if admin? %> - <%= render 'shared/meeting_url', hidden: false %> + <%= render 'shared/meeting_name_form' %> +
    + <%= render 'landing/previously_joined' %> +
    +
    + <%= render 'landing/invite_join' %> +
    <% else %> - <%= render 'shared/meeting_url', hidden: true %> +
    +
    <%= t('are_you', name: @user.name) %>
    +
    <%= link_to t('login'), '/users/login' %>
    +
    <% end %> - <%= render 'shared/join_form' %>
    <% end %> diff --git a/app/views/landing/_rooms_meetings_center_panel.html.erb b/app/views/landing/_rooms_meetings_center_panel.html.erb new file mode 100644 index 00000000..c80f0141 --- /dev/null +++ b/app/views/landing/_rooms_meetings_center_panel.html.erb @@ -0,0 +1,29 @@ + + +<% content_for :title do %> +
    +

    + <%= t('join_session_id', id: @meeting_id) %> +

    +
    +<% end %> + +<%= render layout: 'shared/center_panel' do %> +
    + <%= render 'shared/meeting_url', hidden: true %> + <%= render 'shared/join_form' %> +
    +<% end %> diff --git a/app/views/landing/index.html.erb b/app/views/landing/index.html.erb index f83f4f9c..126a4493 100644 --- a/app/views/landing/index.html.erb +++ b/app/views/landing/index.html.erb @@ -15,16 +15,7 @@ with BigBlueButton; if not, see . <% content_for :title do %>
    -

    <%= t('create_session') %>

    -
    -<% end %> - -<% content_for :footer do %> - <% end %> @@ -34,8 +25,16 @@ with BigBlueButton; if not, see .
    <%= render layout: 'shared/center_panel' do %>
    - <%= render 'shared/meeting_url', hidden: false %> - <%= render 'shared/join_form' %> + <%= render 'shared/meeting_name_form' %> + +
    + <%= render 'landing/previously_joined' %> +
    + +
    + <%= render 'landing/invite_join' %> +
    +
    <% end %>
    diff --git a/app/views/landing/meetings.html.erb b/app/views/landing/meetings.html.erb index 60a42c3c..244acfe9 100644 --- a/app/views/landing/meetings.html.erb +++ b/app/views/landing/meetings.html.erb @@ -22,11 +22,7 @@ with BigBlueButton; if not, see . <% content_for :footer do %> <% end %> diff --git a/app/views/landing/rooms.html.erb b/app/views/landing/rooms.html.erb index 1fc84c05..f24e6217 100644 --- a/app/views/landing/rooms.html.erb +++ b/app/views/landing/rooms.html.erb @@ -19,22 +19,30 @@ with BigBlueButton; if not, see . <% page_title= t('greet_guest', name: @user.username) %> <% end %> -
    +
    - <%= render 'rooms_center_panel' %> + <% if @main_room %> + <%= render 'rooms_center_panel' %> + <% else %> + <%= render 'rooms_meetings_center_panel' %> + <% end %>
    - <%= render 'shared/signup' %> + <% unless @main_room && !current_user %> + <%= render 'shared/signup' %> + <% end %>
    + - + + @@ -53,14 +61,14 @@ with BigBlueButton; if not, see .
    - -
    diff --git a/app/views/landing/session_status_refresh.html.erb b/app/views/landing/session_status_refresh.html.erb index fdb2c9f5..983fbb77 100644 --- a/app/views/landing/session_status_refresh.html.erb +++ b/app/views/landing/session_status_refresh.html.erb @@ -13,4 +13,4 @@ You should have received a copy of the GNU Lesser General Public License along with BigBlueButton; if not, see . --> -<%= render 'rooms_center_panel' %> +<%= render 'rooms_meetings_center_panel' %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 1db824d3..1cd6131b 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -43,7 +43,7 @@ with BigBlueButton; if not, see . diff --git a/app/views/shared/_meeting_name_form.html.erb b/app/views/shared/_meeting_name_form.html.erb new file mode 100644 index 00000000..f9189f12 --- /dev/null +++ b/app/views/shared/_meeting_name_form.html.erb @@ -0,0 +1,3 @@ +
    + +
    diff --git a/app/views/shared/_meeting_url.html.erb b/app/views/shared/_meeting_url.html.erb index 38197633..e67aace3 100644 --- a/app/views/shared/_meeting_url.html.erb +++ b/app/views/shared/_meeting_url.html.erb @@ -14,34 +14,50 @@ with BigBlueButton; if not, see . --> diff --git a/app/views/shared/_signup.html.erb b/app/views/shared/_signup.html.erb index b4085d12..087bfbd9 100644 --- a/app/views/shared/_signup.html.erb +++ b/app/views/shared/_signup.html.erb @@ -1,13 +1,17 @@ diff --git a/app/views/shared/_title.html.erb b/app/views/shared/_title.html.erb new file mode 100644 index 00000000..f18a9627 --- /dev/null +++ b/app/views/shared/_title.html.erb @@ -0,0 +1,7 @@ +<% title_class ||= '' %> +
    +
    +

    <%= title %>

    + <%= yield %> +
    +
    diff --git a/circle.yml b/circle.yml index caf30c5c..5667afd2 100644 --- a/circle.yml +++ b/circle.yml @@ -10,5 +10,4 @@ deployment: hub: branch: master commands: - - ./scripts/set_version.sh - ./scripts/build_image_master.sh diff --git a/config/locales/en-us.yml b/config/locales/en-us.yml index 92989d73..f0da90d6 100644 --- a/config/locales/en-us.yml +++ b/config/locales/en-us.yml @@ -37,44 +37,51 @@ en-US: actions: Actions - are_you: Are you %{name}? + are_you: Are you %{name} ? are_you_sure: Are you sure? change_recording_visibility: "Change visibility to:" client: are_you_sure: Are you sure? delete_recording: Delete recording + join_title: Join "%{id}" meeting_ended: Meeting was ended meeting_started: Meeting was started no_recordings: No Recordings no_recordings_yet: No Recordings (Yet!) publish_recording: Publish recording + published: Everyone recording_created: A recording was created recording_deleted: Recording was deleted recording_published: Recording was published recording_unlisted: Recording was unlisted recording_unpublished: Recording was unpublished unpublish_recording: Hide recording + unlisted: Unlisted + unpublished: No one user_waiting_body: "%{user} is waiting to join your room!" user_waiting_title: A user is waiting copied: Copied copy_error: Use Ctrl-c to copy - create_session: Invite others to a meeting create_your_session: Create your own meeting date_recorded: Date duration: Duration (minutes) end: End enter_name: Enter your name + enter_meeting_name: Enter a meeting name to start + footer_html: Powered by %{bbb_link} greet_user: Welcome, %{name} greet_guest: Welcome to %{name} Meeting Space hi_all: Hi Everyone + home_title: Welcome to BigBlueButton + invite: Invite + invite_description: (share this link below to invite others to this meeting) join: Join - join_session: Join the current meeting join_session_id: Join %{id} - join_session_user: Join %{name} meeting logged_in_description_html: You are logged in as %{link} login: login login_description: Want your own recorded meetings? logout: logout + meeting: Meeting meeting_invite: explanation: Send an email with an invitation to this meeting not_signed_in: @@ -92,10 +99,8 @@ en-US: phrase2: "Access the following website to view it and publish to other users: %{url}" subject: "Your recording is ready!" past_recordings: Past Recordings - footer_html: "%{greenlight_link} build %{version}, Powered by %{bbb_link}" presentation: Presentation previously_joined_meetings: Previously Joined Meetings - published: Published return_to_room: Return to your personal room session_url_explanation: The meeting will be taking place using the following URL signin_text: Log in with %{provider} @@ -103,12 +108,11 @@ en-US: start_meeting: Start Meeting your_personal_room: Your Personal Room thumbnails: Thumbnails - unlisted: Unlisted - unpublished: Unpublished url_copy_explanation: Copy this URL to invite others to the meeting - url_refresh_hint: Generate a new meeting URL + user_person_room: "%{name} personal room" video: Video - views: Views + visibility: Visibility wait_for_mod_msg: Looks like you're the first one here... wait_for_mod_explanation: You will automatically join when the meeting starts + watch: Watch yes: Yes diff --git a/config/routes.rb b/config/routes.rb index 83d238c0..5817bb61 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -28,15 +28,29 @@ 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 '/:resource/:id/wait', to: 'landing#wait_for_moderator' - get '/:resource/:id/session_status_refresh', to: 'landing#session_status_refresh' post '/:resource/:id/callback', to: 'bbb#callback' #, defaults: {format: 'json'} - delete '/rooms/:id/end', to: 'bbb#end', defaults: {format: 'json'} - get '/rooms/:id/recordings', to: 'bbb#recordings', 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'} - root to: 'landing#index', :resource => "meetings" + get '/:resource/:room_id/:id/wait', to: 'landing#wait_for_moderator' + get '/:resource/:room_id/:id/session_status_refresh', to: 'landing#session_status_refresh' + + + root to: 'landing#index', :resource => 'meetings' end diff --git a/scripts/set_version.sh b/scripts/set_version.sh deleted file mode 100755 index 523e0e7f..00000000 --- a/scripts/set_version.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -echo "module Greenlight VERSION = ${CIRCLE_BUILD_NUM} end" >| $HOME/greenlight/app/lib/version.rb diff --git a/test/controllers/landing_controller_test.rb b/test/controllers/landing_controller_test.rb index bdb4ac76..4a698af5 100644 --- a/test/controllers/landing_controller_test.rb +++ b/test/controllers/landing_controller_test.rb @@ -39,12 +39,12 @@ class LandingControllerTest < ActionController::TestCase end test "should get wait for moderator" do - get :wait_for_moderator, params: { id: @user.encrypted_id, resource: 'rooms' } + get :wait_for_moderator, params: { room_id: @user.encrypted_id, id: 'room1', resource: 'rooms' } assert_response :success end test "should get session status refresh" do - get :wait_for_moderator, params: { id: @user.encrypted_id, resource: 'rooms' } + get :wait_for_moderator, params: { room_id: @user.encrypted_id, id: 'room1', resource: 'rooms' } assert_response :success end
    <%= t('date_recorded') %><%= t('meeting') %> <%= t('thumbnails') %> <%= t('duration') %><%= t('views') %><%= t('visibility') %><%= t('watch') %> published <%= t('actions') %>