#{message}")
+
diff --git a/app/coffeescripts/views/ExternalTools/IndexView.coffee b/app/coffeescripts/views/ExternalTools/IndexView.coffee
new file mode 100644
index 00000000000..23a8ae5ca3f
--- /dev/null
+++ b/app/coffeescripts/views/ExternalTools/IndexView.coffee
@@ -0,0 +1,70 @@
+define [
+ 'underscore'
+ 'jquery'
+ 'jst/ExternalTools/IndexView'
+ 'compiled/views/ExternalTools/EditView'
+ 'compiled/views/PaginatedView'
+ 'i18n!external_tools'
+], (_, $, template, EditView, PaginatedView, I18n) ->
+
+ class IndexView extends PaginatedView
+
+ template: template
+
+ events:
+ 'click [data-delete-external-tool]': 'deleteExternalToolHandler'
+ 'click [data-edit-external-tool]': 'editExternalToolHandler'
+ 'click .add_tool_link': 'addTool'
+
+ initialize: ->
+ super
+ @collection.on 'sync', @render, this
+ @collection.on 'reset', @render, this
+ @collection.on 'destroy', @render, this
+ @render()
+
+ deleteExternalToolHandler: (e) =>
+ id = @$(e.target).closest('a').data('delete-external-tool')
+ @confirmDelete =>
+ @collection.get(id).destroy()
+
+ confirmDelete: (deleteFunc) ->
+ msg = I18n.t 'remove_tool',
+ "Are you sure you want to remove this tool?
+ Any courses using this tool will no longer work."
+ dialog = $("
#{msg}
").dialog
+ modal: true,
+ resizable: false
+ title: I18n.t 'are_you_sure', 'Are you sure?'
+ buttons: [
+ text: I18n.t 'buttons.cancel', 'Cancel'
+ click: => dialog.dialog 'close'
+ ,
+ text: I18n.t 'buttons.delete', 'Delete'
+ click: =>
+ deleteFunc()
+ dialog.dialog 'close'
+ ]
+
+ editExternalToolHandler: (e) =>
+ id = @$(e.target).closest('a').data('edit-external-tool')
+ new EditView(model: @collection.get(id)).render()
+
+ addTool: =>
+ @collection.add({}, silent: true)
+ new EditView(model: @collection.last()).render()
+
+ toJSON: ->
+ extras = [
+ {extension_type: 'editor_button', text: I18n.t 'editor_button_configured', 'Editor button configured'}
+ {extension_type: 'resource_selection', text: I18n.t 'resource_selection_configured', 'Resource selection configured'}
+ {extension_type: 'course_navigation', text: I18n.t 'course_navigation_configured', 'Course navigation configured'}
+ {extension_type: 'account_navigation', text: I18n.t 'account_navigation_configured', 'Account navigation configured'}
+ {extension_type: 'user_navigation', text: I18n.t 'user_navigation_configured', 'User navigation configured'}
+ {extension_type: 'homework_submission', text: I18n.t 'homework_submission_configured', 'Homework submission configured'}
+ ]
+
+ json = super
+ for tool in json
+ tool.extras = (extra for extra in extras when tool[extra.extension_type]?)
+ json
diff --git a/app/coffeescripts/views/ValidatedFormView.coffee b/app/coffeescripts/views/ValidatedFormView.coffee
index 6b55e489b88..c2087792408 100644
--- a/app/coffeescripts/views/ValidatedFormView.coffee
+++ b/app/coffeescripts/views/ValidatedFormView.coffee
@@ -4,10 +4,11 @@ define [
'jquery'
'underscore'
'compiled/fn/preventDefault'
+ 'i18n!errors'
'jquery.toJSON'
'jquery.disableWhileLoading'
'jquery.instructure_forms'
-], (Backbone, ValidatedMixin, $, _) ->
+], (Backbone, ValidatedMixin, $, _, preventDefault, I18n) ->
##
# Sets model data from a form, saves it, and displays errors returned in a
@@ -121,3 +122,33 @@ define [
$.parseJSON(response.responseText).errors
catch error
{}
+
+ translations:
+ required: I18n.t "required", "Required"
+ blank: I18n.t "blank", "Required"
+
+ ##
+ # Errors are displayed relative to the field to which they belong. If
+ # the key of the error in the response doesn't match the name attribute
+ # of the form input element, configure a selector here.
+ #
+ # For example, given a form field like this:
+ #
+ #
+ #
+ # and an error response like this:
+ #
+ # {errors: { first_name: {...} }}
+ #
+ # you would do this:
+ #
+ # fieldSelectors:
+ # first_name: '[name=user[first_name]]'
+ fieldSelectors: null
+
+ findField: (field) ->
+ selector = @fieldSelectors?[field] or "[name='#{field}']"
+ $el = @$(selector)
+ if $el.data('rich_text')
+ $el = $el.next('.mceEditor').find(".mceIframeContainer")
+ $el
diff --git a/app/models/context_external_tool.rb b/app/models/context_external_tool.rb
index 84203fcad07..ba093365844 100644
--- a/app/models/context_external_tool.rb
+++ b/app/models/context_external_tool.rb
@@ -12,6 +12,9 @@ class ContextExternalTool < ActiveRecord::Base
validates_length_of :name, :maximum => maximum_string_length
validates_presence_of :consumer_key
validates_presence_of :shared_secret
+ validates_presence_of :config_url, :if => lambda { |t| t.config_type == "by_url" }
+ validates_presence_of :config_xml, :if => lambda { |t| t.config_type == "by_xml" }
+ validates_length_of :domain, :maximum => 253, :allow_blank => true
validate :url_or_domain_is_set
serialize :settings
attr_accessor :config_type, :config_url, :config_xml
@@ -76,15 +79,10 @@ class ContextExternalTool < ActiveRecord::Base
settings[:text] || name || "External Tool"
end
- def xml_error(error)
- @xml_error = error
- end
-
def check_for_xml_error
- if @xml_error
- errors.add_to_base(@xml_error)
- false
- end
+ (@config_errors || []).each { |attr,msg|
+ errors.add attr, msg
+ }
end
protected :check_for_xml_error
@@ -154,18 +152,37 @@ class ContextExternalTool < ActiveRecord::Base
rescue CC::Importer::BLTIConverter::CCImportError => e
tool_hash = {:error => e.message}
end
+
+ @config_errors = []
+ error_field = config_type == 'by_xml' ? 'config_xml' : 'config_url'
+
+ converter = CC::Importer::BLTIConverter.new
+ tool_hash = if config_type == 'by_url'
+ uri = URI.parse(config_url)
+ raise URI::InvalidURIError unless uri.host && uri.port
+ converter.retrieve_and_convert_blti_url(config_url)
+ else
+ converter.convert_blti_xml(config_xml)
+ end
+
real_name = self.name
if tool_hash[:error]
- xml_error(tool_hash[:error])
+ @config_errors << [error_field, tool_hash[:error]]
else
- ContextExternalTool.import_from_migration(tool_hash, self.context, self)
+ ContextExternalTool.import_from_migration(tool_hash, context, self)
end
self.name = real_name unless real_name.blank?
+ rescue CC::Importer::BLTIConverter::CCImportError => e
+ @config_errors << [error_field, e.message]
+ rescue URI::InvalidURIError
+ @config_errors << [:config_url, "Invalid URL"]
+ rescue ActiveRecord::RecordInvalid => e
+ @config_errors += Array(e.record.errors)
end
def custom_fields_string=(str)
hash = {}
- str.split(/\n/).each do |line|
+ str.split(/[\r\n]+/).each do |line|
key, val = line.split(/=/)
hash[key] = val if key.present? && val.present?
end
diff --git a/app/stylesheets/external_tools.sass b/app/stylesheets/external_tools.sass
index c65688a5364..a3bf6fd939e 100644
--- a/app/stylesheets/external_tools.sass
+++ b/app/stylesheets/external_tools.sass
@@ -21,7 +21,6 @@
.content
padding: 0 20px 5px
.extras
- display: none
div
font-style: italic
font-size: 0.9em
@@ -52,4 +51,9 @@
&.has_account_navigation
div.account_navigation
display: block
+ table
+ td:first-child
+ width: 100px
+
+
diff --git a/app/views/external_tools/_external_tools.html.erb b/app/views/external_tools/_external_tools.html.erb
index 954dd5e252a..a0eedfffc83 100644
--- a/app/views/external_tools/_external_tools.html.erb
+++ b/app/views/external_tools/_external_tools.html.erb
@@ -1,94 +1,3 @@
+<% js_bundle :external_tools %>
-
<%= mt :external_tools_note, <<-HEREDOC, :lti_index_url => "https://lti-examples.heroku.com/index.html", :lti_examples_url => "http://help.instructure.com/entries/20878626-lti-tools-and-examples"
- External (LTI) Tools are an easy way to add new features to Canvas.
- They can be added to individual courses, or to all courses in an account.
- Once configured, you can link to them through course modules and create assignments for
- assessment tools.
-
- Click [here](%{lti_index_url}) to see some LTI tools that work great with Canvas. You can also check out the Canvas Community topics about LTI tools [here](%{lti_examples_url}).
- HEREDOC
- %>
-
diff --git a/app/views/jst/ExternalTools/EditView.handlebars b/app/views/jst/ExternalTools/EditView.handlebars
new file mode 100644
index 00000000000..685da671d14
--- /dev/null
+++ b/app/views/jst/ExternalTools/EditView.handlebars
@@ -0,0 +1,116 @@
+
\ No newline at end of file
diff --git a/app/views/jst/ExternalTools/IndexView.handlebars b/app/views/jst/ExternalTools/IndexView.handlebars
new file mode 100644
index 00000000000..dd64562a3e3
--- /dev/null
+++ b/app/views/jst/ExternalTools/IndexView.handlebars
@@ -0,0 +1,83 @@
+
{{#t "external_tools_note"}}
+External (LTI) Tools are an easy way to add new features to Canvas.
+They can be added to individual courses, or to all courses in an account.
+Once configured, you can link to them through course modules and create assignments for
+assessment tools.{{/t}}
+
+
{{#t "external_tools_references"}}Click here to see some
+LTI tools that work great with Canvas. You can also check out the Canvas
+Community topics about LTI tools
+here
+{{/t}}