Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dbp 237 bbb dial in #2

Merged
merged 13 commits into from
Aug 18, 2023
4 changes: 3 additions & 1 deletion app/assets/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"close": "Schließen",
"delete": "Löschen",
"copy": "Einladungslink kopieren",
"copy_voice_bridge": "Telefoneinwahl kopieren",
"or": "oder",
"online": "Online",
"help_center": "Hilfe",
Expand Down Expand Up @@ -382,7 +383,8 @@
"access_code_copied": "Zugangscode kopiert.",
"access_code_generated": "Zugangscode generiert.",
"access_code_deleted": "Zugriffcode wurde gelöscht.",
"copied_meeting_url": "Die URL der Konferenz wurde kopiert. Der Link kann verwendet werden, um an der Konferenz teilzunehmen."
"copied_meeting_url": "Die URL der Konferenz wurde kopiert. Der Link kann verwendet werden, um an der Konferenz teilzunehmen.",
"copied_voice_bridge": "Die Telefonnummer und der Pin wurden kopiert. Diese können genutzt werden um an der Konferenz teilzunehmen."
},
"site_settings": {
"site_setting_updated": "Grundeinstellungen aktualisiert.",
Expand Down
4 changes: 3 additions & 1 deletion app/assets/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"close": "Close",
"delete": "Delete",
"copy": "Copy Join Link",
"copy_voice_bridge": "Copy phone dialup",
"or": "Or",
"online": "Online",
"help_center": "Help Center",
Expand Down Expand Up @@ -382,7 +383,8 @@
"access_code_copied": "The access code has been copied.",
"access_code_generated": "A new access code has been generated.",
"access_code_deleted": "The access code has been deleted.",
"copied_meeting_url": "The meeting URL has been copied. The link can be used to join the meeting."
"copied_meeting_url": "The meeting URL has been copied. The link can be used to join the meeting.",
"copied_voice_bridge": "The phone number and pin have been copied. They can be used to join the conference."
},
"site_settings": {
"site_setting_updated": "The site setting has been updated.",
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/api/v1/rooms_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def create
room = Room.new(name: room_params[:name], user_id: room_params[:user_id])

if room.save
logger.info "room(friendly_id):#{room.friendly_id} created for user(id):#{room.user_id}"
logger.info "room(friendly_id):#{room.friendly_id} created for user(id):#{room.user_id} with voice brige: #{room.voice_bridge}"
render_data status: :created
else
render_error errors: room.errors.to_a, status: :bad_request
Expand Down Expand Up @@ -160,7 +160,7 @@ def find_room
end

def room_params
params.require(:room).permit(:name, :user_id, :presentation)
params.require(:room).permit(:name, :user_id, :voice_bridge, :presentation)
end
end
end
Expand Down
15 changes: 14 additions & 1 deletion app/javascript/components/rooms/RoomCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import React, { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { Button, Card, Stack } from 'react-bootstrap';
import PropTypes from 'prop-types';
import { DocumentDuplicateIcon, LinkIcon } from '@heroicons/react/24/outline';
import { DocumentDuplicateIcon, LinkIcon, PhoneIcon } from '@heroicons/react/24/outline';
import { toast } from 'react-toastify';
import { useTranslation } from 'react-i18next';
import { useAuth } from '../../contexts/auth/AuthProvider';
Expand All @@ -41,6 +41,11 @@ export default function RoomCard({ room }) {
toast.success(t('toast.success.room.copied_meeting_url'));
}

function copyVoiceBridge(voice_bridge, voice_bridge_phone_number) {
navigator.clipboard.writeText(`Tel.: ${voice_bridge_phone_number} Pin: ${voice_bridge}`);
toast.success(t('toast.success.room.copied_voice_bridge'));
}

return (
<Card id="room-card" className="h-100 card-shadow border-0">
<Card.Body className="pb-0" onClick={handleClick}>
Expand Down Expand Up @@ -73,6 +78,12 @@ export default function RoomCard({ room }) {
>
<DocumentDuplicateIcon className="hi-m mt-1 text-muted" />
</Button>
{typeof room.voice_bridge_phone_number !== 'undefined' && <Button
variant="icon"
onClick={() => copyVoiceBridge(room.voice_bridge, room.voice_bridge_phone_number)}
>
<PhoneIcon className="hi-m mt-1 text-muted" />
</Button>}
<Button variant="brand-outline" className="btn btn-md float-end" onClick={startMeeting.mutate} disabled={startMeeting.isLoading}>
{startMeeting.isLoading && <Spinner className="me-2" />}
{ room.online ? (
Expand Down Expand Up @@ -101,5 +112,7 @@ RoomCard.propTypes = {
shared_owner: PropTypes.string,
online: PropTypes.bool,
participants: PropTypes.number,
voice_bridge: PropTypes.string,
voice_bridge_phone_number: PropTypes.string,
}).isRequired,
};
18 changes: 17 additions & 1 deletion app/javascript/components/rooms/room/Room.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
import {
Link, Navigate, useLocation, useParams,
} from 'react-router-dom';
import { HomeIcon, Square2StackIcon } from '@heroicons/react/24/outline';
import { HomeIcon, Square2StackIcon, PhoneIcon } from '@heroicons/react/24/outline';
import { toast } from 'react-toastify';
import { useTranslation } from 'react-i18next';
import { useAuth } from '../../../contexts/auth/AuthProvider';
Expand Down Expand Up @@ -51,6 +51,11 @@ export default function Room() {
toast.success(t('toast.success.room.copied_meeting_url'));
}

function copyVoiceBridge(voice_bridge, voice_bridge_phone_number) {
navigator.clipboard.writeText(`Tel.: ${voice_bridge_phone_number} Pin: ${voice_bridge}`);
toast.success(t('toast.success.room.copied_voice_bridge'));
}

// Custom logic to redirect from Rooms page to join page if this isnt the users room and they're not allowed to view it
if (isError && error.response.status === 403) {
return <Navigate to={`${location.pathname}/join`} />;
Expand Down Expand Up @@ -93,6 +98,12 @@ export default function Room() {
}
</Col>
<Col>
{
isRoomLoading
? (
<RoomNamePlaceHolder />
) : (
<>
<Button variant="brand" className="start-meeting-btn mt-1 mx-2 float-end" onClick={startMeeting.mutate} disabled={startMeeting.isLoading}>
{startMeeting.isLoading && <Spinner className="me-2" />}
{ room?.online ? (
Expand All @@ -105,6 +116,11 @@ export default function Room() {
<Square2StackIcon className="hi-s me-1" />
{ t('copy') }
</Button>
{ typeof room.voice_bridge_phone_number !== 'undefined' && <Button variant="brand-outline" className="mt-1 mx-2 float-end" onClick={() => copyVoiceBridge(room?.voice_bridge, room?.voice_bridge_phone_number)}>
<PhoneIcon className="hi-s me-1" />
{ t('copy_voice_bridge') }
</Button>}</>)
}
</Col>
</Row>
</div>
Expand Down
15 changes: 14 additions & 1 deletion app/models/room.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,15 @@ class Room < ApplicationRecord
validates :name, presence: true
validates :friendly_id, presence: true, uniqueness: true
validates :meeting_id, presence: true, uniqueness: true
validates :voice_bridge, uniqueness: true
validates :presentation,
content_type: Rails.configuration.uploads[:presentations][:formats],
size: { less_than: Rails.configuration.uploads[:presentations][:max_size] }

validates :name, length: { minimum: 2, maximum: 255 }
validates :recordings_processing, numericality: { only_integer: true, greater_than_or_equal_to: 0 }

before_validation :set_friendly_id, :set_meeting_id, on: :create
before_validation :set_friendly_id, :set_meeting_id, :set_voice_brige, on: :create
after_create :create_meeting_options

attr_accessor :shared, :active, :participants
Expand Down Expand Up @@ -99,4 +100,16 @@ def set_meeting_id
rescue StandardError
retry
end

# Create unique pin for voice brige max 10^5 - 10000 unique ids
def set_voice_brige
if Rails.application.config.voice_bridge_phone_number != nil
id = SecureRandom.random_number((10.pow(5)) - 1)
raise if Room.exists?(voice_bridge: id) || id < 10000

self.voice_bridge = id
end
rescue StandardError
retry
end
end
7 changes: 7 additions & 0 deletions app/serializers/current_room_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ class CurrentRoomSerializer < ApplicationSerializer

attribute :last_session, if: -> { object.last_session }

attribute :voice_bridge, if: -> { Rails.application.config.voice_bridge_phone_number }
attribute :voice_bridge_phone_number, if: -> { Rails.application.config.voice_bridge_phone_number }

def presentation_name
presentation_file_name(object)
end
Expand All @@ -34,4 +37,8 @@ def thumbnail
def owner_name
object.user.name
end

def voice_bridge_phone_number
Rails.application.config.voice_bridge_phone_number
end
end
6 changes: 6 additions & 0 deletions app/serializers/room_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,14 @@ class RoomSerializer < ApplicationSerializer
attributes :id, :name, :friendly_id, :online, :participants, :last_session

attribute :shared_owner, if: -> { object.shared }
attribute :voice_bridge, if: -> { Rails.application.config.voice_bridge_phone_number }
attribute :voice_bridge_phone_number, if: -> { Rails.application.config.voice_bridge_phone_number }

def shared_owner
object.user.name
end

def voice_bridge_phone_number
Rails.application.config.voice_bridge_phone_number
end
end
2 changes: 1 addition & 1 deletion app/services/meeting_starter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def initialize(room:, base_url:, current_user:, provider:)

def call
# TODO: amir - Check the legitimately of the action.
options = RoomSettingsGetter.new(room_id: @room.id, provider: @room.user.provider, current_user: @current_user, only_bbb_options: true).call
options = RoomSettingsGetter.new(room_id: @room.id, provider: @room.user.provider, current_user: @current_user, only_bbb_options: true, voice_bridge: @room.voice_bridge).call
viewer_code = RoomSettingsGetter.new(
room_id: @room.id,
provider: @room.user.provider,
Expand Down
11 changes: 10 additions & 1 deletion app/services/room_settings_getter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ class RoomSettingsGetter
# Hash(`<option_name> => {'true' => <Postive>, 'false' => <Negative>})`
SPECIAL_OPTIONS = { 'guestPolicy' => { 'true' => 'ASK_MODERATOR', 'false' => 'ALWAYS_ACCEPT' } }.freeze

def initialize(room_id:, provider:, current_user:, settings: [], show_codes: false, only_enabled: false, only_bbb_options: false)
def initialize(room_id:, provider:, current_user:, settings: [], show_codes: false, only_enabled: false, only_bbb_options: false, voice_bridge: nil)
@current_user = current_user
@room_id = room_id
@only_bbb_options = only_bbb_options # When used only BBB options (not prefixed with 'gl') will be returned.
@only_enabled = only_enabled # When used only optional and force enabled options will be returned.
@show_codes = show_codes # When used access code values will be returned.
@settings = settings # When given only the settings contained in the Array<String> will be returned.
@voice_bridge = voice_bridge

# Fetching only rooms configs that are not optional to overwrite the settings values.
@rooms_configs = MeetingOption.joins(:rooms_configurations)
Expand All @@ -55,6 +56,8 @@ def call
infer_codes(room_settings:, access_codes:) # Access codes should map their forced values as intended.
infer_can_record(room_settings:) if room_settings['record'] && @rooms_configs['record'].nil?

set_voice_brige(room_settings:)

room_settings
end

Expand Down Expand Up @@ -98,4 +101,10 @@ def infer_can_record(room_settings:)

room_settings['record'] = 'false'
end

def set_voice_brige(room_settings:)
if @voice_bridge != nil
room_settings['voiceBridge'] = "#{@voice_bridge}"
end
end
end
3 changes: 3 additions & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ class Application < Rails::Application

config.bigbluebutton_secret = ENV.fetch('BIGBLUEBUTTON_SECRET', '8cd8ef52e8e101574e400365b55e11a6')

config.voice_bridge_phone_number = ENV.fetch('VOICE_BRIDGE_PHONE_NUMBER', nil)

config.relative_url_root = ENV.fetch('RELATIVE_URL_ROOT', '/')
# Fetch 'RELATIVE_URL_ROOT' ENV variable value while removing any trailing slashes.
config.relative_url_root = ENV.fetch('RELATIVE_URL_ROOT', nil)&.sub(%r{/*\z}, '')
config.relative_url_root = '/' if config.relative_url_root.blank?
Expand Down
34 changes: 34 additions & 0 deletions db/data/20230328124724_populate_voice_brige_for_existing_rooms.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# frozen_string_literal: true

class PopulateVoiceBrigeForExistingRooms < ActiveRecord::Migration[7.0]
def up
if Rails.application.config.voice_bridge_phone_number == nil
return
end

if Room.all.length > 89999
raise "The db contains to many rooms to assign each one a unique voice_brige"
end

Room.where(voice_bridge: nil).each do |room|
id = SecureRandom.random_number((10.pow(5)) - 1)

if id < 10000
id = id + 10000
end

while Room.exists?(voice_bridge: id)
id = id + 1
if id >= 99999
id = 10000
end
end

room.update(voice_bridge: id)
end
end

def down
Room.update_all(voice_bridge: nil)
end
end
2 changes: 1 addition & 1 deletion db/data_schema.rb
Original file line number Diff line number Diff line change
@@ -1 +1 @@
DataMigrate::Data.define(version: 20230228193705)
DataMigrate::Data.define(version: 20230328124724)
6 changes: 6 additions & 0 deletions db/migrate/20230321125010_add_voice_brige_to_romms.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class AddVoiceBrigeToRomms < ActiveRecord::Migration[7.0]
def change
add_column :rooms, :voice_bridge, :integer, null: true, default: nil
add_index :rooms, :voice_bridge
end
end
2 changes: 2 additions & 0 deletions db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions sample.env
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ REDIS_URL=
# [en, ar, fr, es]
#DEFAULT_LOCALE=en

# Define the phone number for the voice bridge.
# This number is not sent to bbb and is only displayed in the greenlight UI, but it should match the number in the bbb instance to avoid user confusion.
# If this number is defined, each newly created room will be assigned a static voiceBridge pin.
#VOICE_BRIDGE_PHONE_NUMBER=

# Set this if you like to deploy Greenlight on a relative root path other than /
#RELATIVE_URL_ROOT=/gl

Expand Down