bigbluebutton_rails 1.2.0 → 1.3.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. data/.ruby-version +1 -0
  2. data/.travis.yml +2 -2
  3. data/CHANGELOG.rdoc +15 -1
  4. data/Gemfile +15 -4
  5. data/Gemfile.lock +187 -125
  6. data/LICENSE +24 -0
  7. data/README.rdoc +96 -19
  8. data/Rakefile +32 -16
  9. data/TODO_08 +9 -10
  10. data/{spec/rails_app/public → app/assets}/stylesheets/bigbluebutton_rails.css +58 -79
  11. data/app/controllers/bigbluebutton/recordings_controller.rb +142 -0
  12. data/app/controllers/bigbluebutton/rooms_controller.rb +105 -50
  13. data/app/controllers/bigbluebutton/servers_controller.rb +78 -2
  14. data/app/helpers/bigbluebutton_rails_helper.rb +11 -4
  15. data/app/models/bigbluebutton_metadata.rb +43 -0
  16. data/app/models/bigbluebutton_playback_format.rb +9 -0
  17. data/app/models/bigbluebutton_recording.rb +191 -0
  18. data/app/models/bigbluebutton_room.rb +92 -66
  19. data/app/models/bigbluebutton_server.rb +49 -3
  20. data/app/views/bigbluebutton/recordings/_form.html.erb +67 -0
  21. data/app/views/bigbluebutton/recordings/_recordings.html.erb +56 -0
  22. data/app/views/bigbluebutton/recordings/edit.html.erb +5 -0
  23. data/app/views/bigbluebutton/recordings/index.html.erb +2 -0
  24. data/app/views/bigbluebutton/recordings/show.html.erb +84 -0
  25. data/app/views/bigbluebutton/rooms/_form.html.erb +28 -7
  26. data/app/views/bigbluebutton/rooms/_rooms.html.erb +19 -4
  27. data/app/views/bigbluebutton/rooms/edit.html.erb +0 -1
  28. data/app/views/bigbluebutton/rooms/invite.html.erb +2 -1
  29. data/app/views/bigbluebutton/rooms/join.html.erb +0 -2
  30. data/app/views/bigbluebutton/rooms/recordings.html.erb +2 -0
  31. data/app/views/bigbluebutton/rooms/show.html.erb +35 -15
  32. data/app/views/bigbluebutton/servers/_form.html.erb +4 -0
  33. data/app/views/bigbluebutton/servers/index.html.erb +8 -3
  34. data/app/views/bigbluebutton/servers/recordings.html.erb +2 -0
  35. data/app/views/bigbluebutton/servers/rooms.html.erb +1 -1
  36. data/app/views/bigbluebutton/servers/show.html.erb +54 -6
  37. data/bigbluebutton_rails.gemspec +1 -1
  38. data/config/locales/en.yml +105 -55
  39. data/config/schedule.rb +3 -0
  40. data/lib/bigbluebutton_rails.rb +52 -2
  41. data/lib/bigbluebutton_rails/controller_methods.rb +31 -0
  42. data/lib/bigbluebutton_rails/rails/routes.rb +50 -14
  43. data/lib/bigbluebutton_rails/version.rb +1 -1
  44. data/lib/generators/bigbluebutton_rails/install_generator.rb +8 -8
  45. data/lib/generators/bigbluebutton_rails/templates/migration.rb +37 -1
  46. data/lib/generators/bigbluebutton_rails/templates/migration_1_3_0.rb +55 -0
  47. data/lib/generators/bigbluebutton_rails/templates/migration_1_3_0_b.rb +13 -0
  48. data/lib/generators/bigbluebutton_rails/views_generator.rb +6 -2
  49. data/lib/tasks/bigbluebutton_rails/recordings.rake +18 -0
  50. data/spec/bigbluebutton_rails_spec.rb +2 -2
  51. data/spec/controllers/bigbluebutton/recordings_controller_json_responses_spec.rb +111 -0
  52. data/spec/controllers/bigbluebutton/recordings_controller_spec.rb +188 -0
  53. data/spec/controllers/bigbluebutton/rooms_controller_exception_handling_spec.rb +15 -10
  54. data/spec/controllers/bigbluebutton/rooms_controller_json_responses_spec.rb +14 -7
  55. data/spec/controllers/bigbluebutton/rooms_controller_spec.rb +327 -103
  56. data/spec/controllers/bigbluebutton/servers_controller_json_responses_spec.rb +12 -12
  57. data/spec/controllers/bigbluebutton/servers_controller_spec.rb +166 -28
  58. data/spec/factories/bigbluebutton_metadata.rb +11 -0
  59. data/spec/factories/bigbluebutton_playback_format.rb +8 -0
  60. data/spec/factories/bigbluebutton_recording.rb +13 -0
  61. data/spec/factories/bigbluebutton_room.rb +2 -1
  62. data/spec/factories/bigbluebutton_server.rb +1 -1
  63. data/spec/factories/user.rb +1 -0
  64. data/spec/generators/install_generator_spec.rb +21 -10
  65. data/spec/generators/views_generator_spec.rb +14 -4
  66. data/spec/models/bigbluebutton_metadata_db_spec.rb +13 -0
  67. data/spec/models/bigbluebutton_metadata_spec.rb +64 -0
  68. data/spec/models/bigbluebutton_playback_format_db_spec.rb +13 -0
  69. data/spec/models/bigbluebutton_playback_format_spec.rb +22 -0
  70. data/spec/models/bigbluebutton_recording_db_spec.rb +24 -0
  71. data/spec/models/bigbluebutton_recording_spec.rb +450 -0
  72. data/spec/models/bigbluebutton_room_db_spec.rb +3 -2
  73. data/spec/models/bigbluebutton_room_spec.rb +194 -166
  74. data/spec/models/bigbluebutton_server_spec.rb +107 -27
  75. data/spec/rails_app/.gitignore +2 -0
  76. data/spec/rails_app/app/assets/javascripts/application.js +2 -0
  77. data/spec/rails_app/app/controllers/application_controller.rb +2 -0
  78. data/spec/rails_app/app/controllers/custom_recordings_controller.rb +8 -0
  79. data/spec/rails_app/app/controllers/frontpage_controller.rb +7 -0
  80. data/spec/rails_app/app/views/frontpage/show.html.erb +5 -0
  81. data/spec/rails_app/app/views/layouts/application.html.erb +5 -2
  82. data/spec/rails_app/config/application.rb +13 -0
  83. data/spec/rails_app/config/environments/development.rb +7 -2
  84. data/spec/rails_app/config/environments/production.rb +9 -0
  85. data/spec/rails_app/config/environments/test.rb +10 -0
  86. data/spec/rails_app/config/routes.rb +20 -2
  87. data/spec/rails_app/features/edit_rooms.feature +2 -3
  88. data/spec/rails_app/features/edit_servers.feature +2 -3
  89. data/spec/rails_app/features/step_definitions/activity_monitor_servers_step.rb +2 -2
  90. data/spec/rails_app/features/step_definitions/common_steps.rb +5 -5
  91. data/spec/rails_app/features/step_definitions/create_rooms_steps.rb +2 -4
  92. data/spec/rails_app/features/step_definitions/create_servers_steps.rb +2 -2
  93. data/spec/rails_app/features/step_definitions/edit_rooms_steps.rb +2 -2
  94. data/spec/rails_app/features/step_definitions/edit_servers_steps.rb +2 -2
  95. data/spec/rails_app/features/step_definitions/join_rooms_steps.rb +2 -2
  96. data/spec/rails_app/features/step_definitions/list_and_show_rooms_steps.rb +2 -2
  97. data/spec/rails_app/features/support/templates.rb +2 -6
  98. data/spec/rails_app/lib/tasks/db/populate.rake +119 -0
  99. data/spec/routing/bigbluebutton/custom_controllers_routing_spec.rb +78 -1
  100. data/spec/routing/bigbluebutton/recordings_only_routing_spec.rb +43 -0
  101. data/spec/routing/bigbluebutton/recordings_routing_spec.rb +46 -0
  102. data/spec/routing/bigbluebutton/rooms_only_routing_spec.rb +67 -0
  103. data/spec/routing/bigbluebutton/rooms_routing_spec.rb +28 -2
  104. data/spec/routing/bigbluebutton/servers_only_routing_spec.rb +43 -0
  105. data/spec/routing/bigbluebutton/servers_routing_spec.rb +16 -1
  106. data/spec/support/matchers/have_same_attributes_as.rb +20 -3
  107. data/spec/support/matchers/shoulda/{be_boolean.rb → be_boolean_matcher.rb} +0 -0
  108. data/spec/support/mocked_server.rb +19 -3
  109. metadata +48 -28
  110. data/public/images/loading.gif +0 -0
  111. data/public/javascripts/jquery.min.js +0 -16
  112. data/public/stylesheets/bigbluebutton_rails.css +0 -148
  113. data/spec/rails_app/public/images/loading.gif +0 -0
  114. data/spec/rails_app/public/images/rails.png +0 -0
  115. data/spec/rails_app/public/index.html +0 -239
  116. data/spec/rails_app/public/javascripts/application.js +0 -2
  117. data/spec/rails_app/public/javascripts/controls.js +0 -965
  118. data/spec/rails_app/public/javascripts/dragdrop.js +0 -974
  119. data/spec/rails_app/public/javascripts/effects.js +0 -1123
  120. data/spec/rails_app/public/javascripts/jquery.min.js +0 -16
  121. data/spec/rails_app/public/javascripts/prototype.js +0 -6001
  122. data/spec/rails_app/public/javascripts/rails.js +0 -191
  123. data/spec/support/shared_examples/rooms_controller.rb +0 -37
@@ -1,17 +1,24 @@
1
1
  module BigbluebuttonRailsHelper
2
2
 
3
- # Returns the URL for a QR-code image usign Google Charts API
3
+ # Returns the URL for a QR-code image using Google Charts API
4
4
  def qrcode_url(content, size=nil)
5
5
  size ||= "200x200"
6
6
  content = CGI::escape(content)
7
7
  "https://chart.googleapis.com/chart?cht=qr&chs=#{size}&chl=#{content}&choe=UTF-8"
8
8
  end
9
9
 
10
- # TODO: improve it, showing all flashes
11
10
  def bbb_rails_error_explanation
12
- if flash.has_key?(:error) and !flash[:error].blank?
13
- content_tag(:div, flash[:error], { :id => "error_explanation" })
11
+ msgs = ""
12
+ flash.each do |key, msg|
13
+ msgs += content_tag(:div, msg, { :id => "error_explanation", :class => key })
14
14
  end
15
+ msgs.html_safe
16
+ end
17
+
18
+ # Setup a BigbluebuttonRoom to show in the forms
19
+ def setup_bigbluebutton_room(room)
20
+ (room.metadata.count..10).each { room.metadata.build }
21
+ room
15
22
  end
16
23
 
17
24
  end
@@ -0,0 +1,43 @@
1
+ class BigbluebuttonMetadata < ActiveRecord::Base
2
+ belongs_to :owner, :polymorphic => true, :inverse_of => :metadata
3
+
4
+ validates :owner_id, :presence => true
5
+ validates :owner_type, :presence => true
6
+
7
+ validates :name, :presence => true
8
+ validates :name, :format => {
9
+ :with => /^[a-zA-Z]+[a-zA-Z\d-]*$/,
10
+ :message => I18n.t('bigbluebutton_rails.metadata.errors.name_format')
11
+ }
12
+ validates :name,
13
+ :uniqueness => { :scope => [:owner_id, :owner_type] }
14
+ validate :exclusion_of_name_in_reserved_metadata_keys
15
+
16
+ attr_accessible :name, :content
17
+
18
+ # metadata keys are only invalid when the metadata belongs to a room,
19
+ # metadata that will be used in a 'create' call
20
+ # TODO: a better solution for rails >= 3.1
21
+ # validates :name, :exclusion => {
22
+ # :in => lambda do |m|
23
+ # if m.owner_type == "BigbluebuttonRoom"
24
+ # BigbluebuttonRails.metadata_invalid_keys.map(&:to_s)
25
+ # else
26
+ # []
27
+ # end
28
+ # end
29
+ # }
30
+ def exclusion_of_name_in_reserved_metadata_keys
31
+ keys = if owner_type == "BigbluebuttonRoom"
32
+ BigbluebuttonRails.metadata_invalid_keys.map(&:to_s)
33
+ else
34
+ []
35
+ end
36
+ if keys.include?(name)
37
+ # use the same message rails would generate for :exclusion => :in
38
+ msg = self.errors.generate_message(:name, :exclusion, {})
39
+ errors.add(:name, msg)
40
+ end
41
+ end
42
+
43
+ end
@@ -0,0 +1,9 @@
1
+ class BigbluebuttonPlaybackFormat < ActiveRecord::Base
2
+ belongs_to :recording, :class_name => 'BigbluebuttonRecording'
3
+
4
+ validates :recording_id, :presence => true
5
+
6
+ validates :format_type, :presence => true
7
+
8
+ attr_accessible :recording_id, :format_type, :url, :length
9
+ end
@@ -0,0 +1,191 @@
1
+ class BigbluebuttonRecording < ActiveRecord::Base
2
+ belongs_to :server, :class_name => 'BigbluebuttonServer'
3
+ belongs_to :room, :class_name => 'BigbluebuttonRoom'
4
+
5
+ validates :server, :presence => true
6
+
7
+ validates :recordid,
8
+ :presence => true,
9
+ :uniqueness => true
10
+
11
+ attr_accessible :recordid, :meetingid, :name, :published, :start_time,
12
+ :end_time, :available
13
+
14
+ has_many :metadata,
15
+ :class_name => 'BigbluebuttonMetadata',
16
+ :as => :owner,
17
+ :dependent => :destroy
18
+
19
+ has_many :playback_formats,
20
+ :class_name => 'BigbluebuttonPlaybackFormat',
21
+ :foreign_key => 'recording_id',
22
+ :dependent => :destroy
23
+
24
+ scope :published, where(:published => true)
25
+
26
+ def to_param
27
+ self.recordid
28
+ end
29
+
30
+ # Syncs the recordings in the db with the array of recordings in 'recordings',
31
+ # as received from BigBlueButtonApi#get_recordings.
32
+ # Will add new recordings that are not in the db yet and update the ones that
33
+ # already are (matching by 'recordid'). Will NOT delete recordings from the db
34
+ # if they are not in the array but instead mark them as unavailable.
35
+ # 'server' is the BigbluebuttonServer object from which the recordings
36
+ # were fetched.
37
+ #
38
+ # TODO: catch exceptions on creating/updating recordings
39
+ def self.sync(server, recordings)
40
+ recordings.each do |rec|
41
+ rec_obj = BigbluebuttonRecording.find_by_recordid(rec[:recordID])
42
+ rec_data = adapt_recording_hash(rec)
43
+ BigbluebuttonRecording.transaction do
44
+ if rec_obj
45
+ logger.info "Sync recordings: updating recording #{rec_obj.inspect}"
46
+ logger.debug "Sync recordings: recording data #{rec_data.inspect}"
47
+ self.update_recording(server, rec_obj, rec_data)
48
+ else
49
+ logger.info "Sync recordings: creating recording"
50
+ logger.debug "Sync recordings: recording data #{rec_data.inspect}"
51
+ self.create_recording(server, rec_data)
52
+ end
53
+ end
54
+ end
55
+
56
+ # set as unavailable the recordings that are not in 'recordings'
57
+ recordIDs = recordings.map{ |rec| rec[:recordID] }
58
+ if recordIDs.length <= 0 # empty response
59
+ BigbluebuttonRecording
60
+ .where(:available => true)
61
+ .update_all(:available => false)
62
+ else
63
+ BigbluebuttonRecording
64
+ .where(:available => true)
65
+ .where("recordid NOT IN (?)", recordIDs)
66
+ .update_all(:available => false)
67
+ end
68
+ end
69
+
70
+ protected
71
+
72
+ # Adapt keys in 'hash' from bigbluebutton-api-ruby's format to ours
73
+ def self.adapt_recording_hash(hash)
74
+ new_hash = hash.clone
75
+ mappings = {
76
+ :recordID => :recordid,
77
+ :meetingID => :meetingid,
78
+ :startTime => :start_time,
79
+ :endTime => :end_time
80
+ }
81
+ new_hash.keys.each { |k| new_hash[ mappings[k] ] = new_hash.delete(k) if mappings[k] }
82
+ new_hash
83
+ end
84
+
85
+ # Updates the BigbluebuttonRecording 'recording' with the data in the hash 'data'.
86
+ # The format expected for 'data' follows the format returned by
87
+ # BigBlueButtonApi#get_recordings but with the keys already converted to our format.
88
+ def self.update_recording(server, recording, data)
89
+ recording.server = server
90
+ recording.room = BigbluebuttonRails.match_room_recording(data)
91
+ recording.attributes = data.slice(:meetingid, :name, :published, :start_time, :end_time)
92
+ recording.available = true
93
+ recording.save!
94
+
95
+ sync_additional_data(recording, data)
96
+ end
97
+
98
+ # Creates a new BigbluebuttonRecording with the data from 'data'.
99
+ # The format expected for 'data' follows the format returned by
100
+ # BigBlueButtonApi#get_recordings but with the keys already converted to our format.
101
+ def self.create_recording(server, data)
102
+ filtered = data.slice(:recordid, :meetingid, :name, :published, :start_time, :end_time)
103
+ recording = BigbluebuttonRecording.create(filtered)
104
+ recording.available = true
105
+ recording.room = BigbluebuttonRails.match_room_recording(data)
106
+ recording.server = server
107
+ recording.save!
108
+
109
+ sync_additional_data(recording, data)
110
+ end
111
+
112
+ # Syncs data that's not directly stored in the recording itself but in
113
+ # associated models (e.g. metadata and playback formats).
114
+ # The format expected for 'data' follows the format returned by
115
+ # BigBlueButtonApi#get_recordings but with the keys already converted to our format.
116
+ def self.sync_additional_data(recording, data)
117
+ sync_metadata(recording, data[:metadata]) if data[:metadata]
118
+ if data[:playback] and data[:playback][:format]
119
+ sync_playback_formats(recording, data[:playback][:format])
120
+ end
121
+ end
122
+
123
+ # Syncs the metadata objects of 'recording' with the data in 'metadata'.
124
+ # The format expected for 'metadata' follows the format returned by
125
+ # BigBlueButtonApi#get_recordings but with the keys already converted to our format.
126
+ def self.sync_metadata(recording, metadata)
127
+ # keys are stored as strings in the db
128
+ received_metadata = metadata.clone.stringify_keys
129
+
130
+ query = { :owner_id => recording.id, :owner_type => recording.class.to_s }
131
+ BigbluebuttonMetadata.where(query).each do |meta_db|
132
+
133
+ # the metadata in the db is also in the received data, update it in the db
134
+ if received_metadata.has_key?(meta_db.name)
135
+ meta_db.update_attributes({ :content => received_metadata[meta_db.name] })
136
+ received_metadata.delete(meta_db.name)
137
+
138
+ # the metadata is not in the received data, remove from the db
139
+ else
140
+ meta_db.destroy
141
+ end
142
+ end
143
+
144
+ # for metadata that are not in the db yet
145
+ received_metadata.each do |name, content|
146
+ attrs = {
147
+ :name => name,
148
+ :content => content,
149
+ }
150
+ meta = BigbluebuttonMetadata.create(attrs)
151
+ meta.owner = recording
152
+ meta.save!
153
+ end
154
+ end
155
+
156
+ # Syncs the playback formats objects of 'recording' with the data in 'formats'.
157
+ # The format expected for 'formats' follows the format returned by
158
+ # BigBlueButtonApi#get_recordings but with the keys already converted to our format.
159
+ def self.sync_playback_formats(recording, formats)
160
+ formats_copy = formats.clone
161
+
162
+ # make it an array if it's a hash with a single format
163
+ formats_copy = [ formats_copy ] if formats_copy.is_a?(Hash)
164
+
165
+ BigbluebuttonPlaybackFormat.where(:recording_id => recording.id).each do |format_db|
166
+ format = formats_copy.select{ |d|
167
+ !d[:type].blank? and d[:type] == format_db.format_type
168
+ }.first
169
+
170
+ # the format exists in the hash, update it in the db
171
+ if format
172
+ format_db.update_attributes({ :url => format[:url], :length => format[:length] })
173
+ formats_copy.delete(format)
174
+
175
+ # the format is not in the hash, remove from the db
176
+ else
177
+ format_db.destroy
178
+ end
179
+ end
180
+
181
+ # for formats that are not in the db yet
182
+ formats_copy.each do |format|
183
+ unless format[:type].blank?
184
+ attrs = { :recording_id => recording.id, :format_type => format[:type],
185
+ :url => format[:url], :length => format[:length].to_i }
186
+ BigbluebuttonPlaybackFormat.create!(attrs)
187
+ end
188
+ end
189
+ end
190
+
191
+ end
@@ -2,20 +2,39 @@ class BigbluebuttonRoom < ActiveRecord::Base
2
2
  belongs_to :server, :class_name => 'BigbluebuttonServer'
3
3
  belongs_to :owner, :polymorphic => true
4
4
 
5
+ has_many :recordings,
6
+ :class_name => 'BigbluebuttonRecording',
7
+ :foreign_key => 'room_id',
8
+ :dependent => :nullify
9
+
10
+ has_many :metadata,
11
+ :class_name => 'BigbluebuttonMetadata',
12
+ :as => :owner,
13
+ :dependent => :destroy,
14
+ :inverse_of => :owner
15
+
16
+ accepts_nested_attributes_for :metadata,
17
+ :allow_destroy => true,
18
+ :reject_if => :all_blank
19
+
5
20
  validates :meetingid, :presence => true, :uniqueness => true,
6
21
  :length => { :minimum => 1, :maximum => 100 }
7
22
  validates :name, :presence => true, :uniqueness => true,
8
23
  :length => { :minimum => 1, :maximum => 150 }
9
24
  validates :welcome_msg, :length => { :maximum => 250 }
10
25
  validates :private, :inclusion => { :in => [true, false] }
11
- validates :randomize_meetingid, :inclusion => { :in => [true, false] }
12
26
  validates :voice_bridge, :presence => true, :uniqueness => true
27
+ validates :record, :inclusion => { :in => [true, false] }
28
+
29
+ validates :duration,
30
+ :presence => true,
31
+ :numericality => { :only_integer => true, :greater_than_or_equal_to => 0 }
13
32
 
14
33
  validates :param,
15
34
  :presence => true,
16
35
  :uniqueness => true,
17
- :length => { :minimum => 3 },
18
- :format => { :with => /^[a-zA-Z\d_]+[a-zA-Z\d_-]*[a-zA-Z\d_]+$/,
36
+ :length => { :minimum => 1 },
37
+ :format => { :with => /^([a-zA-Z\d_]|[a-zA-Z\d_]+[a-zA-Z\d_-]*[a-zA-Z\d_]+)$/,
19
38
  :message => I18n.t('bigbluebutton_rails.rooms.errors.param_format') }
20
39
 
21
40
  # Passwords are 16 character strings
@@ -28,8 +47,8 @@ class BigbluebuttonRoom < ActiveRecord::Base
28
47
 
29
48
  attr_accessible :name, :server_id, :meetingid, :attendee_password, :moderator_password,
30
49
  :welcome_msg, :owner, :server, :private, :logout_url, :dial_number,
31
- :voice_bridge, :max_participants, :owner_id, :owner_type, :randomize_meetingid,
32
- :external, :param
50
+ :voice_bridge, :max_participants, :owner_id, :owner_type,
51
+ :external, :param, :record, :duration, :metadata_attributes
33
52
 
34
53
  # Note: these params need to be fetched from the server before being accessed
35
54
  attr_accessor :running, :participant_count, :moderator_count, :attendees,
@@ -37,10 +56,16 @@ class BigbluebuttonRoom < ActiveRecord::Base
37
56
 
38
57
  after_initialize :init
39
58
  before_validation :set_param
59
+ before_validation :set_passwords
40
60
 
41
61
  # the full logout_url used when logout_url is a relative path
42
62
  attr_accessor :full_logout_url
43
63
 
64
+ # HTTP headers that will be passed to the BigBlueButtonApi object to send
65
+ # in all GET/POST requests to a webconf server.
66
+ # Currently used to send the client's IP to the load balancer.
67
+ attr_accessor :request_headers
68
+
44
69
  # Convenience method to access the attribute <tt>running</tt>
45
70
  def is_running?
46
71
  @running
@@ -61,7 +86,7 @@ class BigbluebuttonRoom < ActiveRecord::Base
61
86
  # * <tt>end_time</tt>
62
87
  # * <tt>attendees</tt> (array of <tt>BigbluebuttonAttendee</tt>)
63
88
  #
64
- # Triggers API call: <tt>get_meeting_info</tt>.
89
+ # Triggers API call: <tt>getMeetingInfo</tt>.
65
90
  def fetch_meeting_info
66
91
  require_server
67
92
 
@@ -85,7 +110,7 @@ class BigbluebuttonRoom < ActiveRecord::Base
85
110
 
86
111
  # Fetches the BBB server to see if the meeting is running. Sets <tt>running</tt>
87
112
  #
88
- # Triggers API call: <tt>is_meeting_running</tt>.
113
+ # Triggers API call: <tt>isMeetingRunning</tt>.
89
114
  def fetch_is_running?
90
115
  require_server
91
116
  @running = self.server.api.is_meeting_running?(self.meetingid)
@@ -93,13 +118,15 @@ class BigbluebuttonRoom < ActiveRecord::Base
93
118
 
94
119
  # Sends a call to the BBB server to end the meeting.
95
120
  #
96
- # Triggers API call: <tt>end_meeting</tt>.
121
+ # Triggers API call: <tt>end</tt>.
97
122
  def send_end
98
123
  require_server
99
124
  self.server.api.end_meeting(self.meetingid, self.moderator_password)
100
125
  end
101
126
 
102
127
  # Sends a call to the BBB server to create the meeting.
128
+ # 'username' is the name of the user that is creating the meeting.
129
+ # 'userid' is the id of the user that is creating the meeting.
103
130
  #
104
131
  # Will trigger 'select_server' to select a server where the meeting
105
132
  # will be created. If a server is selected, the model is saved.
@@ -108,37 +135,15 @@ class BigbluebuttonRoom < ActiveRecord::Base
108
135
  # * <tt>attendee_password</tt>
109
136
  # * <tt>moderator_password</tt>
110
137
  #
111
- # Triggers API call: <tt>create_meeting</tt>.
112
- def send_create
113
- # updates the server whenever a meeting will be created
138
+ # Triggers API call: <tt>create</tt>.
139
+ def send_create(username=nil, userid=nil)
140
+ # updates the server whenever a meeting will be created and guarantees it has a meetingid
114
141
  self.server = select_server
142
+ self.meetingid = unique_meetingid() if self.meetingid.nil?
115
143
  self.save unless self.new_record?
116
144
  require_server
117
145
 
118
- unless self.randomize_meetingid
119
- response = do_create_meeting
120
-
121
- # create a new random meetingid everytime create fails with "duplicateWarning"
122
- else
123
- self.meetingid = random_meetingid
124
-
125
- count = 0
126
- try_again = true
127
- while try_again and count < 10
128
- response = do_create_meeting
129
-
130
- count += 1
131
- try_again = false
132
- unless response.nil?
133
- if response[:returncode] && response[:messageKey] == "duplicateWarning"
134
- self.meetingid = random_meetingid
135
- try_again = true
136
- end
137
- end
138
-
139
- end
140
- end
141
-
146
+ response = do_create_meeting(username, userid)
142
147
  unless response.nil?
143
148
  self.attendee_password = response[:attendeePW]
144
149
  self.moderator_password = response[:moderatorPW]
@@ -208,25 +213,18 @@ class BigbluebuttonRoom < ActiveRecord::Base
208
213
  self.param
209
214
  end
210
215
 
211
- # The join logic
212
- # A moderator can create the meeting and join
213
- # An attendee can only join if the meeting is running
214
- def perform_join(username, role, request=nil)
216
+ # The create logic.
217
+ # Will create the meeting in this room unless it is already running.
218
+ # Returns true if the meeting was created.
219
+ def create_meeting(username, userid=nil, request=nil)
215
220
  fetch_is_running?
216
-
217
- # if the user is a moderator, create the room (if needed) and join it
218
- if role == :moderator
221
+ unless is_running?
219
222
  add_domain_to_logout_url(request.protocol, request.host_with_port) unless request.nil?
220
- send_create unless is_running?
221
- ret = join_url(username, role)
222
-
223
- # normal user only joins if the conference is running
224
- # if it's not, wait for a moderator to create the conference
223
+ send_create(username, userid)
224
+ true
225
225
  else
226
- ret = join_url(username, role) if is_running?
226
+ false
227
227
  end
228
-
229
- ret
230
228
  end
231
229
 
232
230
  # add a domain name and/or protocol to the logout_url if needed
@@ -244,6 +242,13 @@ class BigbluebuttonRoom < ActiveRecord::Base
244
242
  end
245
243
  end
246
244
 
245
+ def unique_meetingid
246
+ # GUID
247
+ # Has to be globally unique in case more that one bigbluebutton_rails application is using
248
+ # the same web conference server.
249
+ "#{SecureRandom.uuid}-#{Time.now.to_i}"
250
+ end
251
+
247
252
  protected
248
253
 
249
254
  # Every room needs a server to be used.
@@ -269,9 +274,11 @@ class BigbluebuttonRoom < ActiveRecord::Base
269
274
  end
270
275
 
271
276
  def init
272
- self[:meetingid] ||= random_meetingid
277
+ self[:meetingid] ||= unique_meetingid
273
278
  self[:voice_bridge] ||= random_voice_bridge
274
279
 
280
+ @request_headers = {}
281
+
275
282
  # fetched attributes
276
283
  @participant_count = 0
277
284
  @moderator_count = 0
@@ -282,15 +289,6 @@ class BigbluebuttonRoom < ActiveRecord::Base
282
289
  @attendees = []
283
290
  end
284
291
 
285
- def random_meetingid
286
- # TODO temporarily using the name to get a friendlier meetingid
287
- if self[:name].blank?
288
- SecureRandom.hex(8)
289
- else
290
- self[:name] + '-' + SecureRandom.random_number(9999).to_s
291
- end
292
- end
293
-
294
292
  def random_voice_bridge
295
293
  value = (70000 + SecureRandom.random_number(9999)).to_s
296
294
  count = 1
@@ -301,14 +299,24 @@ class BigbluebuttonRoom < ActiveRecord::Base
301
299
  value
302
300
  end
303
301
 
304
- def do_create_meeting
305
- msg = (self.welcome_msg.nil? or self.welcome_msg.empty?) ? default_welcome_message : self.welcome_msg
302
+ def do_create_meeting(username=nil, userid=nil)
306
303
  opts = {
307
- :moderatorPW => self.moderator_password, :attendeePW => self.attendee_password,
308
- :welcome => msg, :dialNumber => self.dial_number,
304
+ :record => self.record,
305
+ :duration => self.duration,
306
+ :moderatorPW => self.moderator_password,
307
+ :attendeePW => self.attendee_password,
308
+ :welcome => self.welcome_msg.blank? ? default_welcome_message : self.welcome_msg,
309
+ :dialNumber => self.dial_number,
309
310
  :logoutURL => self.full_logout_url || self.logout_url,
310
- :maxParticipants => self.max_participants, :voiceBridge => self.voice_bridge
311
- }
311
+ :maxParticipants => self.max_participants,
312
+ :voiceBridge => self.voice_bridge
313
+ }.merge(self.get_metadata_for_create)
314
+
315
+ # Add information about the user that is creating the meeting (if any)
316
+ opts.merge!({ "meta_#{BigbluebuttonRails.metadata_user_id}" => userid }) unless userid.nil?
317
+ opts.merge!({ "meta_#{BigbluebuttonRails.metadata_user_name}" => username }) unless username.nil?
318
+
319
+ self.server.api.request_headers = @request_headers # we need the client's IP
312
320
  self.server.api.create_meeting(self.name, self.meetingid, opts)
313
321
  end
314
322
 
@@ -327,4 +335,22 @@ class BigbluebuttonRoom < ActiveRecord::Base
327
335
  end
328
336
  end
329
337
 
338
+ # When setting a room as private we generate passwords in case they don't exist.
339
+ def set_passwords
340
+ if self.private_changed? and self.private
341
+ if self.moderator_password.blank?
342
+ self.moderator_password = SecureRandom.hex(4)
343
+ end
344
+ if self.attendee_password.blank?
345
+ self.attendee_password = SecureRandom.hex(4)
346
+ end
347
+ end
348
+ end
349
+
350
+ def get_metadata_for_create
351
+ self.metadata.inject({}) { |result, meta|
352
+ result["meta_#{meta.name}"] = meta.content; result
353
+ }
354
+ end
355
+
330
356
  end