bigbluebutton_rails 1.3.0 → 1.4.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. data/.gitignore +2 -0
  2. data/.rspec +2 -2
  3. data/.ruby-version +1 -1
  4. data/CHANGELOG.rdoc +31 -6
  5. data/Gemfile +13 -3
  6. data/Gemfile.lock +88 -42
  7. data/README.rdoc +84 -15
  8. data/Rakefile +22 -25
  9. data/{TODO_08 → TODO.rdoc} +6 -2
  10. data/app/assets/images/badges/apple_store_en.png +0 -0
  11. data/app/assets/images/badges/apple_store_pt-br.png +0 -0
  12. data/app/assets/images/badges/google_play_en.png +0 -0
  13. data/app/assets/images/badges/google_play_pt-br.png +0 -0
  14. data/app/controllers/bigbluebutton/recordings_controller.rb +25 -10
  15. data/app/controllers/bigbluebutton/rooms_controller.rb +158 -141
  16. data/app/controllers/bigbluebutton/servers_controller.rb +25 -12
  17. data/app/helpers/bigbluebutton_rails_helper.rb +20 -0
  18. data/app/models/bigbluebutton_meeting.rb +28 -0
  19. data/app/models/bigbluebutton_metadata.rb +2 -2
  20. data/app/models/bigbluebutton_playback_format.rb +2 -2
  21. data/app/models/bigbluebutton_recording.rb +29 -4
  22. data/app/models/bigbluebutton_room.rb +153 -22
  23. data/app/models/bigbluebutton_room_options.rb +46 -0
  24. data/app/models/bigbluebutton_server.rb +4 -2
  25. data/app/views/bigbluebutton/recordings/_form.html.erb +4 -0
  26. data/app/views/bigbluebutton/recordings/_recordings.html.erb +1 -0
  27. data/app/views/bigbluebutton/recordings/show.html.erb +5 -0
  28. data/app/views/bigbluebutton/rooms/join_mobile.html.erb +29 -5
  29. data/app/workers/bigbluebutton_meeting_updater.rb +31 -0
  30. data/bigbluebutton_rails.gemspec +5 -1
  31. data/config/locales/en.yml +6 -5
  32. data/config/schedule.rb +4 -0
  33. data/lib/bigbluebutton_rails/controller_methods.rb +29 -10
  34. data/lib/bigbluebutton_rails/internal_controller_methods.rb +56 -0
  35. data/lib/bigbluebutton_rails/rails/routes.rb +1 -5
  36. data/lib/bigbluebutton_rails/version.rb +1 -1
  37. data/lib/bigbluebutton_rails.rb +11 -0
  38. data/lib/generators/bigbluebutton_rails/templates/migration.rb +28 -0
  39. data/lib/generators/bigbluebutton_rails/templates/migration_1_4_0.rb +37 -0
  40. data/lib/tasks/bigbluebutton_rails/meetings.rake +22 -0
  41. data/lib/tasks/bigbluebutton_rails/recordings.rake +2 -1
  42. data/spec/classes/bigbluebutton_attendee_spec.rb +1 -1
  43. data/spec/controllers/bigbluebutton/recordings_controller_json_responses_spec.rb +10 -9
  44. data/spec/controllers/bigbluebutton/recordings_controller_spec.rb +103 -16
  45. data/spec/controllers/bigbluebutton/rooms_controller_exception_handling_spec.rb +15 -5
  46. data/spec/controllers/bigbluebutton/rooms_controller_json_responses_spec.rb +17 -12
  47. data/spec/controllers/bigbluebutton/rooms_controller_spec.rb +485 -432
  48. data/spec/controllers/bigbluebutton/servers_controller_json_responses_spec.rb +20 -13
  49. data/spec/controllers/bigbluebutton/servers_controller_spec.rb +174 -16
  50. data/spec/factories/bigbluebutton_meeting.rb +13 -0
  51. data/spec/factories/bigbluebutton_recording.rb +4 -2
  52. data/spec/factories/bigbluebutton_room.rb +5 -1
  53. data/spec/factories/bigbluebutton_room_options.rb +6 -0
  54. data/spec/generators/install_generator_spec.rb +1 -1
  55. data/spec/generators/views_generator_spec.rb +0 -1
  56. data/spec/lib/tasks/meetings_rake_spec.rb +5 -0
  57. data/spec/lib/tasks/recordings_rake_spec.rb +5 -0
  58. data/spec/models/bigbluebutton_meeting_db_spec.rb +24 -0
  59. data/spec/models/bigbluebutton_meeting_spec.rb +52 -0
  60. data/spec/models/bigbluebutton_metadata_db_spec.rb +2 -0
  61. data/spec/models/bigbluebutton_metadata_spec.rb +18 -22
  62. data/spec/models/bigbluebutton_playback_format_db_spec.rb +2 -0
  63. data/spec/models/bigbluebutton_playback_format_spec.rb +0 -4
  64. data/spec/models/bigbluebutton_recording_db_spec.rb +3 -0
  65. data/spec/models/bigbluebutton_recording_spec.rb +69 -16
  66. data/spec/models/bigbluebutton_room_db_spec.rb +2 -0
  67. data/spec/models/bigbluebutton_room_options_db_spec.rb +16 -0
  68. data/spec/models/bigbluebutton_room_options_spec.rb +206 -0
  69. data/spec/models/bigbluebutton_room_spec.rb +420 -80
  70. data/spec/models/bigbluebutton_server_db_spec.rb +2 -0
  71. data/spec/models/bigbluebutton_server_spec.rb +22 -24
  72. data/spec/rails_app/config/application.rb +3 -0
  73. data/spec/rails_app/config/database.yml.example +28 -0
  74. data/spec/rails_app/config/environments/test.rb +1 -1
  75. data/spec/rails_app/db/seeds.rb +1 -0
  76. data/spec/rails_app/features/step_definitions/activity_monitor_servers_step.rb +0 -2
  77. data/spec/rails_app/features/step_definitions/common_steps.rb +6 -0
  78. data/spec/rails_app/features/step_definitions/join_rooms_steps.rb +0 -6
  79. data/spec/rails_app/features/support/locales.rb +4 -2
  80. data/spec/rails_app/features/support/paths.rb +0 -2
  81. data/spec/rails_app/features/support/templates.rb +2 -17
  82. data/spec/rails_app/lib/tasks/db/populate.rake +2 -0
  83. data/spec/rails_app/lib/tasks/resque.rake +3 -0
  84. data/spec/routing/bigbluebutton/custom_controllers_routing_spec.rb +1 -10
  85. data/spec/routing/bigbluebutton/rooms_only_routing_spec.rb +1 -9
  86. data/spec/routing/bigbluebutton/rooms_routing_spec.rb +3 -31
  87. data/spec/spec_helper.rb +40 -24
  88. data/spec/support/matchers/array_with_key.rb +7 -0
  89. data/spec/support/matchers/array_without_key.rb +7 -0
  90. data/spec/support/matchers/delegate_matcher.rb +62 -0
  91. data/spec/support/matchers/respond_with_content_type.rb +8 -0
  92. data/spec/support/matchers/shoulda/assign_to_matcher.rb +131 -0
  93. data/spec/support/mocked_server.rb +2 -2
  94. data/spec/support/models/user.rb +11 -0
  95. data/spec/workers/bigbluebutton_meeting_updater_spec.rb +9 -0
  96. metadata +86 -16
  97. data/app/views/bigbluebutton/rooms/external.html.erb +0 -25
  98. data/spec/rails_app/config/database.yml +0 -25
  99. data/spec/rails_app/features/join_external_rooms.feature +0 -61
@@ -1,4 +1,5 @@
1
1
  class Bigbluebutton::ServersController < ApplicationController
2
+ include BigbluebuttonRails::InternalControllerMethods
2
3
 
3
4
  respond_to :html
4
5
  respond_to :json, :only => [:index, :show, :new, :create, :update, :destroy, :activity, :rooms]
@@ -57,17 +58,17 @@ class Bigbluebutton::ServersController < ApplicationController
57
58
  end
58
59
 
59
60
  def create
60
- @server = BigbluebuttonServer.new(params[:bigbluebutton_server])
61
+ @server = BigbluebuttonServer.new(server_params)
61
62
 
62
63
  respond_with @server do |format|
63
64
  if @server.save
64
65
  format.html {
65
66
  message = t('bigbluebutton_rails.servers.notice.create.success')
66
- redirect_to(@server, :notice => message)
67
+ redirect_to_using_params @server, :notice => message
67
68
  }
68
69
  format.json { render :json => @server, :status => :created }
69
70
  else
70
- format.html { render :new }
71
+ format.html { redirect_to_params_or_render :new }
71
72
  format.json { render :json => @server.errors.full_messages, :status => :unprocessable_entity }
72
73
  end
73
74
  end
@@ -75,14 +76,14 @@ class Bigbluebutton::ServersController < ApplicationController
75
76
 
76
77
  def update
77
78
  respond_with @server do |format|
78
- if @server.update_attributes(params[:bigbluebutton_server])
79
+ if @server.update_attributes(server_params)
79
80
  format.html {
80
81
  message = t('bigbluebutton_rails.servers.notice.update.success')
81
- redirect_to(@server, :notice => message)
82
+ redirect_to_using_params @server, :notice => message
82
83
  }
83
- format.json { head :ok }
84
+ format.json { render :json => true, :status => :ok }
84
85
  else
85
- format.html { render :edit }
86
+ format.html { redirect_to_params_or_render :edit }
86
87
  format.json { render :json => @server.errors.full_messages, :status => :unprocessable_entity }
87
88
  end
88
89
  end
@@ -93,8 +94,8 @@ class Bigbluebutton::ServersController < ApplicationController
93
94
  @server.destroy
94
95
 
95
96
  respond_with do |format|
96
- format.html { redirect_to(bigbluebutton_servers_url) }
97
- format.json { head :ok }
97
+ format.html { redirect_to_using_params bigbluebutton_servers_url }
98
+ format.json { render :json => true, :status => :ok }
98
99
  end
99
100
  end
100
101
 
@@ -146,7 +147,7 @@ class Bigbluebutton::ServersController < ApplicationController
146
147
  if error
147
148
  render :json => { :message => message }, :status => :error
148
149
  else
149
- head :ok
150
+ render :json => true, :status => :ok
150
151
  end
151
152
  }
152
153
  end
@@ -168,7 +169,7 @@ class Bigbluebutton::ServersController < ApplicationController
168
169
  else
169
170
  message = t('bigbluebutton_rails.servers.notice.unpublish_recordings.success')
170
171
  end
171
- redirect_to(recordings_bigbluebutton_server_path(@server), :notice => message)
172
+ redirect_to_using_params recordings_bigbluebutton_server_path(@server), :notice => message
172
173
  }
173
174
  format.json { render :json => message }
174
175
  end
@@ -176,11 +177,23 @@ class Bigbluebutton::ServersController < ApplicationController
176
177
  respond_with do |format|
177
178
  format.html {
178
179
  flash[:error] = e.to_s[0..200]
179
- redirect_to recordings_bigbluebutton_server_path(@server)
180
+ redirect_to_using_params recordings_bigbluebutton_server_path(@server)
180
181
  }
181
182
  format.json { render :json => e.to_s, :status => :error }
182
183
  end
183
184
  end
184
185
  end
185
186
 
187
+ def server_params
188
+ unless params[:bigbluebutton_server].nil?
189
+ params[:bigbluebutton_server].permit(*server_allowed_params)
190
+ else
191
+ []
192
+ end
193
+ end
194
+
195
+ def server_allowed_params
196
+ [ :name, :url, :version, :salt, :param ]
197
+ end
198
+
186
199
  end
@@ -21,4 +21,24 @@ module BigbluebuttonRailsHelper
21
21
  room
22
22
  end
23
23
 
24
+ # Link to download the Android application from Google Play.
25
+ def mobile_google_play_link
26
+ "https://play.google.com/store/apps/details?id=air.com.mconf.mconfmobile"
27
+ end
28
+
29
+ # Google Play image to show together with the link to download the Android client.
30
+ def mobile_google_play_image
31
+ "badges/google_play_#{I18n.locale}.png"
32
+ end
33
+
34
+ # Link to download the iOS application from Apple Store.
35
+ def mobile_apple_store_link
36
+ "https://itunes.apple.com/us/app/mconf-mobile/id864391260"
37
+ end
38
+
39
+ # Apple Store image to show together with the link to download the iOS client.
40
+ def mobile_apple_store_image
41
+ "badges/apple_store_#{I18n.locale}.png"
42
+ end
43
+
24
44
  end
@@ -0,0 +1,28 @@
1
+ class BigbluebuttonMeeting < ActiveRecord::Base
2
+ include ActiveModel::ForbiddenAttributesProtection
3
+
4
+ belongs_to :server, :class_name => 'BigbluebuttonServer'
5
+ belongs_to :room, :class_name => 'BigbluebuttonRoom'
6
+
7
+ has_one :recording,
8
+ :class_name => 'BigbluebuttonRecording',
9
+ :foreign_key => 'meeting_id',
10
+ :dependent => :nullify
11
+
12
+ validates :room, :presence => true
13
+
14
+ validates :meetingid, :presence => true, :length => { :minimum => 1, :maximum => 100 }
15
+
16
+ validates :start_time, :presence => true
17
+ validates :start_time, :uniqueness => { :scope => :room_id }
18
+
19
+ # Whether the meeting was created by the `user` or not.
20
+ def created_by?(user)
21
+ unless user.nil?
22
+ userid = user.send(BigbluebuttonRails.user_attr_id)
23
+ self.creator_id == userid
24
+ else
25
+ false
26
+ end
27
+ end
28
+ end
@@ -1,4 +1,6 @@
1
1
  class BigbluebuttonMetadata < ActiveRecord::Base
2
+ include ActiveModel::ForbiddenAttributesProtection
3
+
2
4
  belongs_to :owner, :polymorphic => true, :inverse_of => :metadata
3
5
 
4
6
  validates :owner_id, :presence => true
@@ -13,8 +15,6 @@ class BigbluebuttonMetadata < ActiveRecord::Base
13
15
  :uniqueness => { :scope => [:owner_id, :owner_type] }
14
16
  validate :exclusion_of_name_in_reserved_metadata_keys
15
17
 
16
- attr_accessible :name, :content
17
-
18
18
  # metadata keys are only invalid when the metadata belongs to a room,
19
19
  # metadata that will be used in a 'create' call
20
20
  # TODO: a better solution for rails >= 3.1
@@ -1,9 +1,9 @@
1
1
  class BigbluebuttonPlaybackFormat < ActiveRecord::Base
2
+ include ActiveModel::ForbiddenAttributesProtection
3
+
2
4
  belongs_to :recording, :class_name => 'BigbluebuttonRecording'
3
5
 
4
6
  validates :recording_id, :presence => true
5
7
 
6
8
  validates :format_type, :presence => true
7
-
8
- attr_accessible :recording_id, :format_type, :url, :length
9
9
  end
@@ -1,6 +1,9 @@
1
1
  class BigbluebuttonRecording < ActiveRecord::Base
2
+ include ActiveModel::ForbiddenAttributesProtection
3
+
2
4
  belongs_to :server, :class_name => 'BigbluebuttonServer'
3
5
  belongs_to :room, :class_name => 'BigbluebuttonRoom'
6
+ belongs_to :meeting, :class_name => 'BigbluebuttonMeeting'
4
7
 
5
8
  validates :server, :presence => true
6
9
 
@@ -8,9 +11,6 @@ class BigbluebuttonRecording < ActiveRecord::Base
8
11
  :presence => true,
9
12
  :uniqueness => true
10
13
 
11
- attr_accessible :recordid, :meetingid, :name, :published, :start_time,
12
- :end_time, :available
13
-
14
14
  has_many :metadata,
15
15
  :class_name => 'BigbluebuttonMetadata',
16
16
  :as => :owner,
@@ -69,7 +69,8 @@ class BigbluebuttonRecording < ActiveRecord::Base
69
69
 
70
70
  protected
71
71
 
72
- # Adapt keys in 'hash' from bigbluebutton-api-ruby's format to ours
72
+ # Adapt keys in 'hash' from bigbluebutton-api-ruby's (the way they are returned by
73
+ # BigBlueButton's API) format to ours (more rails-like).
73
74
  def self.adapt_recording_hash(hash)
74
75
  new_hash = hash.clone
75
76
  mappings = {
@@ -104,6 +105,8 @@ class BigbluebuttonRecording < ActiveRecord::Base
104
105
  recording.available = true
105
106
  recording.room = BigbluebuttonRails.match_room_recording(data)
106
107
  recording.server = server
108
+ recording.description = I18n.t('bigbluebutton_rails.recordings.default.description', :time => recording.start_time.utc.to_formatted_s(:long))
109
+ recording.meeting = BigbluebuttonRecording.find_matching_meeting(recording)
107
110
  recording.save!
108
111
 
109
112
  sync_additional_data(recording, data)
@@ -188,4 +191,26 @@ class BigbluebuttonRecording < ActiveRecord::Base
188
191
  end
189
192
  end
190
193
 
194
+ # Finds the BigbluebuttonMeeting that generated this recording. The meeting is searched using
195
+ # the room associated with this recording and the create time of the meeting, taken from
196
+ # the recording's ID.
197
+ def self.find_matching_meeting(recording)
198
+ meeting = nil
199
+
200
+ unless recording.nil? or recording.room.nil?
201
+
202
+ # recordid is something like: 'dd2816950ce2f1e0a928c1a5b8d5b526e9b3e32c-1381978014526'
203
+ # the create time of the meeting is the timestamp at the end
204
+ start_time = recording.recordid.match(/-(\d*)$/)
205
+ unless start_time.nil?
206
+ start_time = start_time[1]
207
+ start_time = Time.at(start_time.to_i).to_datetime.utc
208
+ meeting = BigbluebuttonMeeting.where(:room_id => recording.room.id, :start_time => start_time).last
209
+ logger.info "Recording: meeting found for the recording #{recording.inspect}: #{meeting.inspect}"
210
+ end
211
+ end
212
+
213
+ meeting
214
+ end
215
+
191
216
  end
@@ -1,4 +1,6 @@
1
1
  class BigbluebuttonRoom < ActiveRecord::Base
2
+ include ActiveModel::ForbiddenAttributesProtection
3
+
2
4
  belongs_to :server, :class_name => 'BigbluebuttonServer'
3
5
  belongs_to :owner, :polymorphic => true
4
6
 
@@ -13,6 +15,18 @@ class BigbluebuttonRoom < ActiveRecord::Base
13
15
  :dependent => :destroy,
14
16
  :inverse_of => :owner
15
17
 
18
+ has_one :room_options,
19
+ :class_name => 'BigbluebuttonRoomOptions',
20
+ :foreign_key => 'room_id',
21
+ :autosave => true,
22
+ :dependent => :destroy
23
+
24
+ delegate :default_layout, :default_layout=, :to => :room_options
25
+ delegate :presenter_share_only, :presenter_share_only=, :to => :room_options
26
+ 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
29
+
16
30
  accepts_nested_attributes_for :metadata,
17
31
  :allow_destroy => true,
18
32
  :reject_if => :all_blank
@@ -45,16 +59,12 @@ class BigbluebuttonRoom < ActiveRecord::Base
45
59
  validates :attendee_password, :presence => true, :if => :private?
46
60
  validates :moderator_password, :presence => true, :if => :private?
47
61
 
48
- attr_accessible :name, :server_id, :meetingid, :attendee_password, :moderator_password,
49
- :welcome_msg, :owner, :server, :private, :logout_url, :dial_number,
50
- :voice_bridge, :max_participants, :owner_id, :owner_type,
51
- :external, :param, :record, :duration, :metadata_attributes
52
-
53
62
  # Note: these params need to be fetched from the server before being accessed
54
63
  attr_accessor :running, :participant_count, :moderator_count, :attendees,
55
64
  :has_been_forcibly_ended, :start_time, :end_time
56
65
 
57
66
  after_initialize :init
67
+ after_create :create_room_options
58
68
  before_validation :set_param
59
69
  before_validation :set_passwords
60
70
 
@@ -66,6 +76,13 @@ class BigbluebuttonRoom < ActiveRecord::Base
66
76
  # Currently used to send the client's IP to the load balancer.
67
77
  attr_accessor :request_headers
68
78
 
79
+ # In case there's no room_options created yet, build one
80
+ # (happens usually when an old database is migrated).
81
+ def room_options_with_initialize
82
+ room_options_without_initialize || build_room_options
83
+ end
84
+ alias_method_chain :room_options, :initialize
85
+
69
86
  # Convenience method to access the attribute <tt>running</tt>
70
87
  def is_running?
71
88
  @running
@@ -105,6 +122,9 @@ class BigbluebuttonRoom < ActiveRecord::Base
105
122
  @attendees << attendee
106
123
  end
107
124
 
125
+ # a 'shortcut' to update meetings since we have all information we need
126
+ update_current_meeting(response[:metadata])
127
+
108
128
  response
109
129
  end
110
130
 
@@ -121,12 +141,19 @@ class BigbluebuttonRoom < ActiveRecord::Base
121
141
  # Triggers API call: <tt>end</tt>.
122
142
  def send_end
123
143
  require_server
124
- self.server.api.end_meeting(self.meetingid, self.moderator_password)
144
+ response = self.server.api.end_meeting(self.meetingid, self.moderator_password)
145
+
146
+ # enqueue an update in the meetings for later on
147
+ Resque.enqueue(::BigbluebuttonMeetingUpdater, self.id, 15.seconds)
148
+
149
+ response
125
150
  end
126
151
 
127
152
  # 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.
153
+ # 'user' is the object that represents the user that is creating the meeting.
154
+ # 'user_opts' is a hash of parameters to override the parameters sent in the create
155
+ # request. Can be passed by the application to enforce some values over the values
156
+ # that are taken from the database.
130
157
  #
131
158
  # Will trigger 'select_server' to select a server where the meeting
132
159
  # will be created. If a server is selected, the model is saved.
@@ -136,14 +163,14 @@ class BigbluebuttonRoom < ActiveRecord::Base
136
163
  # * <tt>moderator_password</tt>
137
164
  #
138
165
  # Triggers API call: <tt>create</tt>.
139
- def send_create(username=nil, userid=nil)
166
+ def send_create(user=nil, user_opts={})
140
167
  # updates the server whenever a meeting will be created and guarantees it has a meetingid
141
168
  self.server = select_server
142
169
  self.meetingid = unique_meetingid() if self.meetingid.nil?
143
170
  self.save unless self.new_record?
144
171
  require_server
145
172
 
146
- response = do_create_meeting(username, userid)
173
+ response = internal_create_meeting(user, user_opts)
147
174
  unless response.nil?
148
175
  self.attendee_password = response[:attendeePW]
149
176
  self.moderator_password = response[:moderatorPW]
@@ -157,19 +184,23 @@ class BigbluebuttonRoom < ActiveRecord::Base
157
184
  # username:: Name of the user
158
185
  # role:: Role of the user in this room. Can be <tt>[:moderator, :attendee]</tt>
159
186
  # password:: Password to be use (in case role == nil)
187
+ # options:: Additional options to use when generating the URL
160
188
  #
161
189
  # Uses the API but does not require a request to the server.
162
- def join_url(username, role, password=nil)
190
+ def join_url(username, role, password=nil, options={})
163
191
  require_server
164
192
 
165
193
  case role
166
194
  when :moderator
167
- self.server.api.join_meeting_url(self.meetingid, username, self.moderator_password)
195
+ r = self.server.api.join_meeting_url(self.meetingid, username, self.moderator_password, options)
168
196
  when :attendee
169
- self.server.api.join_meeting_url(self.meetingid, username, self.attendee_password)
197
+ r = self.server.api.join_meeting_url(self.meetingid, username, self.attendee_password, options)
170
198
  else
171
- self.server.api.join_meeting_url(self.meetingid, username, password)
199
+ r = self.server.api.join_meeting_url(self.meetingid, username, password, options)
172
200
  end
201
+
202
+ r.strip! unless r.nil?
203
+ r
173
204
  end
174
205
 
175
206
 
@@ -179,7 +210,7 @@ class BigbluebuttonRoom < ActiveRecord::Base
179
210
  # params:: Hash with a key :password
180
211
  def user_role(params)
181
212
  role = nil
182
- if params.has_key?(:password)
213
+ if params && params.has_key?(:password)
183
214
  if self.moderator_password == params[:password]
184
215
  role = :moderator
185
216
  elsif self.attendee_password == params[:password]
@@ -216,11 +247,11 @@ class BigbluebuttonRoom < ActiveRecord::Base
216
247
  # The create logic.
217
248
  # Will create the meeting in this room unless it is already running.
218
249
  # Returns true if the meeting was created.
219
- def create_meeting(username, userid=nil, request=nil)
250
+ def create_meeting(user=nil, request=nil, user_opts={})
220
251
  fetch_is_running?
221
252
  unless is_running?
222
253
  add_domain_to_logout_url(request.protocol, request.host_with_port) unless request.nil?
223
- send_create(username, userid)
254
+ send_create(user, user_opts)
224
255
  true
225
256
  else
226
257
  false
@@ -249,8 +280,97 @@ class BigbluebuttonRoom < ActiveRecord::Base
249
280
  "#{SecureRandom.uuid}-#{Time.now.to_i}"
250
281
  end
251
282
 
283
+ # Returns the current meeting running on this room, if any.
284
+ def get_current_meeting
285
+ unless self.start_time.nil?
286
+ BigbluebuttonMeeting.find_by_room_id_and_start_time(self.id, self.start_time.utc)
287
+ else
288
+ nil
289
+ end
290
+ end
291
+
292
+ # Updates the current meeting associated with this room
293
+ def update_current_meeting(metadata=nil)
294
+ unless self.start_time.nil?
295
+ attrs = {
296
+ :server => self.server,
297
+ :meetingid => self.meetingid,
298
+ :name => self.name,
299
+ :record => self.record,
300
+ :running => self.running
301
+ }
302
+ unless metadata.nil?
303
+ begin
304
+ attrs[:creator_id] = metadata[BigbluebuttonRails.metadata_user_id].to_i
305
+ attrs[:creator_name] = metadata[BigbluebuttonRails.metadata_user_name]
306
+ rescue
307
+ attrs[:creator_id] = nil
308
+ attrs[:creator_name] = nil
309
+ end
310
+ end
311
+
312
+ meeting = self.get_current_meeting
313
+ if !meeting.nil?
314
+ meeting.update_attributes(attrs)
315
+
316
+ # only create a new meeting if it is running
317
+ elsif self.running
318
+ attrs.merge!({ :room => self, :start_time => self.start_time.utc })
319
+ meeting = BigbluebuttonMeeting.create(attrs)
320
+
321
+ end
322
+ else
323
+ # TODO: not enough information to find the meeting, do what?
324
+ end
325
+ end
326
+
327
+ # Sets all meetings related to this room as not running
328
+ def finish_meetings
329
+ BigbluebuttonMeeting.where(:running => true)
330
+ .find_by_room_id(room_id)
331
+ .update_attributes(:running => false)
332
+ end
333
+
334
+ # Gets a 'configToken' to use when joining the room.
335
+ # Returns a string with the token generated or nil if there's no need
336
+ # for a token (the options set in the room are the default options or there
337
+ # are no options set in the room) or if an error occurred.
338
+ #
339
+ # The entire process consists in these steps:
340
+ # * Go to the server get the default config.xml;
341
+ # * Modify the config.xml based on the room options set in the room;
342
+ # * Go to the server set the new config.xml;
343
+ # * Get the token identifier and return it.
344
+ #
345
+ # Triggers API call: <tt>getDefaultConfigXML</tt>.
346
+ # Triggers API call: <tt>setConfigXML</tt>.
347
+ def fetch_new_token
348
+ if self.room_options.is_modified?
349
+
350
+ # get the default XML we will use to create a new one
351
+ config_xml = self.server.api.get_default_config_xml
352
+
353
+ # set the options on the XML
354
+ # returns true if something was changed
355
+ config_xml = self.room_options.set_on_config_xml(config_xml)
356
+ if config_xml
357
+
358
+ # get the new token for the room, and return it
359
+ self.server.api.set_config_xml(self.meetingid, config_xml)
360
+ else
361
+ nil
362
+ end
363
+ else
364
+ nil
365
+ end
366
+ end
367
+
252
368
  protected
253
369
 
370
+ def create_room_options
371
+ BigbluebuttonRoomOptions.create(:room => self)
372
+ end
373
+
254
374
  # Every room needs a server to be used.
255
375
  # The server of a room can change during the room's lifespan, but
256
376
  # it should not change if the room is running or if it was created
@@ -299,7 +419,7 @@ class BigbluebuttonRoom < ActiveRecord::Base
299
419
  value
300
420
  end
301
421
 
302
- def do_create_meeting(username=nil, userid=nil)
422
+ def internal_create_meeting(user=nil, user_opts={})
303
423
  opts = {
304
424
  :record => self.record,
305
425
  :duration => self.duration,
@@ -310,14 +430,25 @@ class BigbluebuttonRoom < ActiveRecord::Base
310
430
  :logoutURL => self.full_logout_url || self.logout_url,
311
431
  :maxParticipants => self.max_participants,
312
432
  :voiceBridge => self.voice_bridge
313
- }.merge(self.get_metadata_for_create)
433
+ }.merge(user_opts)
434
+
435
+ opts.merge!(self.get_metadata_for_create)
314
436
 
315
437
  # 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?
438
+ unless user.nil?
439
+ userid = user.send(BigbluebuttonRails.user_attr_id)
440
+ username = user.send(BigbluebuttonRails.user_attr_name)
441
+ opts.merge!({ "meta_#{BigbluebuttonRails.metadata_user_id}" => userid })
442
+ opts.merge!({ "meta_#{BigbluebuttonRails.metadata_user_name}" => username })
443
+ end
318
444
 
319
445
  self.server.api.request_headers = @request_headers # we need the client's IP
320
- self.server.api.create_meeting(self.name, self.meetingid, opts)
446
+ response = self.server.api.create_meeting(self.name, self.meetingid, opts)
447
+
448
+ # enqueue an update in the meetings to start now
449
+ Resque.enqueue(::BigbluebuttonMeetingUpdater, self.id)
450
+
451
+ response
321
452
  end
322
453
 
323
454
  # Returns the default welcome message to be shown in a conference in case
@@ -0,0 +1,46 @@
1
+ require 'bigbluebutton_api'
2
+
3
+ class BigbluebuttonRoomOptions < ActiveRecord::Base
4
+ include ActiveModel::ForbiddenAttributesProtection
5
+
6
+ belongs_to :room, :class_name => 'BigbluebuttonRoom'
7
+ validates :room_id, :presence => true
8
+
9
+ def get_available_layouts
10
+ ["Default", "Video Chat", "Meeting", "Webinar", "Lecture assistant", "Lecture"]
11
+ end
12
+
13
+ # Sets the attributes from the model into the config.xml passed in the arguments.
14
+ # If anything was modified in the XML, returns the new XML generated as string.
15
+ # Otherwise returns false.
16
+ #
17
+ # xml (string):: The config.xml in which the attributes will be set
18
+ def set_on_config_xml(xml)
19
+ config_xml = BigBlueButton::BigBlueButtonConfigXml.new(xml)
20
+ unless self.default_layout.blank?
21
+ config_xml.set_attribute("layout", "defaultLayout", self.default_layout, false)
22
+ end
23
+ unless self.presenter_share_only.nil?
24
+ config_xml.set_attribute("VideoconfModule", "presenterShareOnly", self.presenter_share_only, true)
25
+ config_xml.set_attribute("PhoneModule", "presenterShareOnly", self.presenter_share_only, true)
26
+ end
27
+ unless self.auto_start_video.nil?
28
+ config_xml.set_attribute("VideoconfModule", "autoStart", self.auto_start_video, true)
29
+ end
30
+ unless self.auto_start_audio.nil?
31
+ config_xml.set_attribute("PhoneModule", "autoJoin", self.auto_start_audio, true)
32
+ end
33
+ if config_xml.is_modified?
34
+ config_xml.as_string
35
+ else
36
+ false
37
+ end
38
+ end
39
+
40
+ # Returns true if any of the attributes was set. Is used to check whether the options
41
+ # have to be sent to the server (setConfigXML) or not.
42
+ def is_modified?
43
+ !self.default_layout.nil? || !self.presenter_share_only.nil? || !self.auto_start_audio.nil? ||
44
+ !self.auto_start_video.nil?
45
+ end
46
+ end
@@ -1,6 +1,8 @@
1
1
  require 'bigbluebutton_api'
2
2
 
3
3
  class BigbluebuttonServer < ActiveRecord::Base
4
+ include ActiveModel::ForbiddenAttributesProtection
5
+
4
6
  has_many :rooms,
5
7
  :class_name => 'BigbluebuttonRoom',
6
8
  :foreign_key => 'server_id',
@@ -38,8 +40,6 @@ class BigbluebuttonServer < ActiveRecord::Base
38
40
  :presence => true,
39
41
  :inclusion => { :in => ['0.7', '0.8'] }
40
42
 
41
- attr_accessible :name, :url, :version, :salt, :param
42
-
43
43
  # Array of <tt>BigbluebuttonMeeting</tt>
44
44
  attr_reader :meetings
45
45
 
@@ -69,6 +69,7 @@ class BigbluebuttonServer < ActiveRecord::Base
69
69
  @meetings = []
70
70
  response[:meetings].each do |attr|
71
71
  room = BigbluebuttonRoom.find_by_server_id_and_meetingid(self.id, attr[:meetingID])
72
+ # TODO: there might be more attributes returned by the API, review them all
72
73
  if room.nil?
73
74
  room = BigbluebuttonRoom.new(:server => self, :meetingid => attr[:meetingID],
74
75
  :name => attr[:meetingID], :attendee_password => attr[:attendeePW],
@@ -78,6 +79,7 @@ class BigbluebuttonServer < ActiveRecord::Base
78
79
  :moderator_password => attr[:moderatorPW])
79
80
  end
80
81
  room.running = attr[:running]
82
+ room.update_current_meeting
81
83
 
82
84
  @meetings << room
83
85
  end
@@ -46,6 +46,10 @@
46
46
  <%= f.label :end_time %><br />
47
47
  <%= f.text_field :end_time %>
48
48
  </div>
49
+ <div class="field">
50
+ <%= f.label :description %><br />
51
+ <%= f.text_field :description %>
52
+ </div>
49
53
 
50
54
  <p>
51
55
  <b><%= BigbluebuttonRecording.human_attribute_name(:metadata) %>:</b>
@@ -22,6 +22,7 @@
22
22
  <div class="field"><label><%= BigbluebuttonRecording.human_attribute_name(:available) %></label> <%= recording.available %></div>
23
23
  <div class="field"><label><%= BigbluebuttonRecording.human_attribute_name(:start_time) %></label> <%= recording.start_time %></div>
24
24
  <div class="field"><label><%= BigbluebuttonRecording.human_attribute_name(:end_time) %></label> <%= recording.end_time %></div>
25
+ <div class="field"><label><%= BigbluebuttonRecording.human_attribute_name(:description) %></label> <%= recording.description %></div>
25
26
  <div class="field">
26
27
  <label><%= BigbluebuttonRecording.human_attribute_name(:metadata) %></label>
27
28
  <div>
@@ -52,6 +52,11 @@
52
52
  <%= @recording.end_time %>
53
53
  </p>
54
54
 
55
+ <p>
56
+ <b><%= BigbluebuttonRecording.human_attribute_name(:description) %>:</b>
57
+ <%= @recording.description %>
58
+ </p>
59
+
55
60
  <p>
56
61
  <b><%= BigbluebuttonRecording.human_attribute_name(:metadata) %>:</b>
57
62
  <% @recording.metadata.each do |metadata| %>