bigbluebutton_rails 1.4.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +10 -1
  4. data/CHANGELOG.md +259 -0
  5. data/Gemfile +14 -19
  6. data/Gemfile.lock +194 -188
  7. data/README.md +416 -0
  8. data/Rakefile +12 -12
  9. data/TODO.md +13 -0
  10. data/app/controllers/bigbluebutton/playback_types_controller.rb +44 -0
  11. data/app/controllers/bigbluebutton/recordings_controller.rb +17 -10
  12. data/app/controllers/bigbluebutton/rooms_controller.rb +65 -36
  13. data/app/controllers/bigbluebutton/servers_controller.rb +12 -8
  14. data/app/models/bigbluebutton_metadata.rb +1 -1
  15. data/app/models/bigbluebutton_playback_format.rb +22 -1
  16. data/app/models/bigbluebutton_playback_type.rb +29 -0
  17. data/app/models/bigbluebutton_recording.rb +38 -19
  18. data/app/models/bigbluebutton_room.rb +150 -73
  19. data/app/models/bigbluebutton_room_options.rb +10 -7
  20. data/app/models/bigbluebutton_server.rb +69 -13
  21. data/app/models/bigbluebutton_server_config.rb +49 -0
  22. data/app/views/bigbluebutton/recordings/_form.html.erb +4 -0
  23. data/app/views/bigbluebutton/recordings/_recordings.html.erb +10 -3
  24. data/app/views/bigbluebutton/rooms/_form.html.erb +18 -6
  25. data/app/views/bigbluebutton/rooms/_rooms.html.erb +6 -3
  26. data/app/views/bigbluebutton/rooms/invite.html.erb +5 -5
  27. data/app/views/bigbluebutton/rooms/show.html.erb +18 -6
  28. data/app/views/bigbluebutton/servers/_form.html.erb +1 -1
  29. data/app/views/bigbluebutton/servers/show.html.erb +5 -0
  30. data/app/workers/bigbluebutton_finish_meetings.rb +11 -0
  31. data/app/workers/bigbluebutton_update_recordings.rb +11 -0
  32. data/app/workers/bigbluebutton_update_server_configs.rb +18 -0
  33. data/bigbluebutton_rails.gemspec +8 -7
  34. data/config/locales/en.yml +52 -7
  35. data/config/locales/pt-br.yml +162 -0
  36. data/config/resque/resque.rake +27 -0
  37. data/config/resque/workers_schedule.yml +17 -0
  38. data/lib/bigbluebutton_rails.rb +30 -2
  39. data/lib/bigbluebutton_rails/background_tasks.rb +31 -0
  40. data/lib/bigbluebutton_rails/controller_methods.rb +12 -8
  41. data/lib/bigbluebutton_rails/dial_number.rb +48 -0
  42. data/lib/bigbluebutton_rails/rails/routes.rb +9 -2
  43. data/lib/bigbluebutton_rails/utils.rb +9 -2
  44. data/lib/bigbluebutton_rails/version.rb +1 -1
  45. data/lib/generators/bigbluebutton_rails/install_generator.rb +0 -8
  46. data/lib/generators/bigbluebutton_rails/templates/migration.rb +28 -6
  47. data/lib/generators/bigbluebutton_rails/templates/migration_2_0_0.rb +56 -0
  48. data/lib/tasks/bigbluebutton_rails/meetings.rake +1 -13
  49. data/lib/tasks/bigbluebutton_rails/recordings.rake +1 -12
  50. data/lib/tasks/bigbluebutton_rails/server_configs.rake +10 -0
  51. data/spec/bigbluebutton_rails_spec.rb +0 -13
  52. data/spec/controllers/bigbluebutton/playback_types_controller_spec.rb +76 -0
  53. data/spec/controllers/bigbluebutton/recordings_controller_spec.rb +109 -37
  54. data/spec/controllers/bigbluebutton/rooms_controller_exception_handling_spec.rb +1 -0
  55. data/spec/controllers/bigbluebutton/rooms_controller_json_responses_spec.rb +27 -3
  56. data/spec/controllers/bigbluebutton/rooms_controller_spec.rb +338 -77
  57. data/spec/controllers/bigbluebutton/servers_controller_json_responses_spec.rb +8 -0
  58. data/spec/controllers/bigbluebutton/servers_controller_spec.rb +182 -47
  59. data/spec/factories/bigbluebutton_meeting.rb +2 -2
  60. data/spec/factories/bigbluebutton_playback_format.rb +2 -2
  61. data/spec/factories/bigbluebutton_playback_type.rb +7 -0
  62. data/spec/factories/bigbluebutton_recording.rb +8 -0
  63. data/spec/factories/bigbluebutton_room.rb +10 -3
  64. data/spec/factories/bigbluebutton_server.rb +6 -1
  65. data/spec/factories/bigbluebutton_server_config.rb +6 -0
  66. data/spec/generators/install_generator_spec.rb +0 -16
  67. data/spec/lib/bigbluebutton_rails/background_tasks_spec.rb +61 -0
  68. data/spec/lib/bigbluebutton_rails/dial_number_spec.rb +78 -0
  69. data/spec/lib/bigbluebutton_rails/utils_spec.rb +56 -0
  70. data/spec/lib/tasks/meetings_rake_spec.rb +14 -1
  71. data/spec/lib/tasks/recordings_rake_spec.rb +14 -1
  72. data/spec/models/bigbluebutton_meeting_db_spec.rb +3 -3
  73. data/spec/models/bigbluebutton_meeting_spec.rb +4 -4
  74. data/spec/models/bigbluebutton_playback_format_db_spec.rb +0 -1
  75. data/spec/models/bigbluebutton_playback_format_spec.rb +75 -4
  76. data/spec/models/bigbluebutton_playback_type_db_spec.rb +14 -0
  77. data/spec/models/bigbluebutton_playback_type_spec.rb +76 -0
  78. data/spec/models/bigbluebutton_recording_db_spec.rb +3 -1
  79. data/spec/models/bigbluebutton_recording_spec.rb +234 -58
  80. data/spec/models/bigbluebutton_room_db_spec.rb +8 -6
  81. data/spec/models/bigbluebutton_room_options_db_spec.rb +1 -0
  82. data/spec/models/bigbluebutton_room_options_spec.rb +137 -38
  83. data/spec/models/bigbluebutton_room_spec.rb +540 -153
  84. data/spec/models/bigbluebutton_server_config_spec.rb +115 -0
  85. data/spec/models/bigbluebutton_server_spec.rb +180 -23
  86. data/spec/rails_app/app/controllers/my_playback_types_controller.rb +7 -0
  87. data/spec/rails_app/app/views/frontpage/show.html.erb +1 -0
  88. data/spec/rails_app/app/views/my_playback_types/index.html.erb +33 -0
  89. data/spec/rails_app/config/application.rb +0 -3
  90. data/spec/rails_app/config/database.yml.travis +10 -0
  91. data/spec/rails_app/config/environments/development.rb +1 -6
  92. data/spec/rails_app/config/environments/production.rb +2 -0
  93. data/spec/rails_app/config/environments/test.rb +2 -0
  94. data/spec/rails_app/config/routes.rb +5 -2
  95. data/spec/rails_app/db/seeds.rb +1 -1
  96. data/spec/rails_app/features/config.yml.example +3 -4
  97. data/spec/rails_app/features/join_rooms.feature +14 -14
  98. data/spec/rails_app/features/step_definitions/activity_monitor_servers_step.rb +2 -2
  99. data/spec/rails_app/features/step_definitions/create_rooms_steps.rb +4 -4
  100. data/spec/rails_app/features/step_definitions/destroy_rooms_steps.rb +2 -2
  101. data/spec/rails_app/features/step_definitions/join_rooms_steps.rb +10 -10
  102. data/spec/rails_app/features/step_definitions/web_steps.rb +2 -2
  103. data/spec/rails_app/features/support/configurations.rb +1 -1
  104. data/spec/rails_app/features/support/templates.rb +12 -12
  105. data/spec/rails_app/lib/tasks/db/populate.rake +19 -6
  106. data/spec/routing/bigbluebutton/custom_controllers_routing_spec.rb +1 -1
  107. data/spec/routing/bigbluebutton/recordings_only_routing_spec.rb +2 -2
  108. data/spec/routing/bigbluebutton/recordings_routing_spec.rb +2 -2
  109. data/spec/routing/bigbluebutton/rooms_only_routing_spec.rb +1 -1
  110. data/spec/routing/bigbluebutton/rooms_routing_spec.rb +15 -1
  111. data/spec/routing/bigbluebutton/servers_only_routing_spec.rb +1 -1
  112. data/spec/routing/bigbluebutton/servers_routing_spec.rb +1 -1
  113. data/spec/spec_helper.rb +23 -31
  114. data/spec/support/matchers/delegate_matcher.rb +8 -1
  115. data/spec/support/matchers/shoulda/respond_with_json_matcher.rb +2 -2
  116. data/spec/support/mocked_server.rb +2 -0
  117. data/spec/support/shared_contexts/rake.rb +23 -0
  118. data/spec/workers/bigbluebutton_finish_meetings_spec.rb +14 -0
  119. data/spec/workers/bigbluebutton_update_recordings_spec.rb +14 -0
  120. data/spec/workers/bigbluebutton_update_server_configs_spec.rb +47 -0
  121. metadata +70 -59
  122. data/CHANGELOG.rdoc +0 -111
  123. data/README.rdoc +0 -319
  124. data/TODO.rdoc +0 -16
  125. data/config/schedule.rb +0 -7
@@ -21,12 +21,17 @@ class BigbluebuttonRecording < ActiveRecord::Base
21
21
  :foreign_key => 'recording_id',
22
22
  :dependent => :destroy
23
23
 
24
- scope :published, where(:published => true)
24
+ scope :published, -> { where(:published => true) }
25
25
 
26
26
  def to_param
27
27
  self.recordid
28
28
  end
29
29
 
30
+ def default_playback_format
31
+ playback_formats.joins(:playback_type)
32
+ .where("bigbluebutton_playback_types.default = ?", true).first
33
+ end
34
+
30
35
  # Syncs the recordings in the db with the array of recordings in 'recordings',
31
36
  # as received from BigBlueButtonApi#get_recordings.
32
37
  # Will add new recordings that are not in the db yet and update the ones that
@@ -56,14 +61,14 @@ class BigbluebuttonRecording < ActiveRecord::Base
56
61
  # set as unavailable the recordings that are not in 'recordings'
57
62
  recordIDs = recordings.map{ |rec| rec[:recordID] }
58
63
  if recordIDs.length <= 0 # empty response
59
- BigbluebuttonRecording
60
- .where(:available => true)
61
- .update_all(:available => false)
64
+ BigbluebuttonRecording.
65
+ where(available: true, server: server).
66
+ update_all(available: false)
62
67
  else
63
- BigbluebuttonRecording
64
- .where(:available => true)
65
- .where("recordid NOT IN (?)", recordIDs)
66
- .update_all(:available => false)
68
+ BigbluebuttonRecording.
69
+ where(available: true, server: server).
70
+ where.not(recordid: recordIDs).
71
+ update_all(available: false)
67
72
  end
68
73
  end
69
74
 
@@ -89,7 +94,7 @@ class BigbluebuttonRecording < ActiveRecord::Base
89
94
  def self.update_recording(server, recording, data)
90
95
  recording.server = server
91
96
  recording.room = BigbluebuttonRails.match_room_recording(data)
92
- recording.attributes = data.slice(:meetingid, :name, :published, :start_time, :end_time)
97
+ recording.attributes = data.slice(:meetingid, :name, :published, :start_time, :end_time, :size)
93
98
  recording.available = true
94
99
  recording.save!
95
100
 
@@ -100,7 +105,7 @@ class BigbluebuttonRecording < ActiveRecord::Base
100
105
  # The format expected for 'data' follows the format returned by
101
106
  # BigBlueButtonApi#get_recordings but with the keys already converted to our format.
102
107
  def self.create_recording(server, data)
103
- filtered = data.slice(:recordid, :meetingid, :name, :published, :start_time, :end_time)
108
+ filtered = data.slice(:recordid, :meetingid, :name, :published, :start_time, :end_time, :size)
104
109
  recording = BigbluebuttonRecording.create(filtered)
105
110
  recording.available = true
106
111
  recording.room = BigbluebuttonRails.match_room_recording(data)
@@ -163,16 +168,16 @@ class BigbluebuttonRecording < ActiveRecord::Base
163
168
  formats_copy = formats.clone
164
169
 
165
170
  # make it an array if it's a hash with a single format
166
- formats_copy = [ formats_copy ] if formats_copy.is_a?(Hash)
171
+ formats_copy = [formats_copy] if formats_copy.is_a?(Hash)
167
172
 
168
- BigbluebuttonPlaybackFormat.where(:recording_id => recording.id).each do |format_db|
169
- format = formats_copy.select{ |d|
170
- !d[:type].blank? and d[:type] == format_db.format_type
171
- }.first
173
+ BigbluebuttonPlaybackFormat.where(recording_id: recording.id).each do |format_db|
174
+ format = formats_copy.select do |d|
175
+ !d[:type].blank? && d[:type] == format_db.format_type
176
+ end.first
172
177
 
173
178
  # the format exists in the hash, update it in the db
174
179
  if format
175
- format_db.update_attributes({ :url => format[:url], :length => format[:length] })
180
+ format_db.update_attributes({ url: format[:url], length: format[:length] })
176
181
  formats_copy.delete(format)
177
182
 
178
183
  # the format is not in the hash, remove from the db
@@ -181,14 +186,28 @@ class BigbluebuttonRecording < ActiveRecord::Base
181
186
  end
182
187
  end
183
188
 
184
- # for formats that are not in the db yet
189
+ # create the formats that are not in the db yet
185
190
  formats_copy.each do |format|
186
191
  unless format[:type].blank?
187
- attrs = { :recording_id => recording.id, :format_type => format[:type],
188
- :url => format[:url], :length => format[:length].to_i }
192
+ playback_type = BigbluebuttonPlaybackType.find_by_identifier(format[:type])
193
+ if playback_type.nil?
194
+ attrs = { identifier: format[:type], visible: true }
195
+ playback_type = BigbluebuttonPlaybackType.create!(attrs)
196
+ end
197
+
198
+ attrs = { recording_id: recording.id, url: format[:url],
199
+ length: format[:length].to_i, playback_type_id: playback_type.id }
189
200
  BigbluebuttonPlaybackFormat.create!(attrs)
190
201
  end
191
202
  end
203
+
204
+ cleanup_playback_types
205
+ end
206
+
207
+ # Remove the unused playback types from the list.
208
+ def self.cleanup_playback_types
209
+ ids = BigbluebuttonPlaybackFormat.uniq.pluck(:playback_type_id)
210
+ BigbluebuttonPlaybackType.destroy_all(['id NOT IN (?)', ids])
192
211
  end
193
212
 
194
213
  # Finds the BigbluebuttonMeeting that generated this recording. The meeting is searched using
@@ -1,8 +1,9 @@
1
1
  class BigbluebuttonRoom < ActiveRecord::Base
2
2
  include ActiveModel::ForbiddenAttributesProtection
3
3
 
4
- belongs_to :server, :class_name => 'BigbluebuttonServer'
5
- belongs_to :owner, :polymorphic => true
4
+ belongs_to :owner, polymorphic: true
5
+
6
+ belongs_to :server, class_name: 'BigbluebuttonServer'
6
7
 
7
8
  has_many :recordings,
8
9
  :class_name => 'BigbluebuttonRecording',
@@ -24,8 +25,8 @@ class BigbluebuttonRoom < ActiveRecord::Base
24
25
  delegate :default_layout, :default_layout=, :to => :room_options
25
26
  delegate :presenter_share_only, :presenter_share_only=, :to => :room_options
26
27
  delegate :auto_start_video, :auto_start_video=, :to => :room_options
27
- delegate :auto_start_audio, :auto_start_audio=, :to => :room_options
28
- delegate :get_available_layouts, :to => :room_options
28
+ delegate :auto_start_audio, :auto_start_audio=, :to => :room_options
29
+ delegate :background, :background=, :to => :room_options
29
30
 
30
31
  accepts_nested_attributes_for :metadata,
31
32
  :allow_destroy => true,
@@ -33,12 +34,11 @@ class BigbluebuttonRoom < ActiveRecord::Base
33
34
 
34
35
  validates :meetingid, :presence => true, :uniqueness => true,
35
36
  :length => { :minimum => 1, :maximum => 100 }
36
- validates :name, :presence => true, :uniqueness => true,
37
+ validates :name, :presence => true,
37
38
  :length => { :minimum => 1, :maximum => 150 }
38
39
  validates :welcome_msg, :length => { :maximum => 250 }
39
40
  validates :private, :inclusion => { :in => [true, false] }
40
- validates :voice_bridge, :presence => true, :uniqueness => true
41
- validates :record, :inclusion => { :in => [true, false] }
41
+ validates :record_meeting, :inclusion => { :in => [true, false] }
42
42
 
43
43
  validates :duration,
44
44
  :presence => true,
@@ -48,16 +48,16 @@ class BigbluebuttonRoom < ActiveRecord::Base
48
48
  :presence => true,
49
49
  :uniqueness => true,
50
50
  :length => { :minimum => 1 },
51
- :format => { :with => /^([a-zA-Z\d_]|[a-zA-Z\d_]+[a-zA-Z\d_-]*[a-zA-Z\d_]+)$/,
51
+ :format => { :with => /\A([a-zA-Z\d_]|[a-zA-Z\d_]+[a-zA-Z\d_-]*[a-zA-Z\d_]+)\z/,
52
52
  :message => I18n.t('bigbluebutton_rails.rooms.errors.param_format') }
53
53
 
54
54
  # Passwords are 16 character strings
55
55
  # See http://groups.google.com/group/bigbluebutton-dev/browse_thread/thread/9be5aae1648bcab?pli=1
56
- validates :attendee_password, :length => { :maximum => 16 }
57
- validates :moderator_password, :length => { :maximum => 16 }
56
+ validates :attendee_key, :length => { :maximum => 16 }
57
+ validates :moderator_key, :length => { :maximum => 16 }
58
58
 
59
- validates :attendee_password, :presence => true, :if => :private?
60
- validates :moderator_password, :presence => true, :if => :private?
59
+ validates :attendee_key, :presence => true, :if => :private?
60
+ validates :moderator_key, :presence => true, :if => :private?
61
61
 
62
62
  # Note: these params need to be fetched from the server before being accessed
63
63
  attr_accessor :running, :participant_count, :moderator_count, :attendees,
@@ -66,7 +66,7 @@ class BigbluebuttonRoom < ActiveRecord::Base
66
66
  after_initialize :init
67
67
  after_create :create_room_options
68
68
  before_validation :set_param
69
- before_validation :set_passwords
69
+ before_validation :set_keys
70
70
 
71
71
  # the full logout_url used when logout_url is a relative path
72
72
  attr_accessor :full_logout_url
@@ -105,9 +105,9 @@ class BigbluebuttonRoom < ActiveRecord::Base
105
105
  #
106
106
  # Triggers API call: <tt>getMeetingInfo</tt>.
107
107
  def fetch_meeting_info
108
- require_server
108
+ require_server :get_meeting_info
109
109
 
110
- response = self.server.api.get_meeting_info(self.meetingid, self.moderator_password)
110
+ response = self.server.api.get_meeting_info(self.meetingid, self.moderator_api_password)
111
111
 
112
112
  @participant_count = response[:participantCount]
113
113
  @moderator_count = response[:moderatorCount]
@@ -132,7 +132,7 @@ class BigbluebuttonRoom < ActiveRecord::Base
132
132
  #
133
133
  # Triggers API call: <tt>isMeetingRunning</tt>.
134
134
  def fetch_is_running?
135
- require_server
135
+ require_server :is_meeting_running
136
136
  @running = self.server.api.is_meeting_running?(self.meetingid)
137
137
  end
138
138
 
@@ -140,8 +140,8 @@ class BigbluebuttonRoom < ActiveRecord::Base
140
140
  #
141
141
  # Triggers API call: <tt>end</tt>.
142
142
  def send_end
143
- require_server
144
- response = self.server.api.end_meeting(self.meetingid, self.moderator_password)
143
+ require_server :end
144
+ response = self.server.api.end_meeting(self.meetingid, self.moderator_api_password)
145
145
 
146
146
  # enqueue an update in the meetings for later on
147
147
  Resque.enqueue(::BigbluebuttonMeetingUpdater, self.id, 15.seconds)
@@ -159,21 +159,25 @@ class BigbluebuttonRoom < ActiveRecord::Base
159
159
  # will be created. If a server is selected, the model is saved.
160
160
  #
161
161
  # With the response, updates the following attributes:
162
- # * <tt>attendee_password</tt>
163
- # * <tt>moderator_password</tt>
162
+ # * <tt>attendee_api_password</tt>
163
+ # * <tt>moderator_api_password</tt>
164
164
  #
165
165
  # Triggers API call: <tt>create</tt>.
166
166
  def send_create(user=nil, user_opts={})
167
167
  # updates the server whenever a meeting will be created and guarantees it has a meetingid
168
- self.server = select_server
169
- self.meetingid = unique_meetingid() if self.meetingid.nil?
168
+ require_server :create
169
+
170
+ self.meetingid = unique_meetingid() if self.meetingid.blank?
171
+ self.moderator_api_password = internal_password() if self.moderator_api_password.blank?
172
+ self.attendee_api_password = internal_password() if self.attendee_api_password.blank?
170
173
  self.save unless self.new_record?
171
- require_server
172
174
 
173
175
  response = internal_create_meeting(user, user_opts)
174
176
  unless response.nil?
175
- self.attendee_password = response[:attendeePW]
176
- self.moderator_password = response[:moderatorPW]
177
+ self.attendee_api_password = response[:attendeePW]
178
+ self.moderator_api_password = response[:moderatorPW]
179
+ self.create_time = response[:createTime]
180
+ self.voice_bridge = response[:voiceBridge] if response.has_key?(:voiceBridge)
177
181
  self.save unless self.new_record?
178
182
  end
179
183
 
@@ -183,20 +187,20 @@ class BigbluebuttonRoom < ActiveRecord::Base
183
187
  # Returns the URL to join this room.
184
188
  # username:: Name of the user
185
189
  # role:: Role of the user in this room. Can be <tt>[:moderator, :attendee]</tt>
186
- # password:: Password to be use (in case role == nil)
190
+ # key:: Key to be use (in case role == nil)
187
191
  # options:: Additional options to use when generating the URL
188
192
  #
189
193
  # Uses the API but does not require a request to the server.
190
- def join_url(username, role, password=nil, options={})
191
- require_server
194
+ def join_url(username, role, key=nil, options={})
195
+ require_server :join_meeting_url
192
196
 
193
197
  case role
194
198
  when :moderator
195
- r = self.server.api.join_meeting_url(self.meetingid, username, self.moderator_password, options)
199
+ r = self.server.api.join_meeting_url(self.meetingid, username, self.moderator_api_password, options)
196
200
  when :attendee
197
- r = self.server.api.join_meeting_url(self.meetingid, username, self.attendee_password, options)
201
+ r = self.server.api.join_meeting_url(self.meetingid, username, self.attendee_api_password, options)
198
202
  else
199
- r = self.server.api.join_meeting_url(self.meetingid, username, password, options)
203
+ r = self.server.api.join_meeting_url(self.meetingid, username, map_key_to_internal_password(key), options)
200
204
  end
201
205
 
202
206
  r.strip! unless r.nil?
@@ -204,16 +208,16 @@ class BigbluebuttonRoom < ActiveRecord::Base
204
208
  end
205
209
 
206
210
 
207
- # Returns the role of the user based on the password given.
211
+ # Returns the role of the user based on the key given.
208
212
  # The return value can be <tt>:moderator</tt>, <tt>:attendee</tt>, or
209
- # nil if the password given does not match any of the room passwords.
210
- # params:: Hash with a key :password
213
+ # nil if the key given does not match any of the room keys.
214
+ # params:: Hash with a key :key
211
215
  def user_role(params)
212
216
  role = nil
213
- if params && params.has_key?(:password)
214
- if self.moderator_password == params[:password]
217
+ if params && params.has_key?(:key)
218
+ if self.moderator_key == params[:key]
215
219
  role = :moderator
216
- elsif self.attendee_password == params[:password]
220
+ elsif self.attendee_key == params[:key]
217
221
  role = :attendee
218
222
  end
219
223
  end
@@ -250,6 +254,10 @@ class BigbluebuttonRoom < ActiveRecord::Base
250
254
  def create_meeting(user=nil, request=nil, user_opts={})
251
255
  fetch_is_running?
252
256
  unless is_running?
257
+
258
+ # in case the meeting is not running but it's still in memory
259
+ suppress(BigBlueButton::BigBlueButtonException) { send_end }
260
+
253
261
  add_domain_to_logout_url(request.protocol, request.host_with_port) unless request.nil?
254
262
  send_create(user, user_opts)
255
263
  true
@@ -296,7 +304,7 @@ class BigbluebuttonRoom < ActiveRecord::Base
296
304
  :server => self.server,
297
305
  :meetingid => self.meetingid,
298
306
  :name => self.name,
299
- :record => self.record,
307
+ :recorded => self.record_meeting,
300
308
  :running => self.running
301
309
  }
302
310
  unless metadata.nil?
@@ -326,9 +334,9 @@ class BigbluebuttonRoom < ActiveRecord::Base
326
334
 
327
335
  # Sets all meetings related to this room as not running
328
336
  def finish_meetings
329
- BigbluebuttonMeeting.where(:running => true)
330
- .find_by_room_id(room_id)
331
- .update_attributes(:running => false)
337
+ BigbluebuttonMeeting.where(running: true)
338
+ .where(room_id: self.id)
339
+ .update_all(running: false)
332
340
  end
333
341
 
334
342
  # Gets a 'configToken' to use when joining the room.
@@ -354,7 +362,7 @@ class BigbluebuttonRoom < ActiveRecord::Base
354
362
  # returns true if something was changed
355
363
  config_xml = self.room_options.set_on_config_xml(config_xml)
356
364
  if config_xml
357
-
365
+ self.server.update_config(config_xml)
358
366
  # get the new token for the room, and return it
359
367
  self.server.api.set_config_xml(self.meetingid, config_xml)
360
368
  else
@@ -365,6 +373,33 @@ class BigbluebuttonRoom < ActiveRecord::Base
365
373
  end
366
374
  end
367
375
 
376
+ def available_layouts
377
+ server.present? ? server.available_layouts : []
378
+ end
379
+
380
+ def available_layouts_names
381
+ server.present? ? server.available_layouts_names : []
382
+ end
383
+
384
+ def available_layouts_for_select
385
+ server.present? ? server.available_layouts_for_select : []
386
+ end
387
+
388
+ # Generates a new dial number following `pattern` and saves it in the room, returning
389
+ # the results of `update_attributes`.
390
+ # Will always generate a unique number. Tries several times if the number already
391
+ # exists and returns `nil` in case it wasn't possible to generate a unique value.
392
+ def generate_dial_number!(pattern=nil)
393
+ pattern ||= 'xxxx-xxxx'
394
+ 20.times do
395
+ dn = BigbluebuttonRails::DialNumber.randomize(pattern)
396
+ if BigbluebuttonRoom.where(dial_number: dn).empty?
397
+ return self.update_attributes(dial_number: dn)
398
+ end
399
+ end
400
+ nil
401
+ end
402
+
368
403
  protected
369
404
 
370
405
  def create_room_options
@@ -377,25 +412,44 @@ class BigbluebuttonRoom < ActiveRecord::Base
377
412
  # but not yet ended.
378
413
  # Any action that requires a server should call 'require_server' before
379
414
  # anything else.
380
- def require_server
415
+ # This method will automatically select a server if the room has no server
416
+ # set on it yet.
417
+ def require_server(api_method=nil)
418
+ selected_server = select_server(api_method)
419
+ unless selected_server.nil?
420
+ self.server = selected_server
421
+ self.save if selected_server && !self.new_record?
422
+ end
381
423
  if self.server.nil?
382
- msg = I18n.t('bigbluebutton_rails.rooms.errors.server.not_set')
424
+ msg = I18n.t('bigbluebutton_rails.rooms.errors.server.nil')
383
425
  raise BigbluebuttonRails::ServerRequired.new(msg)
384
426
  end
385
427
  end
386
428
 
429
+ # Selects the server with less rooms in it in case this room has no server
430
+ # set to it yet. Returns nil if there are no servers to select or if the
431
+ # room already has a server associated to it.
432
+ #
387
433
  # This method can be overridden to change the way the server is selected
388
- # before a room is created
389
- # This one selects the server with less rooms in it
390
- def select_server
391
- BigbluebuttonServer.
392
- select("bigbluebutton_servers.*, count(bigbluebutton_rooms.id) as room_count").
393
- joins(:rooms).group(:server_id).order("room_count ASC").first
434
+ # before a room is used. `api_method` contains the API method that is being
435
+ # called. Any server returned here will be used. If no server is returned, the
436
+ # room will continue with its current server (if any).
437
+ # One good example is to always select a new server when a meeting is being
438
+ # created (in case `api_method` is `:create`), making this a simple load
439
+ # balancing tool that can work well in simple cases.
440
+ def select_server(api_method=nil)
441
+ if self.server.nil?
442
+ BigbluebuttonServer.
443
+ select("bigbluebutton_servers.*, count(bigbluebutton_rooms.id) as room_count").
444
+ joins("LEFT JOIN bigbluebutton_rooms ON bigbluebutton_servers.id = bigbluebutton_rooms.server_id").
445
+ group(:server_id).order("room_count ASC").first
446
+ else
447
+ nil
448
+ end
394
449
  end
395
450
 
396
451
  def init
397
452
  self[:meetingid] ||= unique_meetingid
398
- self[:voice_bridge] ||= random_voice_bridge
399
453
 
400
454
  @request_headers = {}
401
455
 
@@ -409,29 +463,27 @@ class BigbluebuttonRoom < ActiveRecord::Base
409
463
  @attendees = []
410
464
  end
411
465
 
412
- def random_voice_bridge
413
- value = (70000 + SecureRandom.random_number(9999)).to_s
414
- count = 1
415
- while not BigbluebuttonRoom.find_by_voice_bridge(value).nil? and count < 10
416
- count += 1
417
- value = (70000 + SecureRandom.random_number(9999)).to_s
418
- end
419
- value
420
- end
421
-
422
466
  def internal_create_meeting(user=nil, user_opts={})
423
467
  opts = {
424
- :record => self.record,
468
+ :record => self.record_meeting,
425
469
  :duration => self.duration,
426
- :moderatorPW => self.moderator_password,
427
- :attendeePW => self.attendee_password,
470
+ :moderatorPW => self.moderator_api_password,
471
+ :attendeePW => self.attendee_api_password,
428
472
  :welcome => self.welcome_msg.blank? ? default_welcome_message : self.welcome_msg,
429
473
  :dialNumber => self.dial_number,
430
474
  :logoutURL => self.full_logout_url || self.logout_url,
431
475
  :maxParticipants => self.max_participants,
432
- :voiceBridge => self.voice_bridge
476
+ :moderatorOnlyMessage => self.moderator_only_message,
477
+ :autoStartRecording => self.auto_start_recording,
478
+ :allowStartStopRecording => self.allow_start_stop_recording
433
479
  }.merge(user_opts)
434
480
 
481
+ # Set the voice bridge only if the gem is configured to do so and the voice bridge
482
+ # is not blank.
483
+ if BigbluebuttonRails.use_local_voice_bridges && !self.voice_bridge.blank?
484
+ opts.merge!({ :voiceBridge => self.voice_bridge })
485
+ end
486
+
435
487
  opts.merge!(self.get_metadata_for_create)
436
488
 
437
489
  # Add information about the user that is creating the meeting (if any)
@@ -442,6 +494,12 @@ class BigbluebuttonRoom < ActiveRecord::Base
442
494
  opts.merge!({ "meta_#{BigbluebuttonRails.metadata_user_name}" => username })
443
495
  end
444
496
 
497
+ # Add the invitation URL, if any
498
+ invitation_url = self.try(BigbluebuttonRails.invitation_url_method)
499
+ unless invitation_url.nil?
500
+ opts.merge!({ "meta_#{BigbluebuttonRails.metadata_invitation_url}" => invitation_url })
501
+ end
502
+
445
503
  self.server.api.request_headers = @request_headers # we need the client's IP
446
504
  response = self.server.api.create_meeting(self.name, self.meetingid, opts)
447
505
 
@@ -455,8 +513,11 @@ class BigbluebuttonRoom < ActiveRecord::Base
455
513
  # there's no message set in this room.
456
514
  # Can be used to easily set a default message format for all rooms.
457
515
  def default_welcome_message
458
- I18n.t('bigbluebutton_rails.rooms.default_welcome_msg',
459
- :name => self.name, :voice_number => self.voice_bridge)
516
+ if self.dial_number.present?
517
+ I18n.t('bigbluebutton_rails.rooms.default_welcome_msg_dial_number')
518
+ else
519
+ I18n.t('bigbluebutton_rails.rooms.default_welcome_msg')
520
+ end
460
521
  end
461
522
 
462
523
  # if :param wasn't set, sets it as :name downcase and parameterized
@@ -466,14 +527,14 @@ class BigbluebuttonRoom < ActiveRecord::Base
466
527
  end
467
528
  end
468
529
 
469
- # When setting a room as private we generate passwords in case they don't exist.
470
- def set_passwords
530
+ # When setting a room as private we generate keys in case they don't exist.
531
+ def set_keys
471
532
  if self.private_changed? and self.private
472
- if self.moderator_password.blank?
473
- self.moderator_password = SecureRandom.hex(4)
533
+ if self.moderator_key.blank?
534
+ self.moderator_key = SecureRandom.hex(4)
474
535
  end
475
- if self.attendee_password.blank?
476
- self.attendee_password = SecureRandom.hex(4)
536
+ if self.attendee_key.blank?
537
+ self.attendee_key = SecureRandom.hex(4)
477
538
  end
478
539
  end
479
540
  end
@@ -484,4 +545,20 @@ class BigbluebuttonRoom < ActiveRecord::Base
484
545
  }
485
546
  end
486
547
 
548
+ private
549
+
550
+ def internal_password
551
+ SecureRandom.uuid
552
+ end
553
+
554
+ def map_key_to_internal_password(key)
555
+ if key == self.attendee_key
556
+ self.attendee_api_password
557
+ elsif key == self.moderator_key
558
+ self.moderator_api_password
559
+ else
560
+ nil
561
+ end
562
+ end
563
+
487
564
  end