add tool_id and icon url to context_external_tools

tool_id can be used to associate multiple context_external_tools
with a single third party tool (i.e. to see how many installs
there are of the youtube tool). When tools are launched Canvas
will also track a custom ganalytics event with this tool id.

icon_url is an attribute that can be set in standard LTI
config that we were ignoring before. In the future this may
be added to the UI when picking external tools.

test plan:
- find an external tool with tool_id and icon configs
  (there are some examples on lti-examples.heroku.com)
- configure an external tool using this xml
- confirm that tool_id and icon_url were correctly set
  (tool.tool_id and tool.settings[:icon_url])

- launch an external tool from within a course
- make sure that nothing breaks

Change-Id: If8d6386e8a919fa70eacd46b4fa6b68ade4b5c7b
Reviewed-on: https://gerrit.instructure.com/10568
Reviewed-by: Brian Whitmer <brian@instructure.com>
Tested-by: Jenkins <jenkins@instructure.com>
This commit is contained in:
Brian Whitmer 2012-05-05 11:18:08 -07:00
parent 375b3ea32a
commit 363fb4c9df
10 changed files with 59 additions and 5 deletions

View File

@ -7,7 +7,7 @@ class ContextExternalTool < ActiveRecord::Base
:name, :description, :custom_fields, :custom_fields_string,
:course_navigation, :account_navigation, :user_navigation,
:resource_selection, :editor_button,
:config_type, :config_url, :config_xml
:config_type, :config_url, :config_xml, :tool_id
validates_presence_of :name
validates_presence_of :consumer_key
validates_presence_of :shared_secret
@ -197,6 +197,7 @@ class ContextExternalTool < ActiveRecord::Base
settings.delete(:account_navigation) if settings[:account_navigation] && (!settings[:account_navigation][:url])
settings.delete(:resource_selection) if settings[:resource_selection] && (!settings[:resource_selection][:url] || !settings[:resource_selection][:selection_width] || !settings[:resource_selection][:selection_height])
settings.delete(:editor_button) if settings[:editor_button] && (!settings[:editor_button][:url] || !settings[:editor_button][:icon_url])
settings[:icon_url] ||= settings[:editor_button][:icon_url] if settings[:editor_button] && settings[:editor_button][:icon_url]
[:resource_selection, :editor_button].each do |type|
if settings[type]
settings[type][:selection_width] = settings[type][:selection_width].to_i
@ -413,6 +414,7 @@ class ContextExternalTool < ActiveRecord::Base
item.migration_id = hash[:migration_id]
item.name = hash[:title]
item.description = hash[:description]
item.tool_id = hash[:tool_id]
item.url = hash[:url] unless hash[:url].blank?
item.domain = hash[:domain] unless hash[:domain].blank?
item.privacy_level = hash[:privacy_level] || 'name_only'

View File

@ -21,6 +21,7 @@ class DeveloperKey < ActiveRecord::Base
belongs_to :account
has_many :page_views
has_many :access_tokens
has_many :context_external_tools, :primary_key => 'tool_id', :foreign_key => 'tool_id'
attr_accessible :api_key, :name, :user, :account

View File

@ -16,7 +16,7 @@
</script>
<% end %>
<form action="<%= @resource_url %>" method="POST" <%= raw("target='#{@target || 'tool_content'}'") unless @self_target %> id="tool_form" class="<%= 'new_tab' if @tag.try(:new_tab) %>">
<form action="<%= @resource_url %>" method="POST" <%= raw("target='#{@target || 'tool_content'}'") unless @self_target %> id="tool_form" class="<%= 'new_tab' if @tag.try(:new_tab) %>" data-tool-id="<%= @tool.tool_id || 'unknown' %>">
<% @tool_settings.each do |key, value| %>
<%= hidden_field_tag key, value %>
<% end %>

View File

@ -0,0 +1,20 @@
class AddToolIdToExternalTools < ActiveRecord::Migration
tag :predeploy
def self.up
# using tool_id instead of developer_key.id lets us
# use the same keys as lti-examples.heroku.com for
# tying multiple context_external_tools to the
# same third-party tool
add_column :context_external_tools, :tool_id, :string
add_index :context_external_tools, [:tool_id]
add_column :developer_keys, :tool_id, :string
add_index :developer_keys, [:tool_id], :unique => true
end
def self.down
remove_column :context_external_tools, :tool_id
remove_index :context_external_tools, [:tool_id]
remove_column :developer_keys, :tool_id
remove_index :developer_keys, [:tool_id]
end
end

View File

@ -61,6 +61,7 @@ module CC
elsif tool.url =~ %r{https://}
blti_node.blti :secure_launch_url, tool.url
end
blti_node.blti(:icon, tool.settings[:icon_url]) if tool.settings[:icon_url]
blti_node.blti :vendor do |v_node|
v_node.lticp :code, 'unknown'
v_node.lticp :name, 'unknown'
@ -75,6 +76,7 @@ module CC
end
blti_node.blti(:extensions, :platform => CC::CCHelper::CANVAS_PLATFORM) do |ext_node|
ext_node.lticm(:property, tool.tool_id, 'name' => 'tool_id') if tool.tool_id
ext_node.lticm :property, tool.workflow_state, 'name' => 'privacy_level'
ext_node.lticm(:property, tool.domain, 'name' => 'domain') unless tool.domain.blank?
if for_course_copy

View File

@ -79,12 +79,16 @@ module CC::Importer
tool[:domain] = ext[:custom_fields].delete 'domain'
tool[:consumer_key] = ext[:custom_fields].delete 'consumer_key'
tool[:shared_secret] = ext[:custom_fields].delete 'shared_secret'
tool[:tool_id] = ext[:custom_fields].delete 'tool_id'
tool[:settings] = ext[:custom_fields]
else
tool[:extensions] << ext
end
end
if icon = get_node_val(doc, "#{blti}|icon")
tool[:settings] ||= {}
tool[:settings][:icon_url] = icon
end
tool
end

View File

@ -15,7 +15,7 @@
* 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/>.
*/
define(['jquery'], function($) {
define(['jquery', 'jquery.google-analytics'], function($) {
if(!$("#tool_form").hasClass('new_tab')) {
$("#content").addClass('padless');
@ -32,6 +32,10 @@ if(!$("#tool_form").hasClass('new_tab')) {
$(this).find(".load_tab,.tab_loaded").toggle();
});
}
var toolName = $("#tool_form").attr('data-tool-id') || "unknown";
$.trackEvent('tool_launch', toolName);
$("#tool_form:not(.new_tab)").submit().hide();
$(document).ready(function() {
if($("#tool_content").length) {

View File

@ -146,12 +146,14 @@ describe "Canvas Cartridge importing" do
tool1.privacy_level = 'name_only'
tool1.consumer_key = 'haha'
tool1.shared_secret = "don't share me"
tool1.tool_id = "test_tool"
tool1.settings[:custom_fields] = {"key1" => "value1", "key2" => "value2"}
tool1.settings[:user_navigation] = {:url => "http://www.example.com", :text => "hello", :labels => {'en' => 'hello', 'es' => 'hola'}, :extra => 'extra'}
tool1.settings[:course_navigation] = {:url => "http://www.example.com", :text => "hello", :labels => {'en' => 'hello', 'es' => 'hola'}, :default => 'disabled', :visibility => 'members', :extra => 'extra'}
tool1.settings[:account_navigation] = {:url => "http://www.example.com", :text => "hello", :labels => {'en' => 'hello', 'es' => 'hola'}, :extra => 'extra'}
tool1.settings[:resource_selection] = {:url => "http://www.example.com", :text => "hello", :labels => {'en' => 'hello', 'es' => 'hola'}, :selection_width => 100, :selection_height => 50, :extra => 'extra'}
tool1.settings[:editor_button] = {:url => "http://www.example.com", :text => "hello", :labels => {'en' => 'hello', 'es' => 'hola'}, :selection_width => 100, :selection_height => 50, :icon_url => "http://www.example.com", :extra => 'extra'}
tool1.settings[:icon_url] = "http://www.example.com/favicon.ico"
tool1.save!
tool2 = @copy_from.context_external_tools.new
tool2.domain = 'example.com'
@ -190,6 +192,8 @@ describe "Canvas Cartridge importing" do
t1.domain.should == nil
t1.consumer_key.should == 'fake'
t1.shared_secret.should == 'fake'
t1.tool_id.should == 'test_tool'
t1.settings[:icon_url].should == 'http://www.example.com/favicon.ico'
[:user_navigation, :course_navigation, :account_navigation].each do |type|
t1.settings[type][:url].should == "http://www.example.com"
t1.settings[type][:text].should == "hello"
@ -228,6 +232,8 @@ describe "Canvas Cartridge importing" do
t2.workflow_state.should == tool2.workflow_state
t2.consumer_key.should == 'fake'
t2.shared_secret.should == 'fake'
t2.tool_id.should be_nil
t2.settings[:icon_url].should be_nil
t2.settings[:user_navigation].should be_nil
t2.settings[:course_navigation].should be_nil
t2.settings[:account_navigation].should be_nil

View File

@ -126,7 +126,7 @@ describe "Standard Common Cartridge importing" do
et.name.should == "BLTI Test"
et.url.should == 'http://www.imsglobal.org/developers/BLTI/tool.php'
et.settings[:custom_fields].should == {"key1"=>"value1", "key2"=>"value2"}
et.settings[:vendor_extensions].should == [{:platform=>"my.lms.com", :custom_fields=>{"key"=>"value"}}, {:platform=>"your.lms.com", :custom_fields=>{"key"=>"value", "key2"=>"value2"}}]
et.settings[:vendor_extensions].should == [{:platform=>"my.lms.com", :custom_fields=>{"key"=>"value"}}, {:platform=>"your.lms.com", :custom_fields=>{"key"=>"value", "key2"=>"value2"}}].map(&:with_indifferent_access)
@migration.warnings.member?("The security parameters for the external tool \"#{et.name}\" need to be set in Course Settings.").should be_true
end

View File

@ -335,6 +335,21 @@ describe ContextExternalTool do
tool.has_editor_button.should be_true
end
it "should allow setting tool_id and icon_url" do
tool = new_external_tool
tool.tool_id = "new_tool"
tool.settings[:icon_url] = "http://www.example.com/favicon.ico"
tool.save
tool.tool_id.should == "new_tool"
tool.settings[:icon_url].should == "http://www.example.com/favicon.ico"
end
it "should use editor button's icon_url if none is set on the tool" do
tool = new_external_tool
tool.settings = {:editor_button => {:url => "http://www.example.com", :icon_url => "http://www.example.com/favicon.ico", :selection_width => 100, :selection_height => 100}}
tool.save
tool.settings[:icon_url].should == "http://www.example.com/favicon.ico"
end
end
describe "standardize_url" do