Class: BigbluebuttonRoom
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- BigbluebuttonRoom
- Includes:
- ActiveModel::ForbiddenAttributesProtection
- Defined in:
- app/models/bigbluebutton_room.rb
Instance Attribute Summary collapse
-
#current_attendees ⇒ Object
Note: these params need to be fetched from the server before being accessed.
-
#end_time ⇒ Object
Note: these params need to be fetched from the server before being accessed.
-
#full_logout_url ⇒ Object
the full logout_url used when logout_url is a relative path.
-
#has_been_forcibly_ended ⇒ Object
Note: these params need to be fetched from the server before being accessed.
-
#moderator_count ⇒ Object
Note: these params need to be fetched from the server before being accessed.
-
#participant_count ⇒ Object
Note: these params need to be fetched from the server before being accessed.
-
#request_headers ⇒ Object
HTTP headers that will be passed to the BigBlueButtonApi object to send in all GET/POST requests to a webconf server.
-
#running ⇒ Object
Note: these params need to be fetched from the server before being accessed.
Class Method Summary collapse
Instance Method Summary collapse
-
#add_domain_to_logout_url(protocol, host) ⇒ Object
add a domain name and/or protocol to the logout_url if needed it doesn’t save in the db, just updates the instance.
-
#attr_equal?(o) ⇒ Boolean
A more complete equal? method, comparing also the attibutes and the instance variables.
- #available_layouts ⇒ Object
- #available_layouts_for_select ⇒ Object
- #available_layouts_names ⇒ Object
-
#create_meeting(user = nil, request = nil) ⇒ Object
The create logic.
- #create_meeting_record(response, server, user, user_opts) ⇒ Object
-
#fetch_is_running? ⇒ Boolean
Fetches the BBB server to see if the meeting is running.
-
#fetch_meeting_info ⇒ Object
Fetches info from BBB about this room.
-
#fetch_new_token ⇒ Object
Gets a ‘configToken’ to use when joining the room.
- #fetch_recordings(filter = {}) ⇒ Object
-
#finish_meetings ⇒ Object
Sets all meetings related to this room as not running.
-
#generate_dial_number!(pattern = nil) ⇒ Object
Generates a new dial number following ‘pattern` and saves it in the room, returning the results of `update_attributes`.
-
#get_current_meeting ⇒ Object
Returns the current meeting running on this room, if any.
-
#instance_variables_compare(o) ⇒ Object
Compare the instance variables of two models to define if they are equal Returns a hash with the variables with different values or an empty hash if they are have all equal values.
-
#is_running? ⇒ Boolean
Convenience method to access the attribute
running
. -
#join_url(username, role, key = nil, options = {}) ⇒ Object
Returns the URL to join this room.
- #parameterized_join_url(username, role, id, options = {}, user = nil) ⇒ Object
-
#room_options_with_initialize ⇒ Object
In case there’s no room_options created yet, build one (happens usually when an old database is migrated).
- #select_server(api_method = nil) ⇒ Object
-
#send_create(user = nil) ⇒ Object
Sends a call to the BBB server to create the meeting.
-
#send_end ⇒ Object
Sends a call to the BBB server to end the meeting.
-
#short_path ⇒ Object
Short URL for this room.
- #to_param ⇒ Object
- #unique_meetingid ⇒ Object
-
#update_current_meeting_record(metadata = nil, force_not_ended = false) ⇒ Object
Updates the current meeting associated with this room.
-
#user_role(params) ⇒ Object
Returns the role of the user based on the key given.
Instance Attribute Details
#current_attendees ⇒ Object
Note: these params need to be fetched from the server before being accessed
66 67 68 |
# File 'app/models/bigbluebutton_room.rb', line 66 def current_attendees @current_attendees end |
#end_time ⇒ Object
Note: these params need to be fetched from the server before being accessed
66 67 68 |
# File 'app/models/bigbluebutton_room.rb', line 66 def end_time @end_time end |
#full_logout_url ⇒ Object
the full logout_url used when logout_url is a relative path
75 76 77 |
# File 'app/models/bigbluebutton_room.rb', line 75 def full_logout_url @full_logout_url end |
#has_been_forcibly_ended ⇒ Object
Note: these params need to be fetched from the server before being accessed
66 67 68 |
# File 'app/models/bigbluebutton_room.rb', line 66 def has_been_forcibly_ended @has_been_forcibly_ended end |
#moderator_count ⇒ Object
Note: these params need to be fetched from the server before being accessed
66 67 68 |
# File 'app/models/bigbluebutton_room.rb', line 66 def moderator_count @moderator_count end |
#participant_count ⇒ Object
Note: these params need to be fetched from the server before being accessed
66 67 68 |
# File 'app/models/bigbluebutton_room.rb', line 66 def participant_count @participant_count end |
#request_headers ⇒ Object
HTTP headers that will be passed to the BigBlueButtonApi object to send in all GET/POST requests to a webconf server. Currently used to send the client’s IP to the load balancer.
80 81 82 |
# File 'app/models/bigbluebutton_room.rb', line 80 def request_headers @request_headers end |
#running ⇒ Object
Note: these params need to be fetched from the server before being accessed
66 67 68 |
# File 'app/models/bigbluebutton_room.rb', line 66 def running @running end |
Class Method Details
.generate_dial_number(pattern = nil) ⇒ Object
552 553 554 555 556 557 558 559 560 561 |
# File 'app/models/bigbluebutton_room.rb', line 552 def self.generate_dial_number(pattern=nil) unless pattern.nil? unless BigbluebuttonRoom.maximum(:dial_number).nil? return BigbluebuttonRoom.maximum(:dial_number).next else return pattern.gsub('x', '0') end nil end end |
Instance Method Details
#add_domain_to_logout_url(protocol, host) ⇒ Object
add a domain name and/or protocol to the logout_url if needed it doesn’t save in the db, just updates the instance
350 351 352 353 354 355 356 357 358 359 360 361 |
# File 'app/models/bigbluebutton_room.rb', line 350 def add_domain_to_logout_url(protocol, host) unless logout_url.nil? url = logout_url.downcase unless url.nil? or url =~ /^[a-z]+:\/\// # matches the protocol unless url =~ /^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*/ # matches the host domain url = host + url end url = protocol + url end self.full_logout_url = url.downcase end end |
#attr_equal?(o) ⇒ Boolean
A more complete equal? method, comparing also the attibutes and the instance variables
320 321 322 323 324 |
# File 'app/models/bigbluebutton_room.rb', line 320 def attr_equal?(o) self == o and self.instance_variables_compare(o).empty? and self.attributes == o.attributes end |
#available_layouts ⇒ Object
524 525 526 527 |
# File 'app/models/bigbluebutton_room.rb', line 524 def available_layouts server = BigbluebuttonRails.configuration.select_server.call(self) server.present? ? server.available_layouts : [] end |
#available_layouts_for_select ⇒ Object
534 535 536 537 |
# File 'app/models/bigbluebutton_room.rb', line 534 def available_layouts_for_select server = BigbluebuttonRails.configuration.select_server.call(self) server.present? ? server.available_layouts_for_select : [] end |
#available_layouts_names ⇒ Object
529 530 531 532 |
# File 'app/models/bigbluebutton_room.rb', line 529 def available_layouts_names server = BigbluebuttonRails.configuration.select_server.call(self) server.present? ? server.available_layouts_names : [] end |
#create_meeting(user = nil, request = nil) ⇒ Object
The create logic. Will create the meeting in this room unless it is already running. Returns true if the meeting was created.
333 334 335 336 337 338 339 340 341 342 343 344 345 346 |
# File 'app/models/bigbluebutton_room.rb', line 333 def create_meeting(user=nil, request=nil) fetch_is_running? unless is_running? # in case the meeting is not running but it's still in memory suppress(BigBlueButton::BigBlueButtonException) { send_end } add_domain_to_logout_url(request.protocol, request.host_with_port) unless request.nil? send_create(user) true else false end end |
#create_meeting_record(response, server, user, user_opts) ⇒ Object
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 |
# File 'app/models/bigbluebutton_room.rb', line 405 def create_meeting_record(response, server, user, user_opts) unless get_current_meeting.present? if self.create_time.present? # to make sure there's no other meeting related to this room that # has not yet been set as ended self.finish_meetings attrs = { room: self, server_url: server.url, server_secret: server.secret, meetingid: self.meetingid, name: self.name, title: self.name, recorded: self.record_meeting, create_time: self.create_time, running: self.running, ended: false } = response[:metadata] unless .nil? begin attrs[:creator_id] = [BigbluebuttonRails.configuration.].to_i attrs[:creator_name] = [BigbluebuttonRails.configuration.] rescue attrs[:creator_id] = nil attrs[:creator_name] = nil end end # the parameters the user might have overwritten in the create call # need to be mapped to the name of the attrs in BigbluebuttMeeting # note: recorded is not in the API response, so we can't just get these # attributes from there attrs_user = { meetingid: user_opts[:meetingID], name: user_opts[:name], recorded: user_opts[:record], creator_id: user_opts[:creator_id], creator_name: user_opts[:creator_name] }.delete_if { |k, v| v.nil? } attrs.merge!(attrs_user) BigbluebuttonMeeting.create(attrs) Rails.logger.error "Did not create a current meeting because there was no create_time on room #{self.meetingid}" else Rails.logger.error "Did not create a current meeting because there was no create_time on room #{self.meetingid}" end end end |
#fetch_is_running? ⇒ Boolean
Fetches the BBB server to see if the meeting is running. Sets running
Triggers API call: isMeetingRunning
.
177 178 179 180 |
# File 'app/models/bigbluebutton_room.rb', line 177 def fetch_is_running? server = BigbluebuttonRails.configuration.select_server.call(self, :is_meeting_running) @running = server.api.is_meeting_running?(self.meetingid) end |
#fetch_meeting_info ⇒ Object
Fetches info from BBB about this room. The response is parsed and stored in the model. You can access it using attributes such as:
room.participant_count
room.current_attendees[0].user_name
The attributes changed are:
-
participant_count
-
moderator_count
-
running
-
has_been_forcibly_ended
-
create_time
-
end_time
-
current_attendees
(array ofBigbluebuttonAttendee
)
Triggers API call: getMeetingInfo
.
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'app/models/bigbluebutton_room.rb', line 137 def fetch_meeting_info begin server = BigbluebuttonRails.configuration.select_server.call(self, :get_meeting_info) response = server.api.get_meeting_info(self.meetingid, self.moderator_api_password) @participant_count = response[:participantCount] @moderator_count = response[:moderatorCount] @running = response[:running] @has_been_forcibly_ended = response[:hasBeenForciblyEnded] @end_time = response[:endTime] @current_attendees = [] if response[:attendees].present? response[:attendees].each do |att| attendee = BigbluebuttonAttendee.new attendee.from_hash(att) @current_attendees << attendee end end # a 'shortcut' to update meetings since we have all information we need # if we got here, it means the meeting is still in the server, so it's not ended self.update_attributes(create_time: response[:createTime]) unless self.new_record? self.update_current_meeting_record(response[:metadata], true) rescue BigBlueButton::BigBlueButtonException => e # note: we could catch only the 'notFound' error, but there are complications, so # it's better to end the meeting prematurely and open it again if needed than to # not end it at all (e.g. in case the server stops responding) Rails.logger.info "BigbluebuttonRoom: detected that a meeting ended in the room #{self.meetingid} after the error #{e.inspect}" self.update_attributes(create_time: nil) unless self.new_record? self.finish_meetings end response end |
#fetch_new_token ⇒ Object
Gets a ‘configToken’ to use when joining the room. Returns a string with the token generated or nil if there’s no need for a token (the options set in the room are the default options or there are no options set in the room) or if an error occurred.
The entire process consists in these steps:
-
Go to the server get the default config.xml;
-
Modify the config.xml based on the room options set in the room;
-
Go to the server set the new config.xml;
-
Get the token identifier and return it.
Triggers API call: getDefaultConfigXML
. Triggers API call: setConfigXML
.
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 |
# File 'app/models/bigbluebutton_room.rb', line 497 def fetch_new_token if self..is_modified? || block_given? server = BigbluebuttonRails.configuration.select_server.call(self, :set_config_xml) # get the default XML we will use to create a new one config_xml = server.api.get_default_config_xml if block_given? config_xml = yield(config_xml) else # set the options on the XML # returns true if something was changed config_xml = self..set_on_config_xml(config_xml) end if config_xml server.update_config(config_xml) # get the new token for the room, and return it server.api.set_config_xml(self.meetingid, config_xml) else nil end else nil end end |
#fetch_recordings(filter = {}) ⇒ Object
563 564 565 566 567 568 569 570 571 |
# File 'app/models/bigbluebutton_room.rb', line 563 def fetch_recordings(filter={}) server = BigbluebuttonRails.configuration.select_server.call(self, :get_recordings) if server.present? server.fetch_recordings(filter.merge({ meetingID: self.meetingid })) true else false end end |
#finish_meetings ⇒ Object
Sets all meetings related to this room as not running
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 |
# File 'app/models/bigbluebutton_room.rb', line 460 def finish_meetings to_be_finished = BigbluebuttonMeeting.where(ended: false, room_id: self.id).to_a now = DateTime.now.strftime('%Q').to_i BigbluebuttonMeeting.where(ended: false) .where(room_id: self.id) .update_all(running: false, ended: true, finish_time: now) # in case there are inconsistent meetings marked as running # but that already ended BigbluebuttonMeeting.where(running: true, ended: true) .where(room_id: self.id) .update_all(running: false, ended: true, finish_time: now) if to_be_finished.count > 0 # start trying to get the recording for this room # since we don't have a way to know exactly when a recording is done, we # have to keep polling the server for them # 3 times so it tries at: 4, 9, 14 and 19 # no point trying more since there is a global synchronization process Resque.enqueue_in(1.minutes, ::BigbluebuttonRecordingsForRoomWorker, self.id, 10) end end |
#generate_dial_number!(pattern = nil) ⇒ Object
Generates a new dial number following ‘pattern` and saves it in the room, returning the results of `update_attributes`. Will always generate a unique number. Tries several times if the number already exists and returns `nil` in case it wasn’t possible to generate a unique value.
543 544 545 546 547 548 549 550 |
# File 'app/models/bigbluebutton_room.rb', line 543 def generate_dial_number!(pattern=nil) unless pattern.nil? dn = self.class.generate_dial_number(pattern) return self.update_attributes(dial_number: dn) else nil end end |
#get_current_meeting ⇒ Object
Returns the current meeting running on this room, if any.
371 372 373 374 375 376 377 |
# File 'app/models/bigbluebutton_room.rb', line 371 def get_current_meeting unless self.create_time.nil? BigbluebuttonMeeting.find_by(room_id: self.id, create_time: self.create_time) else nil end end |
#instance_variables_compare(o) ⇒ Object
Compare the instance variables of two models to define if they are equal Returns a hash with the variables with different values or an empty hash if they are have all equal values. From: alicebobandmallory.com/articles/2009/11/02/comparing-instance-variables-in-ruby
310 311 312 313 314 315 316 |
# File 'app/models/bigbluebutton_room.rb', line 310 def instance_variables_compare(o) vars = [ :@running, :@participant_count, :@moderator_count, :@current_attendees, :@has_been_forcibly_ended, :@end_time ] Hash[*vars.map { |v| self.instance_variable_get(v)!=o.instance_variable_get(v) ? [v,o.instance_variable_get(v)] : []}.flatten] end |
#is_running? ⇒ Boolean
Convenience method to access the attribute running
117 118 119 |
# File 'app/models/bigbluebutton_room.rb', line 117 def is_running? @running end |
#join_url(username, role, key = nil, options = {}) ⇒ Object
Returns the URL to join this room.
- username
-
Name of the user
- role
-
Role of the user in this room. Can be
[:moderator, :attendee]
- key
-
Key to be use (in case role == nil)
- options
-
Additional options to use when generating the URL
Uses the API but does not require a request to the server.
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
# File 'app/models/bigbluebutton_room.rb', line 245 def join_url(username, role, key=nil, ={}) server = BigbluebuttonRails.configuration.select_server.call(self, :join_meeting_url) pass = case role when :moderator self.moderator_api_password when :attendee self.attendee_api_password when :guest if BigbluebuttonRails.configuration.guest_support = { guest: true }.merge() end self.attendee_api_password else map_key_to_internal_password(key) end r = server.api.join_meeting_url(self.meetingid, username, pass, ) r.strip! unless r.nil? r end |
#parameterized_join_url(username, role, id, options = {}, user = nil) ⇒ Object
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'app/models/bigbluebutton_room.rb', line 267 def parameterized_join_url(username, role, id, ={}, user=nil) opts = .clone # gets the token with the configurations for this user/room if opts[:configToken].blank? token = self.fetch_new_token opts.merge!({ configToken: token }) unless token.blank? end # set the create time and the user id, if they exist opts.merge!({ createTime: self.create_time }) unless self.create_time.blank? || [:createTime].present? opts.merge!({ userID: id }) unless id.blank? || [:userID].present? # Get options passed by the application, if any user_opts = BigbluebuttonRails.configuration..call(self, user, { username: username, role: role }) user_opts = {} if user_opts.blank? opts.merge!(user_opts) self.join_url(username, role, nil, opts) end |
#room_options_with_initialize ⇒ Object
In case there’s no room_options created yet, build one (happens usually when an old database is migrated).
111 112 113 |
# File 'app/models/bigbluebutton_room.rb', line 111 def || end |
#select_server(api_method = nil) ⇒ Object
573 574 575 576 577 578 579 580 |
# File 'app/models/bigbluebutton_room.rb', line 573 def select_server(api_method=nil) server = BigbluebuttonServer.first if server.nil? msg = I18n.t('bigbluebutton_rails.rooms.errors.server.nil') raise BigbluebuttonRails::ServerRequired.new(msg) end server end |
#send_create(user = nil) ⇒ Object
Sends a call to the BBB server to create the meeting. ‘user’ is the object that represents the user that is creating the meeting. ‘user_opts’ is a hash of parameters to override the parameters sent in the create
request. Can be passed by the application to enforce some values over the values
that are taken from the database.
With the response, updates the following attributes:
-
attendee_api_password
-
moderator_api_password
Triggers API call: create
.
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
# File 'app/models/bigbluebutton_room.rb', line 206 def send_create(user=nil) self.meetingid = unique_meetingid() if self.meetingid.blank? self.moderator_api_password = internal_password() if self.moderator_api_password.blank? self.attendee_api_password = internal_password() if self.attendee_api_password.blank? self.save unless self.new_record? # Get the user options to use when creating the meeting user_opts = BigbluebuttonRails.configuration..call(self, user) user_opts = {} if user_opts.blank? server, response = internal_create_meeting(user, user_opts) unless response.nil? self.attendee_api_password = response[:attendeePW] self.moderator_api_password = response[:moderatorPW] self.create_time = response[:createTime] self.voice_bridge = response[:voiceBridge] if response.has_key?(:voiceBridge) unless self.new_record? self.save # creates the meeting object since the create was successful create_meeting_record(response, server, user, user_opts) # enqueue an update in the meeting with a small delay we assume to be # enough for the user to fully join the meeting Resque.enqueue(::BigbluebuttonMeetingUpdaterWorker, self.id, 10.seconds) end end response end |
#send_end ⇒ Object
Sends a call to the BBB server to end the meeting.
Triggers API call: end
.
185 186 187 188 189 190 191 192 193 |
# File 'app/models/bigbluebutton_room.rb', line 185 def send_end server = BigbluebuttonRails.configuration.select_server.call(self, :end) response = server.api.end_meeting(self.meetingid, self.moderator_api_password) # enqueue an update in the meeting to end it faster Resque.enqueue(::BigbluebuttonMeetingUpdaterWorker, self.id) response end |
#short_path ⇒ Object
Short URL for this room. Can be overwritten by applications that want to use a different route.
584 585 586 |
# File 'app/models/bigbluebutton_room.rb', line 584 def short_path Rails.application.routes.url_helpers.(self) end |
#to_param ⇒ Object
326 327 328 |
# File 'app/models/bigbluebutton_room.rb', line 326 def to_param self.slug end |
#unique_meetingid ⇒ Object
363 364 365 366 367 368 |
# File 'app/models/bigbluebutton_room.rb', line 363 def unique_meetingid # GUID # Has to be globally unique in case more that one bigbluebutton_rails application is using # the same web conference server. "#{SecureRandom.uuid}-#{Time.now.to_i}" end |
#update_current_meeting_record(metadata = nil, force_not_ended = false) ⇒ Object
Updates the current meeting associated with this room
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 |
# File 'app/models/bigbluebutton_room.rb', line 380 def update_current_meeting_record(=nil, force_not_ended=false) unless self.create_time.nil? attrs = { :running => self.running, :create_time => self.create_time } # note: it's important to update the 'ended' attr so the meeting is # reopened in case it was mistakenly considered as ended attrs[:ended] = false if force_not_ended unless .nil? begin attrs[:creator_id] = [BigbluebuttonRails.configuration.].to_i attrs[:creator_name] = [BigbluebuttonRails.configuration.] rescue attrs[:creator_id] = nil attrs[:creator_name] = nil end end meeting = self.get_current_meeting meeting.update_attributes(attrs) if meeting.present? end end |
#user_role(params) ⇒ Object
Returns the role of the user based on the key given. The return value can be :moderator
, :attendee
, or nil if the key given does not match any of the room keys.
- params
-
Hash with a key :key
292 293 294 295 296 297 298 299 300 301 302 303 304 |
# File 'app/models/bigbluebutton_room.rb', line 292 def user_role(params) role = nil key = params.is_a?(String) ? params : (params && params.has_key?(:key) ? params[:key] : nil) unless key.blank? if self.moderator_key == key role = :moderator elsif self.attendee_key == key role = :attendee end end role end |