Skip to content

Commit

Permalink
Merge pull request #2 from dBildungsplattform/DBP-237-bbb-dial-in
Browse files Browse the repository at this point in the history
Dbp 237 bbb dial in
  • Loading branch information
simoncolincap authored Aug 18, 2023
2 parents 7a8b385 + 6a935a4 commit 80a30b2
Show file tree
Hide file tree
Showing 16 changed files with 128 additions and 10 deletions.
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

0 comments on commit 80a30b2

Please sign in to comment.