diff --git a/app/controllers/concerns/bbb_helper.rb b/app/controllers/concerns/bbb_helper.rb index c808e5ba..0b1b3259 100644 --- a/app/controllers/concerns/bbb_helper.rb +++ b/app/controllers/concerns/bbb_helper.rb @@ -28,17 +28,17 @@ module BbbHelper # Sets a BigBlueButtonApi object for interacting with the API. def bbb @bbb_credentials ||= initialize_bbb_credentials - bbb_url = remove_slash(@bbb_credentials.endpoint(@room.tenant)) - bbb_secret = @bbb_credentials.secret(@room.tenant) + bbb_url = remove_slash(@bbb_credentials.endpoint(room.tenant)) + bbb_secret = @bbb_credentials.secret(room.tenant) BigBlueButton::BigBlueButtonApi.new(bbb_url, bbb_secret, '1.0', Rails.logger) rescue StandardError => e logger.error("Error in creating BBB object: #{e}") raise RoomsError::CustomError.new(code: 500, message: 'There was an error initializing BigBlueButton credentials', key: 'BigBlueButton Error') end - # Generates URL for joining the current @room meeting. + # Generates URL for joining the current room meeting. def join_meeting_url - return unless @room && @user + return unless room && @user unless bbb @error = { @@ -51,40 +51,40 @@ def join_meeting_url end create_meeting - role = @user.moderator?(bigbluebutton_moderator_roles) || string_to_bool(@room.allModerators) ? 'moderator' : 'viewer' + role = @user.moderator?(bigbluebutton_moderator_roles) || string_to_bool(room.allModerators) ? 'moderator' : 'viewer' join_options = {} join_options[:createTime] = meeting_info[:createTime] join_options[:userID] = @user.uid - bbb.join_meeting_url(@room.handler, @user.username(t("default.bigbluebutton.#{role}")), @room.attributes[role], join_options) + bbb.join_meeting_url(room.handler, @user.username(t("default.bigbluebutton.#{role}")), room.attributes[role], join_options) end - # Create meeting for the current @room. + # Create meeting for the current room. def create_meeting - record = bigbluebutton_recording_enabled ? string_to_bool(@room.record) : false + record = bigbluebutton_recording_enabled ? string_to_bool(room.record) : false create_options = { - moderatorPW: @room.moderator, - attendeePW: @room.viewer, - welcome: @room.welcome, + moderatorPW: room.moderator, + attendeePW: room.viewer, + welcome: room.welcome, record: record, logoutURL: autoclose_url, - lockSettingsDisableCam: string_to_bool(@room.lockSettingsDisableCam), - lockSettingsDisableMic: string_to_bool(@room.lockSettingsDisableMic), - lockSettingsDisableNote: string_to_bool(@room.lockSettingsDisableNote), - lockSettingsDisablePrivateChat: string_to_bool(@room.lockSettingsDisablePrivateChat), - lockSettingsDisablePublicChat: string_to_bool(@room.lockSettingsDisablePublicChat), - autoStartRecording: string_to_bool(@room.autoStartRecording), - allowStartStopRecording: string_to_bool(@room.allowStartStopRecording), - 'meta_description': @room.description.truncate(128, separator: ' '), + lockSettingsDisableCam: string_to_bool(room.lockSettingsDisableCam), + lockSettingsDisableMic: string_to_bool(room.lockSettingsDisableMic), + lockSettingsDisableNote: string_to_bool(room.lockSettingsDisableNote), + lockSettingsDisablePrivateChat: string_to_bool(room.lockSettingsDisablePrivateChat), + lockSettingsDisablePublicChat: string_to_bool(room.lockSettingsDisablePublicChat), + autoStartRecording: string_to_bool(room.autoStartRecording), + allowStartStopRecording: string_to_bool(room.allowStartStopRecording), + 'meta_description': room.description.truncate(128, separator: ' '), } # Send the create request. - bbb.create_meeting(@room.name, @room.handler, create_options) + bbb.create_meeting(room.name, room.handler, create_options) end - # Perform ends meeting for the current @room. + # Perform ends meeting for the current room. def end_meeting response = { returncode: 'FAILED' } begin - response = bbb.end_meeting(@room.handler, @room.moderator) + response = bbb.end_meeting(room.handler, room.moderator) rescue BigBlueButton::BigBlueButtonException # this can be thrown if all participants left (clicked 'x' before pressing the end button) end @@ -95,30 +95,30 @@ def end_meeting def meeting_info info = { returncode: 'FAILED' } begin - info = bbb.get_meeting_info(@room.handler, @user) + info = bbb.get_meeting_info(room.handler, @user) rescue BigBlueButton::BigBlueButtonException => e logger.error(e.to_s) end info end - # Checks if the meeting for current @room is running. + # Checks if the meeting for current room is running. def meeting_running? - bbb.is_meeting_running?(@room.handler) + bbb.is_meeting_running?(room.handler) rescue BigBlueButton::BigBlueButtonException => e logger.error(e.to_s) end # Fetches all recordings for a room. def recordings - res = Rails.cache.fetch("#{@room.handler}/#{RECORDINGS_KEY}", expires_in: 30.minutes) if Rails.configuration.cache_enabled - res ||= bbb.get_recordings(meetingID: @room.handler) + res = Rails.cache.fetch("#{room.handler}/#{RECORDINGS_KEY}", expires_in: 30.minutes) if Rails.configuration.cache_enabled + res ||= bbb.get_recordings(meetingID: room.handler) recordings_formatted(res) end # Fetch an individual recording def recording(record_id) - r = bbb.get_recordings(meetingID: @room.handler, recordID: record_id) + r = bbb.get_recordings(meetingID: room.handler, recordID: record_id) unless r.key?(:error) r[:playbacks] = if !r[:playback] || !r[:playback][:format] @@ -137,7 +137,7 @@ def recording(record_id) def server_running? begin - bbb.get_meeting_info(@room.handler, @user) + bbb.get_meeting_info(room.handler, @user) rescue BigBlueButton::BigBlueButtonException => e logger.info('We could not find a meeting with that meeting ID') return e.to_s @@ -148,34 +148,34 @@ def server_running? # Deletes a recording. def delete_recording(record_id) - Rails.cache.delete("#{@room.handler}/#{RECORDINGS_KEY}") if Rails.configuration.cache_enabled + Rails.cache.delete("#{room.handler}/#{RECORDINGS_KEY}") if Rails.configuration.cache_enabled bbb.delete_recordings(record_id) end # Publishes a recording. def publish_recording(record_id) - Rails.cache.delete("#{@room.handler}/#{RECORDINGS_KEY}") if Rails.configuration.cache_enabled + Rails.cache.delete("#{room.handler}/#{RECORDINGS_KEY}") if Rails.configuration.cache_enabled bbb.publish_recordings(record_id, true) end # Unpublishes a recording. def unpublish_recording(record_id) - Rails.cache.delete("#{@room.handler}/#{RECORDINGS_KEY}") if Rails.configuration.cache_enabled + Rails.cache.delete("#{room.handler}/#{RECORDINGS_KEY}") if Rails.configuration.cache_enabled bbb.publish_recordings(record_id, false) end # Updates a recording. def update_recording(record_id, meta) - Rails.cache.delete("#{@room.handler}/#{RECORDINGS_KEY}") if Rails.configuration.cache_enabled + Rails.cache.delete("#{room.handler}/#{RECORDINGS_KEY}") if Rails.configuration.cache_enabled meta[:recordID] = record_id bbb.send_api_request('updateRecordings', meta) end - # Check if the current @user must wait for moderator to join the current @room. + # Check if the current @user must wait for moderator to join the current room. def wait_for_mod? - return unless @room && @user + return unless room && @user - wait_setting = @room.waitForModerator != '0' + wait_setting = room.waitForModerator != '0' wait_setting && !@user.moderator?(bigbluebutton_moderator_roles) end @@ -268,4 +268,14 @@ def recordings_formatted(res) def string_to_bool(value) ActiveModel::Type::Boolean.new.cast(value) end + + # If the room is using a shared code, then use the shared room's recordings and bbb link + def room + use_shared_room? ? @shared_room : @room + end + + # Returns true only if @room.use_shared_code and the shared code is valid + def use_shared_room? + @room.use_shared_code && Room.where(code: @room.shared_code, tenant: @room.tenant).exists? + end end diff --git a/app/controllers/concerns/broker_helper.rb b/app/controllers/concerns/broker_helper.rb index 6c6a4dd8..0016a1ce 100644 --- a/app/controllers/concerns/broker_helper.rb +++ b/app/controllers/concerns/broker_helper.rb @@ -21,12 +21,12 @@ module BrokerHelper # Fetch tenant settings from the broker def tenant_settings(options = {}) tenant = options[:tenant] || @room&.tenant || '' - Rails.cache.fetch("rooms/tenant_settings/#{tenant}", expires_in: 1.hour) do - bbbltibroker_url = omniauth_bbbltibroker_url("/api/v1/tenants/#{tenant}") - get_response = RestClient.get(bbbltibroker_url, 'Authorization' => "Bearer #{omniauth_client_token(omniauth_bbbltibroker_url)}") + # Rails.cache.fetch("rooms/tenant_settings/#{tenant}", expires_in: 1.hour) do + bbbltibroker_url = omniauth_bbbltibroker_url("/api/v1/tenants/#{tenant}") + get_response = RestClient.get(bbbltibroker_url, 'Authorization' => "Bearer #{omniauth_client_token(omniauth_bbbltibroker_url)}") - JSON.parse(get_response) - end + JSON.parse(get_response) + # end rescue StandardError => e Rails.logger.error("Could not fetch tenant credentials from broker. Error message: #{e}") nil diff --git a/app/controllers/rooms_controller.rb b/app/controllers/rooms_controller.rb index 82e69f2f..b5d59293 100644 --- a/app/controllers/rooms_controller.rb +++ b/app/controllers/rooms_controller.rb @@ -99,10 +99,16 @@ def create # PATCH/PUT /rooms/1.json def update respond_to do |format| - if @room.update(room_params) + # block update if shared_code doesn't exist + shared_code = room_params[:shared_code] + code_found = shared_code.blank? ? true : Room.where(code: shared_code, tenant: @room.tenant).exists? + + if code_found && @room.update(room_params) format.html { redirect_to(room_path(@room, launch_nonce: params[:launch_nonce]), notice: t('default.room.updated')) } format.json { render(:show, status: :ok, location: @room) } else + # If the room wasn't updated because a code was not found then show an error message + flash.now[:alert] = code_found ? nil : t('error.room.codenotfound.message') format.html { render(:edit) } format.json { render(json: @error, status: :unprocessable_entity) } end @@ -267,6 +273,9 @@ def authenticate_user! # Use callbacks to share common setup or constraints between actions. def set_room @room = Room.find_by(id: params[:id]) + @shared_room = Room.find_by(code: @room.shared_code, tenant: @room.tenant) if @room&.use_shared_code + logger.debug("Room with id #{params[:id]} is using shared code: #{@room&.shared_code}") if @room&.use_shared_code + # Exit with error if room was not found set_error('notfound', :not_found) && return unless @room # Exit with error by re-setting the room to nil if the session for the room.handler is not set @@ -337,6 +346,9 @@ def room_params :all_moderators, :hide_name, :hide_description, + :code, + :shared_code, + :use_shared_code, settings: Room.stored_attributes[:settings] ) end @@ -353,7 +365,10 @@ def launch_params_to_new_room_params(handler, handler_legacy, launch_params) all_moderators: message_has_custom?(launch_params, 'all_moderators') || false, hide_name: message_has_custom?(launch_params, 'hide_name') || false, hide_description: message_has_custom?(launch_params, 'hide_description') || false, - settings: message_has_custom?(launch_params, 'settings') || {} + settings: message_has_custom?(launch_params, 'settings') || {}, + code: '', + shared_code: '', + use_shared_code: false ) end diff --git a/app/javascript/packs/edit.js b/app/javascript/packs/edit.js index e86c9270..aa4643a3 100644 --- a/app/javascript/packs/edit.js +++ b/app/javascript/packs/edit.js @@ -30,9 +30,7 @@ $(document).on('turbolinks:load', function(){ if (wait_mod_checked){ $('#allModerators_checkbox').prop("checked", false); } - }) - - + }) function check_record_status(){ var record_checked = $('#record_checkbox').prop("checked"); @@ -52,4 +50,29 @@ $(document).on('turbolinks:load', function(){ $('#record_checkbox').on('click', function() { check_record_status(); }) + + // If shared room is selected, allow the code field to be editable + $('#use_shared_code_checkbox').on('click', function() { + var use_shared_code_checked = $('#use_shared_code_checkbox').prop("checked"); + if (use_shared_code_checked){ + $('#shared_code_field').prop("disabled", false); + $('#shared_code_field').val(''); + } else { + $('#shared_code_field').prop("disabled", true); + console.log("code_val: = ",$('#room_code_value').val() ) + $('#shared_code_field').val($('#room_code_value').val()); + } + }) + + function checkSharedCodeCheckboxStatus() { + var sharedcode_checked = $('#use_shared_code_checkbox').prop("checked"); + if (!sharedcode_checked){ + $('#shared_code_field').prop("disabled", true); + } else { + $('#shared_code_field').prop("disabled", false); + } + } + + checkSharedCodeCheckboxStatus(); + }); diff --git a/app/models/room.rb b/app/models/room.rb index 339e603d..32219505 100644 --- a/app/models/room.rb +++ b/app/models/room.rb @@ -17,15 +17,19 @@ # with BigBlueButton; if not, see . class Room < ApplicationRecord before_save :default_values + validates :code, uniqueness: true store_accessor :settings, [:lockSettingsDisableCam, :lockSettingsDisableMic, :lockSettingsDisablePrivateChat, :lockSettingsDisablePublicChat, :lockSettingsDisableNote] store_accessor :settings, [:waitForModerator, :allModerators, :record, :autoStartRecording, :allowStartStopRecording] + + # after_find is used for the following so that rooms that already exist will have these fields upon launch after_find :initialize_setting_defaults, if: :settings_blank? - after_find :delete_settings + after_find :set_empty_code attr_accessor :can_grade RECORDING_SETTINGS = [:record, :autoStartRecording, :allowStartStopRecording].freeze + CODE_LENGTH = 10 def default_values self.handler ||= Digest::SHA1.hexdigest(SecureRandom.uuid) @@ -101,4 +105,12 @@ def bool_to_binary(value) '0' end + + # Assign a random alphanumeric code to the room if it doesn't already have one + # Assign the shared_code to equal to the room's code. + def set_empty_code + self.code = SecureRandom.alphanumeric(CODE_LENGTH) if code.blank? + self.shared_code = code if shared_code.blank? + save! + end end diff --git a/app/views/rooms/_form.html.erb b/app/views/rooms/_form.html.erb index 3740635a..346746a5 100644 --- a/app/views/rooms/_form.html.erb +++ b/app/views/rooms/_form.html.erb @@ -122,6 +122,25 @@
<% end %> + + <%= form.hidden_field :code, id: 'room_code_value', value: @room.code %> + +
+ <%= form.label t('default.room.code') %> + <%= form.text_field :shared_code, class: "form-control input mt-1 block disabled:border-slate-200 disabled:text-slate-500 disabled:shadow-none disabled:bg-slate-100", id: 'shared_code_field', disabled: true %> +
+ + <% unless flash[:alert] == nil %> +
+ <%= flash.alert %> +
+ <% end %> +
+ <%= form.check_box :use_shared_code, id: 'use_shared_code_checkbox' %>  + <%= t('default.room.usesharedcode') %>  +
+
+
diff --git a/app/views/shared/_room.html.erb b/app/views/shared/_room.html.erb index 9aae970b..e0c2ce3b 100644 --- a/app/views/shared/_room.html.erb +++ b/app/views/shared/_room.html.erb @@ -12,6 +12,9 @@ # You should have received a copy of the GNU Lesser General Public License along # with BigBlueButton; if not, see . %> + + <% room = @room.use_shared_code ? @shared_room : @room %> +