wimba integration, fixes #2989
added wimba web conferencing support, moved dimdim config into plugin settings, fixed a few web conferencing ui issues Change-Id: I6b36b0e594a9f296d14cd35bec02186478bcbd13 Reviewed-on: https://gerrit.instructure.com/2343 Tested-by: Hudson <hudson@instructure.com> Reviewed-by: Bracken Mosbacker <bracken@instructure.com>
This commit is contained in:
parent
820fa03212
commit
86b062bf61
|
@ -42,17 +42,7 @@ class ConferencesController < ApplicationController
|
|||
if authorized_action(@context.web_conferences.new, @current_user, :create)
|
||||
@conference = @context.web_conferences.build(params[:web_conference])
|
||||
@conference.user = @current_user
|
||||
@conference.duration += 30 if @conference.duration
|
||||
members = [@current_user]
|
||||
if params[:user] && params[:user][:all] != '1'
|
||||
ids = []
|
||||
params[:user].each do |id, val|
|
||||
ids << id.to_i if val == '1'
|
||||
end
|
||||
members += @context.users.find_all_by_id(ids).to_a
|
||||
else
|
||||
members += @context.users.to_a
|
||||
end
|
||||
members = get_new_members
|
||||
respond_to do |format|
|
||||
if @conference.save
|
||||
@conference.add_initiator(@current_user)
|
||||
|
@ -74,8 +64,13 @@ class ConferencesController < ApplicationController
|
|||
get_conference
|
||||
if authorized_action(@conference, @current_user, :update)
|
||||
@conference.user ||= @current_user
|
||||
members = get_new_members
|
||||
respond_to do |format|
|
||||
if @conference.update_attributes(params[:web_conference])
|
||||
if @conference.update_attributes(params[:web_conference].delete_if{|k,v| k.to_sym == :conference_type})
|
||||
# TODO: ability to dis-invite people
|
||||
members.uniq.each do |u|
|
||||
@conference.add_invitee(u)
|
||||
end
|
||||
format.html { redirect_to named_context_url(@context, :context_conference_url, @conference.id) }
|
||||
format.json { render :json => @conference.to_json(:permissions => {:user => @current_user, :session => session}) }
|
||||
else
|
||||
|
@ -100,7 +95,12 @@ class ConferencesController < ApplicationController
|
|||
@conference.restart if @conference.ended_at && @conference.grants_right?(@current_user, session, :initiate)
|
||||
log_asset_access(@conference, "conferences", "conferences", 'participate')
|
||||
@conference.touch
|
||||
redirect_to @conference.craft_url(@current_user, session, named_context_url(@context, :context_url, :include_host => true))
|
||||
if url = @conference.craft_url(@current_user, session, named_context_url(@context, :context_url, :include_host => true))
|
||||
redirect_to url
|
||||
else
|
||||
flash[:error] = "There was an error joining the conference"
|
||||
redirect_to named_context_url(@context, :context_url)
|
||||
end
|
||||
else
|
||||
flash[:notice] = "That conference is not currently active"
|
||||
redirect_to named_context_url(@context, :context_url)
|
||||
|
@ -128,7 +128,21 @@ class ConferencesController < ApplicationController
|
|||
redirect_to named_context_url(@context, :context_url)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def get_new_members
|
||||
members = [@current_user]
|
||||
if params[:user] && params[:user][:all] != '1'
|
||||
ids = []
|
||||
params[:user].each do |id, val|
|
||||
ids << id.to_i if val == '1'
|
||||
end
|
||||
members += @context.users.find_all_by_id(ids).to_a
|
||||
else
|
||||
members += @context.users.to_a
|
||||
end
|
||||
members - @conference.invitees
|
||||
end
|
||||
|
||||
def get_conference
|
||||
@conference = @context.web_conferences.find(params[:conference_id] || params[:id])
|
||||
end
|
||||
|
|
|
@ -16,20 +16,6 @@
|
|||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
class PluginsController < ApplicationController
|
||||
before_filter :require_setting_site_admin
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ class DimDimConference < WebConference
|
|||
require 'uri'
|
||||
active = nil
|
||||
begin
|
||||
url = URI.parse("http://#{config['domain']}/dimdim/IsMeetingKeyInUse.action?key=#{self.conference_key}&roomName=#{self.conference_key}")
|
||||
url = URI.parse("http://#{config[:domain]}/dimdim/IsMeetingKeyInUse.action?key=#{self.conference_key}&roomName=#{self.conference_key}")
|
||||
req = Net::HTTP::Get.new(url.path + '?' + url.query)
|
||||
res = Net::HTTP.start(url.host, url.port) {|http|
|
||||
http.request(req)
|
||||
|
@ -46,11 +46,11 @@ class DimDimConference < WebConference
|
|||
end
|
||||
|
||||
def admin_join_url(user, return_to="http://www.instructure.com")
|
||||
"http://#{config['domain']}/dimdim/html/envcheck/connect.action?action=host&email=#{CGI::escape(user.email)}&confKey=#{self.conference_key}&attendeePwd=#{self.attendee_key}&presenterPwd=#{self.presenter_key}&displayName=#{CGI::escape(user.name)}&meetingRoomName=#{self.conference_key}&confName=#{CGI::escape(self.title)}&presenterAV=av&collabUrl=#{CGI::escape("http://#{HostUrl.context_host(self.context)}/dimdim_welcome.html")}&returnUrl=#{CGI::escape(return_to)}"
|
||||
"http://#{config[:domain]}/dimdim/html/envcheck/connect.action?action=host&email=#{CGI::escape(user.email)}&confKey=#{self.conference_key}&attendeePwd=#{self.attendee_key}&presenterPwd=#{self.presenter_key}&displayName=#{CGI::escape(user.name)}&meetingRoomName=#{self.conference_key}&confName=#{CGI::escape(self.title)}&presenterAV=av&collabUrl=#{CGI::escape("http://#{HostUrl.context_host(self.context)}/dimdim_welcome.html")}&returnUrl=#{CGI::escape(return_to)}"
|
||||
end
|
||||
|
||||
def participant_join_url(user, return_to="http://www.instructure.com")
|
||||
"http://#{config['domain']}/dimdim/html/envcheck/connect.action?action=join&email=#{CGI::escape(user.email)}&confKey=#{self.conference_key}&attendeePwd=#{self.attendee_key}&displayName=#{CGI::escape(user.name)}&meetingRoomName=#{self.conference_key}"
|
||||
"http://#{config[:domain]}/dimdim/html/envcheck/connect.action?action=join&email=#{CGI::escape(user.email)}&confKey=#{self.conference_key}&attendeePwd=#{self.attendee_key}&displayName=#{CGI::escape(user.name)}&meetingRoomName=#{self.conference_key}"
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -31,6 +31,9 @@ class PluginSetting < ActiveRecord::Base
|
|||
before_save :validate_posted_settings
|
||||
serialize :settings
|
||||
attr_accessor :posted_settings
|
||||
attr_writer :plugin
|
||||
|
||||
before_save :encrypt_settings
|
||||
|
||||
def validate_uniqueness_of_name?
|
||||
true
|
||||
|
@ -42,10 +45,46 @@ class PluginSetting < ActiveRecord::Base
|
|||
plugin.validate_settings(self, @posted_settings)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def plugin
|
||||
@plugin ||= Canvas::Plugin.find(name.to_s)
|
||||
end
|
||||
|
||||
# dummy value for encrypted fields so that you can still have something in the form (to indicate
|
||||
# it's set) and be able to tell when it gets blanked out.
|
||||
DUMMY_STRING = "~!?3NCRYPT3D?!~"
|
||||
def after_initialize
|
||||
if settings && self.plugin && self.plugin.encrypted_settings
|
||||
self.plugin.encrypted_settings.each do |key|
|
||||
if settings["#{key}_enc".to_sym]
|
||||
settings["#{key}_dec".to_sym] = self.class.decrypt(settings["#{key}_enc"], settings["#{key}_salt".to_sym])
|
||||
settings[key] = DUMMY_STRING
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def encrypt_settings
|
||||
if settings && self.plugin && self.plugin.encrypted_settings
|
||||
self.plugin.encrypted_settings.each do |key|
|
||||
unless settings[key].blank?
|
||||
value = settings.delete(key)
|
||||
settings.delete("#{key}_dec".to_sym)
|
||||
if value == DUMMY_STRING # no change, use what was there previously
|
||||
settings["#{key}_enc".to_sym] = settings_was["#{key}_enc".to_sym]
|
||||
settings["#{key}_salt".to_sym] = settings_was["#{key}_salt".to_sym]
|
||||
else
|
||||
settings["#{key}_enc".to_sym], settings["#{key}_salt".to_sym] = self.class.encrypt(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.settings_for_plugin(name, plugin=nil)
|
||||
if settings = PluginSetting.find_by_name(name.to_s)
|
||||
settings = settings.settings
|
||||
if plugin_setting = PluginSetting.find_by_name(name.to_s)
|
||||
plugin_setting.plugin = plugin
|
||||
settings = plugin_setting.settings
|
||||
else
|
||||
plugin ||= Canvas::Plugin.find(name.to_s)
|
||||
raise Canvas::NoPluginError unless plugin
|
||||
|
@ -55,4 +94,11 @@ class PluginSetting < ActiveRecord::Base
|
|||
settings
|
||||
end
|
||||
|
||||
def self.encrypt(text)
|
||||
Canvas::Security.encrypt_password(text, 'instructure_plugin_setting')
|
||||
end
|
||||
|
||||
def self.decrypt(text, salt)
|
||||
Canvas::Security.decrypt_password(text, salt, 'instructure_plugin_setting')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -27,10 +27,10 @@ class WebConference < ActiveRecord::Base
|
|||
has_many :attendees, :through => :web_conference_participants, :source => :user, :conditions => ['web_conference_participants.participation_type = ?', 'attendee']
|
||||
belongs_to :user
|
||||
validates_length_of :description, :maximum => maximum_text_length, :allow_nil => true, :allow_blank => true
|
||||
|
||||
validates_presence_of :conference_type, :title
|
||||
|
||||
adheres_to_policy
|
||||
before_save :infer_conference_details
|
||||
before_validation :infer_conference_details
|
||||
|
||||
before_create :assign_uuid
|
||||
after_save :touch_context
|
||||
|
@ -92,11 +92,11 @@ class WebConference < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def conference_type=(val)
|
||||
conf_type = WebConference.conference_types.detect{|t| t['name'] == val }
|
||||
conf_type = WebConference.conference_types.detect{|t| t[:conference_type] == val }
|
||||
if conf_type
|
||||
write_attribute(:conference_type, conf_type['name'] )
|
||||
write_attribute(:type, "#{conf_type['type'].classify}Conference" )
|
||||
conf_type['name']
|
||||
write_attribute(:conference_type, conf_type[:conference_type] )
|
||||
write_attribute(:type, conf_type[:class_name] )
|
||||
conf_type[:conference_type]
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
@ -104,7 +104,7 @@ class WebConference < ActiveRecord::Base
|
|||
|
||||
def infer_conference_details
|
||||
infer_conference_settings
|
||||
self.conference_type ||= config && config['name']
|
||||
self.conference_type ||= config && config[:conference_type]
|
||||
self.context_code = "#{self.context_type.underscore}_#{self.context_id}" rescue nil
|
||||
self.duration ||= 30
|
||||
self.user_ids ||= (self.user_id || "").to_s
|
||||
|
@ -204,11 +204,12 @@ class WebConference < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def initiate_conference
|
||||
true
|
||||
end
|
||||
|
||||
def craft_url(user=nil,session=nil,return_to="http://www.instructure.com")
|
||||
user ||= self.user
|
||||
initiate_conference
|
||||
initiate_conference or return nil
|
||||
if (user == self.user || self.grants_right?(user,session,:initiate)) && !active?(true)
|
||||
admin_join_url(user, return_to)
|
||||
else
|
||||
|
@ -248,36 +249,46 @@ class WebConference < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def config
|
||||
@config ||= WebConference.config(self.class.to_s, conference_type)
|
||||
@config ||= WebConference.config(self.class.to_s)
|
||||
end
|
||||
|
||||
def valid_config?
|
||||
if !config
|
||||
false
|
||||
elsif conference_type
|
||||
config['name'] == conference_type
|
||||
else
|
||||
"#{config['type'].classify}Conference" == self.class.to_s
|
||||
config[:class_name] == self.class.to_s
|
||||
end
|
||||
end
|
||||
|
||||
named_scope :active, lambda {
|
||||
}
|
||||
|
||||
|
||||
def self.plugins
|
||||
Canvas::Plugin.all_for_tag(:web_conferencing)
|
||||
end
|
||||
|
||||
def self.conference_types
|
||||
@configs ||= (YAML.load_file(RAILS_ROOT + "/config/web_conferences.yml")[RAILS_ENV] rescue nil) || []
|
||||
plugins.select{ |plugin|
|
||||
plugin.settings &&
|
||||
!plugin.settings.values.all?(&:blank?) &&
|
||||
(klass = (plugin.base || "#{plugin.id.classify}Conference").constantize rescue nil) &&
|
||||
klass < self
|
||||
}.
|
||||
map{ |plugin|
|
||||
plugin.settings.merge(
|
||||
:conference_type => plugin.id.classify,
|
||||
:class_name => (plugin.base || "#{plugin.id.classify}Conference")
|
||||
)
|
||||
}
|
||||
end
|
||||
|
||||
def self.config(type=nil, name=nil)
|
||||
configs ||= conference_types
|
||||
if name
|
||||
config = configs.detect{|c| c['name'] == name }
|
||||
elsif type
|
||||
config = configs.detect{|c| "#{c['type'].classify}Conference" == type }
|
||||
def self.config(class_name=nil)
|
||||
if class_name
|
||||
conference_types.detect{ |c| c[:class_name] == class_name }
|
||||
else
|
||||
config = configs.first
|
||||
conference_types.first
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def self.serialization_excludes; [:uuid]; end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require 'net/http'
|
||||
require 'uri'
|
||||
|
||||
class WimbaConference < WebConference
|
||||
def server
|
||||
config[:domain]
|
||||
end
|
||||
|
||||
def craft_api_url(action, opts={})
|
||||
url = "http://#{server}/admin/api/api.pl"
|
||||
query_string = "function=#{action.to_s}"
|
||||
opts.each do |key, val|
|
||||
query_string += "&#{CGI::escape(key)}=#{CGI::escape(val.to_s)}"
|
||||
end
|
||||
url + "?" + query_string
|
||||
end
|
||||
|
||||
def send_request(action, opts={})
|
||||
headers = {}
|
||||
if action.to_s == 'Init'
|
||||
url = craft_api_url('NoOp', {
|
||||
'AuthType' => 'AuthCookieHandler',
|
||||
'AuthName' => 'Horizon',
|
||||
'credential_0' => config[:user],
|
||||
'credential_1' => config[:password_dec]
|
||||
})
|
||||
else
|
||||
init_session or return nil
|
||||
url = craft_api_url(action, opts)
|
||||
headers['Cookie'] = @auth_cookie
|
||||
end
|
||||
|
||||
uri = URI.parse(url)
|
||||
res = nil
|
||||
# TODO: rework this so that we reuse the same tcp conn (we may call
|
||||
# send_request multiple times in the course of one browser request).
|
||||
Net::HTTP.start(uri.host, uri.port) do |http|
|
||||
http.read_timeout = 10
|
||||
5.times do # follow redirects, but not forever
|
||||
logger.debug "wimba api call: #{uri.path}?#{uri.query}"
|
||||
res = http.request_get("#{uri.path}?#{uri.query}", headers)
|
||||
if res['Set-Cookie'] && res['Set-Cookie'] =~ /AuthCookieHandler_Horizon=.*;/
|
||||
@auth_cookie = headers['Cookie'] = res['Set-Cookie'].sub(/.*(AuthCookieHandler_Horizon=.*?);.*/, '\\1')
|
||||
end
|
||||
if res.is_a?(Net::HTTPRedirection)
|
||||
url = res['location']
|
||||
uri = URI.parse(url)
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
case res
|
||||
when Net::HTTPSuccess
|
||||
api_status = res.body.to_s.split("\n").first.split(" ", 2)
|
||||
if api_status[0] == "100"
|
||||
logger.debug "wimba api success: #{res.body}"
|
||||
return res.body
|
||||
end
|
||||
# any other status indicates an error
|
||||
logger.error "wimba api error #{api_status[1]} (#{api_status[0]})"
|
||||
else
|
||||
logger.error "wimba http error #{res}"
|
||||
end
|
||||
nil
|
||||
rescue Timeout::Error
|
||||
logger.error "wimba timeout error"
|
||||
nil
|
||||
rescue
|
||||
logger.error "wimba unhandled exception #{$!}"
|
||||
nil
|
||||
end
|
||||
|
||||
def add_user_to_conference(user, role='participant')
|
||||
names = user.last_name_first.split(/, /, 2)
|
||||
(
|
||||
send_request('modifyUser', {
|
||||
'target' => wimba_id(user.uuid),
|
||||
'password_type' => 'A',
|
||||
'first_name' => names[1],
|
||||
'last_name' => names[0]}) ||
|
||||
send_request('createUser', {
|
||||
'target' => wimba_id(user.uuid),
|
||||
'password_type' => 'A',
|
||||
'first_name' => names[1],
|
||||
'last_name' => names[0]})
|
||||
) &&
|
||||
send_request('createRole', {
|
||||
'target' => wimba_id,
|
||||
'user_id' => wimba_id(user.uuid),
|
||||
'role_id' => (role == 'participant' ? 'Student' : 'Instructor')
|
||||
})
|
||||
end
|
||||
|
||||
def remove_user_from_conference(user)
|
||||
send_request('deleteRole', {
|
||||
'target' => wimba_id,
|
||||
'user_id' => wimba_id(user.uuid)
|
||||
})
|
||||
end
|
||||
|
||||
def join_url(user)
|
||||
if (res = send_request('getAuthToken', {
|
||||
'target' => wimba_id(user.uuid),
|
||||
'nickname' => user.name.gsub(/[^a-zA-Z0-9]/, '')
|
||||
})) && (token = res.split("\n").detect{|s| s.match(/^authToken/) })
|
||||
"http://#{server}/launcher.cgi.pl?hzA=#{CGI::escape(token.split(/=/, 2).last.chomp)}&room=#{CGI::escape(wimba_id)}"
|
||||
end
|
||||
end
|
||||
|
||||
def admin_join_url(user, return_to="http://www.instructure.com")
|
||||
add_user_to_conference(user, :admin) &&
|
||||
join_url(user)
|
||||
end
|
||||
|
||||
def participant_join_url(user, return_to="http://www.instructure.com")
|
||||
add_user_to_conference(user) &&
|
||||
join_url(user)
|
||||
end
|
||||
|
||||
def initiate_conference
|
||||
return conference_key if conference_key
|
||||
self.conference_key = uuid
|
||||
send_request('createClass', {
|
||||
'target' => wimba_id,
|
||||
'longname' => title[0,50],
|
||||
'preview' => '0' # we want the room open by default
|
||||
}) or return nil
|
||||
save
|
||||
conference_key
|
||||
end
|
||||
|
||||
def init_session
|
||||
if !@auth_cookie
|
||||
send_request('Init') or return false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def conference_status
|
||||
active = nil
|
||||
if res = send_request('statusClass', {'target' => wimba_id})
|
||||
res.split(/\r?\n/).each do |str|
|
||||
key, value = str.strip.split(/=/, 2)
|
||||
if key == 'num_users'
|
||||
return :closed unless value.to_i > 0
|
||||
active = true
|
||||
end
|
||||
if key == 'roomlock'
|
||||
return :closed if value.to_i == 1
|
||||
active = true
|
||||
end
|
||||
end
|
||||
end
|
||||
active ? :active : :closed
|
||||
end
|
||||
|
||||
def wimba_id(id = uuid)
|
||||
# wimba ids are limited to 34 chars. assuming we are using uuids, we can put an "IN" prefix,
|
||||
# which makes distinguishing these users easier.
|
||||
"IN" + id.delete("-")[0,32]
|
||||
end
|
||||
|
||||
end
|
|
@ -40,11 +40,11 @@
|
|||
<div class="content">
|
||||
<table class="formtable" style="width: 100%;">
|
||||
<tr>
|
||||
<td class="nobr" style="width: 5px;"><%= f.label :title, "Name:", :value => "#{@context.name} Conference" %></td>
|
||||
<td class="nobr" style="width: 5px;"><%= f.label :title, "Name:" %></td>
|
||||
<td><%= f.text_field :title, :style => "width: 200px;", :value => "#{@context.name} Conference" %></td>
|
||||
</tr><tr style="<%= hidden unless WebConference.conference_types.length > 1 %>">
|
||||
<td class="nobr" style="width: 5px;"><%= f.label :conference_type, "Type:" %></td>
|
||||
<td><%= f.select :conference_type, WebConference.conference_types.map{|c| [c['name'], c['name']] } %></td>
|
||||
<td><%= f.select :conference_type, WebConference.conference_types.map{|c| [c[:conference_type], c[:conference_type]] } %></td>
|
||||
</tr><tr>
|
||||
<td class="nobr" style="width: 5px;"><%= f.label :duration, "Duration:" %></td>
|
||||
<td><%= f.text_field :duration, :style => "width: 25px;", :value => "60" %> minutes</td>
|
||||
|
@ -112,8 +112,10 @@
|
|||
beforeSubmit: function(data) {
|
||||
var $conference = $(this).prev(".conference");
|
||||
if($conference.length == 0) {
|
||||
$conference = $("#conference_blank").clone(true);
|
||||
$conference = $("#conference_blank").clone(true).attr('id', '');
|
||||
$("#conferences").prepend($conference.show());
|
||||
} else {
|
||||
$conference.show();
|
||||
}
|
||||
$("#add_conference_form").hide();
|
||||
$conference.loadingImage();
|
||||
|
@ -137,22 +139,36 @@
|
|||
$conference.find(".join_conference_link").show();
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function(data, $conference) {
|
||||
$conference.loadingImage('remove');
|
||||
$conference.remove();
|
||||
$(this).show();
|
||||
}
|
||||
});
|
||||
$(".edit_conference_link, .add_conference_link").click(function(event) {
|
||||
event.preventDefault();
|
||||
var $form = $("#add_conference_form");
|
||||
if ($form.is(":visible")) {
|
||||
if (confirm('It looks like you are already editing another conference. Do you wish to continue? Any unsaved changes will be lost.')) {
|
||||
$form.prev(".conference").show();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
var edit = $(this).hasClass('edit_conference_link');
|
||||
var $conference = $(this).parents(".conference")
|
||||
if(!edit) {
|
||||
$conference = $("#conference_blank");
|
||||
}
|
||||
var $form = $("#add_conference_form");
|
||||
var data = $conference.getTemplateData({
|
||||
textValues: ['title', 'duration', 'description', 'user_ids']
|
||||
textValues: ['title', 'duration', 'description', 'user_ids', 'conference_type']
|
||||
});
|
||||
if(edit) {
|
||||
$form.find("h3").text("Edit Conference Details");
|
||||
$form.find("span.title").text("Edit Conference Details");
|
||||
$form.find("button[type=submit]").text("Update Conference");
|
||||
$form.attr('method', 'PUT').attr('action', $(this).attr('href'));
|
||||
$form.find('select').attr("disabled", true);
|
||||
$conference.after($form);
|
||||
$form.find("#members_list").show().find(":checkbox").attr('checked', false).end().end()
|
||||
.find(".all_users_checkbox").attr('checked', false);
|
||||
|
@ -162,10 +178,12 @@
|
|||
$form.find("#members_list .member.user_" + id).find(":checkbox").attr('checked', true);
|
||||
}
|
||||
} else {
|
||||
$form.find("h3").text("Start a New Conference");
|
||||
$form.find("span.title").text("Start a New Conference");
|
||||
$form.find("button[type=submit]").text("Create Conference");
|
||||
$form.attr('method', 'POST').attr('action', $(".add_conference_url").attr('href'));
|
||||
$form.find("all_users_checkbox").attr('checked', true).end()
|
||||
.find("#members_list").hide().attr('checked', true).find(":checkbox").attr('checked', false);
|
||||
$form.find('select').attr("disabled", false);
|
||||
$form.find(".all_users_checkbox").attr('checked', true).end()
|
||||
.find("#members_list").hide().find(":checkbox").attr('checked', false);
|
||||
$("#conferences").before($form);
|
||||
}
|
||||
$form.fillFormData(data, {object_name: 'web_conference'});
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<% fields_for :settings, OpenObject.new(settings) do |f| %>
|
||||
<table style="width: 500px;" class="formtable">
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<p>
|
||||
You will need access to a Dim Dim instance.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><%= f.label :domain, "Domain:" %></td>
|
||||
<td>
|
||||
<%= f.text_field :domain %>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<% end %>
|
|
@ -0,0 +1,29 @@
|
|||
<% fields_for :settings, OpenObject.new(settings) do |f| %>
|
||||
<table style="width: 500px;" class="formtable">
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<p>
|
||||
You will need access to a <a href="http://wimba.com">Wimba</a> account.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><%= f.label :domain, "Domain:" %></td>
|
||||
<td>
|
||||
<%= f.text_field :domain %>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><%= f.label :user, "Username:" %></td>
|
||||
<td>
|
||||
<%= f.text_field :user %>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><%= f.label :password, "Password:" %></td>
|
||||
<td>
|
||||
<%= f.password_field :password, :autocomplete => false %>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<% end %>
|
|
@ -26,6 +26,7 @@
|
|||
<% end %>
|
||||
<div style="display: none;" class="duration"><%= conference.duration.to_i rescue "60" %></div>
|
||||
<div style="display: none;" class="user_ids"><%= Array(conference.user_ids).join(",") rescue nbsp %></div>
|
||||
<div style="display: none;" class="conference_type"><%= conference && conference.conference_type %></div>
|
||||
</div>
|
||||
<div class="description">
|
||||
<%= conference.description rescue "" %>
|
||||
|
|
|
@ -60,7 +60,6 @@ Rails::Initializer.run do |config|
|
|||
config.middleware.use("RequestContextGenerator")
|
||||
config.to_prepare do
|
||||
require_dependency 'canvas/plugins/default_plugins'
|
||||
require_dependency 'canvas/plugins/validators/kaltura_validator'
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
production:
|
||||
- name: DimDim
|
||||
type: dim_dim
|
||||
domain: dimdim.com
|
||||
|
||||
development:
|
||||
- name: DimDim
|
||||
type: dim_dim
|
||||
domain: dimdim.com
|
||||
|
||||
test:
|
||||
- name: DimDim
|
||||
type: dim_dim
|
||||
domain: dimdim.com
|
|
@ -1,3 +1,21 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require 'canvas'
|
||||
|
||||
module Canvas
|
||||
|
@ -21,7 +39,9 @@ module Canvas
|
|||
:author_website=>nil,
|
||||
:version=>nil,
|
||||
:settings_partial=>nil,
|
||||
:settings=>nil
|
||||
:settings=>nil,
|
||||
:encrypted_settings=>nil,
|
||||
:base=>nil
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
|
@ -42,7 +62,11 @@ module Canvas
|
|||
# cache this properly across all web servers
|
||||
saved_settings
|
||||
end
|
||||
|
||||
|
||||
def encrypted_settings
|
||||
@meta[:encrypted_settings]
|
||||
end
|
||||
|
||||
def description
|
||||
@meta[:description]
|
||||
end
|
||||
|
@ -75,6 +99,11 @@ module Canvas
|
|||
!meta[:settings_partial].blank?
|
||||
end
|
||||
|
||||
# base class/module for this plugin
|
||||
def base
|
||||
@meta[:base]
|
||||
end
|
||||
|
||||
# Let the plugin do any validations necessary.
|
||||
# If the plugin has defined a validator, call
|
||||
# the :validate method on that validator. If it
|
||||
|
|
|
@ -1,9 +1,31 @@
|
|||
Dir.glob('lib/canvas/plugins/validators/*').each do |file|
|
||||
require_dependency file
|
||||
end
|
||||
|
||||
Canvas::Plugin.register('kaltura', nil, {
|
||||
:description => 'Kaltura video/audio recording and playback',
|
||||
:website => 'http://www.instructure.com',
|
||||
:website => 'http://corp.kaltura.com',
|
||||
:author => 'instructure',
|
||||
:author_website => 'http://www.instructure.com',
|
||||
:version => 1.0,
|
||||
:settings_partial => 'plugins/kaltura_settings',
|
||||
:validator => 'KalturaValidator'
|
||||
})
|
||||
})
|
||||
Canvas::Plugin.register('dim_dim', :web_conferencing, {
|
||||
:description => 'DimDim web conferencing support',
|
||||
:website => 'http://www.dimdim.com',
|
||||
:author => 'instructure',
|
||||
:author_website => 'http://www.instructure.com',
|
||||
:version => 1.0,
|
||||
:settings_partial => 'plugins/dim_dim_settings'
|
||||
})
|
||||
Canvas::Plugin.register('wimba', :web_conferencing, {
|
||||
:description => 'Wimba web conferencing support',
|
||||
:website => 'http://www.wimba.com',
|
||||
:author => 'instructure',
|
||||
:author_website => 'http://www.instructure.com',
|
||||
:version => 1.0,
|
||||
:settings_partial => 'plugins/wimba_settings',
|
||||
:validator => 'WimbaValidator',
|
||||
:encrypted_settings => [:password]
|
||||
})
|
||||
|
|
|
@ -1,3 +1,21 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module Canvas::Plugins::Validators::KalturaValidator
|
||||
def self.validate(settings, plugin_setting)
|
||||
if settings.map(&:last).all?(&:blank?)
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module Canvas::Plugins::Validators::WimbaValidator
|
||||
def self.validate(settings, plugin_setting)
|
||||
if settings.map(&:last).all?(&:blank?)
|
||||
{}
|
||||
else
|
||||
if settings.size != 3 || settings.map(&:last).any?(&:blank?)
|
||||
plugin_setting.errors.add_to_base('All fields are required')
|
||||
false
|
||||
else
|
||||
settings.slice(:domain, :user, :password)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,3 @@
|
|||
dim_dim_plugin:
|
||||
name: dim_dim
|
||||
settings: "--- \n:domain: dimdim.instructure.com\n"
|
|
@ -22,7 +22,7 @@ require File.expand_path(File.dirname(__FILE__) + '/messages_helper')
|
|||
describe 'web_conference_invitation.email' do
|
||||
it "should render" do
|
||||
course_model(:reusable => true)
|
||||
@object = @course.web_conferences.create!
|
||||
@object = @course.web_conferences.create!(:conference_type => 'DimDim')
|
||||
generate_message(:web_conference_invitation, :email, @object)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,7 +22,7 @@ require File.expand_path(File.dirname(__FILE__) + '/messages_helper')
|
|||
describe 'web_conference_invitation.facebook' do
|
||||
it "should render" do
|
||||
course_model(:reusable => true)
|
||||
@object = @course.web_conferences.create!
|
||||
@object = @course.web_conferences.create!(:conference_type => 'DimDim')
|
||||
generate_message(:web_conference_invitation, :facebook, @object)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,7 +22,7 @@ require File.expand_path(File.dirname(__FILE__) + '/messages_helper')
|
|||
describe 'web_conference_invitation.sms' do
|
||||
it "should render" do
|
||||
course_model(:reusable => true)
|
||||
@object = @course.web_conferences.create!
|
||||
@object = @course.web_conferences.create!(:conference_type => 'DimDim')
|
||||
generate_message(:web_conference_invitation, :sms, @object)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,7 +22,7 @@ require File.expand_path(File.dirname(__FILE__) + '/messages_helper')
|
|||
describe 'web_conference_invitation.summary' do
|
||||
it "should render" do
|
||||
course_model(:reusable => true)
|
||||
@object = @course.web_conferences.create!
|
||||
@object = @course.web_conferences.create!(:conference_type => 'DimDim')
|
||||
generate_message(:web_conference_invitation, :summary, @object)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,19 +20,41 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
|
|||
|
||||
describe WebConference do
|
||||
before(:all) do
|
||||
WebConference.instance_variable_set('@configs', [{
|
||||
'type' => 'dim_dim',
|
||||
'name' => 'dimdim',
|
||||
'domain' => 'dimdim.instructure.com'
|
||||
}])
|
||||
WebConference.instance_eval do
|
||||
def plugins
|
||||
[OpenObject.new(:id => "dim_dim", :settings => {:domain => "dimdim.instructure.com"}),
|
||||
OpenObject.new(:id => "broken_plugin", :settings => {:foo => :bar})]
|
||||
end
|
||||
end
|
||||
end
|
||||
after(:all) do
|
||||
WebConference.instance_eval do
|
||||
def plugins; Canvas::Plugin.all_for_tag(:web_conferencing); end
|
||||
end
|
||||
end
|
||||
|
||||
context "broken_plugin" do
|
||||
it "should return false on valid_config? if no matching config" do
|
||||
WebConference.new.valid_config?.should be_false
|
||||
conf = WebConference.new
|
||||
conf.conference_type = 'bad_type'
|
||||
conf.valid_config?.should be_false
|
||||
end
|
||||
|
||||
it "should return false on valid_config? if plugin subclass is broken/missing" do
|
||||
conf = WebConference.new
|
||||
conf.conference_type = "broken_plugin"
|
||||
conf.valid_config?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context "dim_dim" do
|
||||
it "should correctly retrieve a config hash" do
|
||||
conference = DimDimConference.new
|
||||
config = conference.config
|
||||
config.should_not be_nil
|
||||
config['type'].should eql('dim_dim')
|
||||
config['name'].should eql('dimdim')
|
||||
config[:conference_type].should eql('DimDim')
|
||||
config[:class_name].should eql('DimDimConference')
|
||||
end
|
||||
|
||||
it "should correctly generate join urls" do
|
||||
|
@ -47,14 +69,7 @@ describe WebConference do
|
|||
|
||||
it "should confirm valid config" do
|
||||
DimDimConference.new.valid_config?.should be_true
|
||||
DimDimConference.new(:conference_type => "dimdim").valid_config?.should be_true
|
||||
end
|
||||
|
||||
it "should return false on valid_config? if no matching config" do
|
||||
WebConference.new.valid_config?.should be_false
|
||||
conf = DimDimConference.new
|
||||
conf.write_attribute(:conference_type, 'bad_type')
|
||||
conf.valid_config?.should be_false
|
||||
DimDimConference.new(:conference_type => "DimDim").valid_config?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
#
|
||||
# Copyright (C) 2011 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
|
||||
|
||||
describe WimbaConference do
|
||||
before(:all) do
|
||||
WebConference.instance_eval do
|
||||
def plugins
|
||||
[OpenObject.new(:id => "wimba", :settings => {:domain => "wimba.test"})]
|
||||
end
|
||||
end
|
||||
|
||||
WimbaConference.class_eval do
|
||||
# set up a simple mock that mimics basic API functionality
|
||||
def send_request(action, opts={})
|
||||
@mocked_users ||= {:added => [], :admins => [], :joined => []}
|
||||
extra = ''
|
||||
if action == 'Init'
|
||||
@auth_cookie = 'authcookie=secret'
|
||||
else
|
||||
return nil unless init_session
|
||||
return nil unless @auth_cookie == 'authcookie=secret'
|
||||
case action
|
||||
when 'modifyUser'
|
||||
return nil unless @mocked_users[:added].include?(opts['target'])
|
||||
when 'createUser'
|
||||
@mocked_users[:added] << opts['target']
|
||||
when 'createRole'
|
||||
@mocked_users[:admins] << opts['user_id'] if opts['role_id'] == 'Instructor'
|
||||
when 'getAuthToken'
|
||||
return nil unless @mocked_users[:added].include?(opts['target'])
|
||||
return nil if @mocked_users[:joined].empty? && !@mocked_users[:admins].include?(opts['target'])
|
||||
@mocked_users[:joined] << opts['target']
|
||||
extra = "\nauthToken=s3kr1tfor#{opts['target']}\nuser_id=#{opts['target']}\n=END RECORD"
|
||||
when 'statusClass'
|
||||
extra = "\nnum_users=#{@mocked_users[:joined].size}\nroomlock=\n=END RECORD"
|
||||
end
|
||||
end
|
||||
"100 OK#{extra}"
|
||||
end
|
||||
end
|
||||
end
|
||||
after(:all) do
|
||||
WebConference.instance_eval do
|
||||
def plugins; Canvas::Plugin.all_for_tag(:web_conferencing); end
|
||||
end
|
||||
end
|
||||
before(:each) do
|
||||
user_model
|
||||
email = "email@email.com"
|
||||
@user.stub!(:email).and_return(email)
|
||||
end
|
||||
|
||||
context "wimba" do
|
||||
it "should correctly retrieve a config hash" do
|
||||
conference = WimbaConference.new
|
||||
config = conference.config
|
||||
config.should_not be_nil
|
||||
config[:conference_type].should eql('Wimba')
|
||||
config[:class_name].should eql('WimbaConference')
|
||||
end
|
||||
|
||||
it "should confirm valid config" do
|
||||
WimbaConference.new.valid_config?.should be_true
|
||||
WimbaConference.new(:conference_type => "Wimba").valid_config?.should be_true
|
||||
end
|
||||
|
||||
it "should be active if an admin has joined" do
|
||||
conference = WimbaConference.create!(:title => "my conference", :user => @user)
|
||||
# this makes it active
|
||||
conference.initiate_conference
|
||||
conference.admin_join_url(@user).should_not be_nil
|
||||
|
||||
conference.conference_status.should eql(:active)
|
||||
conference.participant_join_url(@user).should_not be_nil
|
||||
end
|
||||
|
||||
it "should be closed if it has not been initiated" do
|
||||
conference = WimbaConference.create!(:title => "my conference", :user => @user)
|
||||
conference.conference_status.should eql(:closed)
|
||||
conference.participant_join_url(@user).should be_nil
|
||||
end
|
||||
|
||||
it "should be closed if no admins have joined" do
|
||||
conference = WimbaConference.create!(:title => "my conference", :user => @user)
|
||||
conference.initiate_conference
|
||||
conference.conference_status.should eql(:closed)
|
||||
conference.participant_join_url(@user).should be_nil
|
||||
end
|
||||
|
||||
it "should correctly generate join urls" do
|
||||
conference = WimbaConference.create!(:title => "my conference", :user => @user)
|
||||
conference.initiate_conference
|
||||
# join urls for admins and participants look the same (though token will vary by user), since
|
||||
# someone's admin/participant-ness is negotiated beforehand through api calls and isn't
|
||||
# reflected in the token/url
|
||||
join_url = "http://wimba.test/launcher.cgi.pl?hzA=s3kr1tfor#{conference.wimba_id(@user.uuid)}&room=#{conference.wimba_id}"
|
||||
conference.admin_join_url(@user).should eql(join_url)
|
||||
conference.participant_join_url(@user).should eql(join_url)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -48,6 +48,7 @@ Spec::Runner.configure do |config|
|
|||
config.use_transactional_fixtures = true
|
||||
config.use_instantiated_fixtures = false
|
||||
config.fixture_path = RAILS_ROOT + '/spec/fixtures/'
|
||||
config.global_fixtures = :plugin_settings
|
||||
|
||||
config.include Webrat::Matchers, :type => :views
|
||||
|
||||
|
|
Loading…
Reference in New Issue