show validation errors when configuring external tools
fixes #9725, fixes #9776, fixes #7311, Test plan: * go to the 'external tools' tab on the 'course settings' page * create and edit some external tools - make sure to omit fields or input invalid data and make sure you get sensible error messages back Change-Id: I88a7d5e439a27a599cbcfcfbd576434386949d35 Reviewed-on: https://gerrit.instructure.com/13332 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Jeremy Putnam <jeremyp@instructure.com> Reviewed-by: Bracken Mosbacker <bracken@instructure.com> Product-Review: Bracken Mosbacker <bracken@instructure.com>
This commit is contained in:
parent
0cb619ce64
commit
5e109f4434
|
@ -1 +1 @@
|
|||
require ['account_settings', 'external_tools']
|
||||
require ['account_settings']
|
||||
|
|
|
@ -5,7 +5,6 @@ require [
|
|||
'compiled/views/course_settings/tabs/tabUsers'
|
||||
'vendor/jquery.cookie'
|
||||
'course_settings'
|
||||
'external_tools'
|
||||
'grading_standards'
|
||||
], (NavigationView, UserCollectionView, UserCollection) ->
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
require [
|
||||
'compiled/collections/ExternalToolCollection',
|
||||
'compiled/views/ExternalTools/IndexView'
|
||||
], (ExternalToolCollection, ExternalToolsIndexView) ->
|
||||
collection = new ExternalToolCollection()
|
||||
collection.fetch()
|
||||
view = new ExternalToolsIndexView
|
||||
el: '#external_tools'
|
||||
collection: collection
|
||||
view.render()
|
|
@ -0,0 +1,7 @@
|
|||
define [
|
||||
'compiled/collections/PaginatedCollection'
|
||||
'compiled/models/ExternalTool'
|
||||
], (PaginatedCollection, ExternalTool) ->
|
||||
|
||||
class ExternalToolCollection extends PaginatedCollection
|
||||
model: ExternalTool
|
|
@ -301,7 +301,11 @@ define [
|
|||
truncate: ( string, max ) ->
|
||||
return textHelper.truncateText( string, { max: max } )
|
||||
|
||||
|
||||
enrollmentName: enrollmentName
|
||||
|
||||
titleize: (str) ->
|
||||
words = str.split(/[ _]+/)
|
||||
titleizedWords = _(words).map (w) -> w[0].toUpperCase() + w.slice(1)
|
||||
titleizedWords.join(' ')
|
||||
}
|
||||
return Handlebars
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
define ['Backbone'], ({Model}) ->
|
||||
|
||||
class ExternalTool extends Model
|
||||
resourceName: 'external_tools'
|
||||
|
||||
computedAttributes: [
|
||||
{
|
||||
name: 'custom_field_string'
|
||||
deps: ['custom_fields']
|
||||
}
|
||||
]
|
||||
|
||||
custom_field_string: ->
|
||||
("#{k}=#{v}" for k,v of @get('custom_fields')).join("\n")
|
|
@ -0,0 +1,74 @@
|
|||
define [
|
||||
'i18n!external_tools'
|
||||
'jst/ExternalTools/EditView'
|
||||
'compiled/views/ValidatedFormView'
|
||||
'compiled/jquery/fixDialogButtons'
|
||||
], (I18n, template, ValidatedFormView) ->
|
||||
|
||||
class EditView extends ValidatedFormView
|
||||
template: template
|
||||
|
||||
id: 'external_tool_form'
|
||||
|
||||
className: 'validated-form-view form-horizontal bootstrap-form'
|
||||
|
||||
events:
|
||||
'change #external_tool_config_type': 'onConfigTypeChange'
|
||||
|
||||
render: ->
|
||||
super
|
||||
@$el.dialog
|
||||
title: I18n.t 'dialog_title', 'Edit External Tool'
|
||||
width: 520
|
||||
height: "auto"
|
||||
resizable: true
|
||||
close: => @$el.remove()
|
||||
buttons: [
|
||||
class: "btn-primary"
|
||||
text: I18n.t 'submit', 'Submit'
|
||||
'data-text-while-loading': I18n.t 'saving', 'Saving...'
|
||||
click: => @submit()
|
||||
]
|
||||
@onConfigTypeChange()
|
||||
@$el.submit (e) =>
|
||||
@submit()
|
||||
return false
|
||||
this
|
||||
|
||||
submit: ->
|
||||
this.$el.parent().find('.btn-primary').removeClass('ui-state-hover')
|
||||
super
|
||||
|
||||
onSaveSuccess: ->
|
||||
@$el.dialog 'close'
|
||||
|
||||
onConfigTypeChange: ->
|
||||
configType = @$('#external_tool_config_type').val()
|
||||
@$('.config_type').hide().attr('aria-expanded', false)
|
||||
@$(".config_type.#{configType}").show().attr('aria-expanded', true)
|
||||
|
||||
showErrors: (errors) ->
|
||||
@removeErrors()
|
||||
for fieldName, field of errors
|
||||
$input = @findField fieldName
|
||||
html = (@translations[message] or message for {message} in field).join('</p><p>')
|
||||
@addError($input, html)
|
||||
|
||||
removeErrors: ->
|
||||
@$('.error .help-inline').remove()
|
||||
@$('.control-group').removeClass('error')
|
||||
@$('.alert.alert-error').remove()
|
||||
|
||||
addError: (input, message) ->
|
||||
input = $(input)
|
||||
input.parents('.control-group').addClass('error')
|
||||
input.after("<span class='help-inline'>#{message}</span>")
|
||||
input.one 'keypress', ->
|
||||
$(this).parents('.control-group').removeClass('error')
|
||||
$(this).parents('.control-group').find('.help-inline').remove()
|
||||
|
||||
onSaveFail: (xhr) =>
|
||||
super
|
||||
message = I18n.t 'generic_error', 'There was an error in processing your request'
|
||||
@$el.prepend("<div class='alert alert-error'>#{message}</span>")
|
||||
|
|
@ -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 = $("<div>#{msg}</div>").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
|
|
@ -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:
|
||||
#
|
||||
# <input name="user[first_name]">
|
||||
#
|
||||
# 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,94 +1,3 @@
|
|||
<% js_bundle :external_tools %>
|
||||
<div id="external_tools">
|
||||
<p><%= 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
|
||||
%>
|
||||
</p>
|
||||
<% @context.context_external_tools.active.each do |tool| %>
|
||||
<%= render :partial => 'external_tools/external_tool', :object => tool %>
|
||||
<% end %>
|
||||
<%= render :partial => 'external_tools/external_tool' %>
|
||||
<div id="external_tools_dialog" style="display: none;">
|
||||
<a href="<%= context_url(@context, :context_external_tools_url) %>" class="external_tools_url" style="display: none;"> </a>
|
||||
<% form_for :external_tool, :url => '.', :html => {:id => 'external_tool_form'} do |f| %>
|
||||
<table class="formtable">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><%= f.blabel :name, :en => "Name" %></td>
|
||||
<td><%= f.text_field :name %></td>
|
||||
</tr><tr>
|
||||
<td><%= f.blabel :consumer_key, :en => "Consumer Key" %></td>
|
||||
<td><%= f.text_field :consumer_key %></td>
|
||||
</tr><tr>
|
||||
<td style="vertical-align: top;"><%= f.blabel :shared_secret, :en => "Shared Secret" %></td>
|
||||
<td>
|
||||
<%= f.text_field :shared_secret %>
|
||||
<div class="shared_secret_note"><%= t :shared_secret_note, "enter a new value to change" %></div>
|
||||
</td>
|
||||
</tr><tr class="config_type_option">
|
||||
<td><%= f.blabel :config_type, :en => "Configuration Type" %></td>
|
||||
<td><%= f.select :config_type, [[t(:manual, "Manual Entry"),'manual'],[t(:by_url, "By URL"),'by_url'],[t(:by_xml, "Paste XML"),'by_xml']] %></td>
|
||||
</tr>
|
||||
</tbody><tbody class="config_type by_url">
|
||||
<tr>
|
||||
<td><%= f.blabel :config_url, :en => "Configuration URL" %></td>
|
||||
<td><%= f.text_field :config_url %></td>
|
||||
</tr>
|
||||
</tbody><tbody class="config_type by_xml">
|
||||
<tr>
|
||||
<td><%= f.blabel :config_xml, :en => "Paste XML Here" %></td>
|
||||
<td><%= f.text_area :config_xml, :style => "width: 300px; height: 60px;" %></td>
|
||||
</tr>
|
||||
</tbody><tbody class="config_type manual">
|
||||
<tr>
|
||||
<td><label for="external_tool_match_by"><%= before_label :match_by, "Match By" %></label></td>
|
||||
<td>
|
||||
<select id="external_tool_match_by">
|
||||
<option value="domain"><%= t :domain, "Domain" %></option>
|
||||
<option value="url"><%= t :url, "URL" %></option>
|
||||
</select>
|
||||
</td>
|
||||
</tr><tr class='tool_url'>
|
||||
<td><%= f.blabel :url, :en => "URL" %></td>
|
||||
<td><%= f.text_field :url %></td>
|
||||
</tr><tr class='tool_domain'>
|
||||
<td><%= f.blabel :domain, :en => "Domain" %></td>
|
||||
<td><%= f.text_field :domain %></td>
|
||||
</tr><tr>
|
||||
<td><%= f.blabel :privacy_level, :en => "Privacy" %></td>
|
||||
<td><%= f.select :privacy_level, [[t(:anonymous, "Anonymous"),'anonymous'],[t(:name_only, "Name Only"),'name_only'],[t(:email_only, "Email Only"),'email_only'],[t(:public, "Public"),'public']] %></td>
|
||||
</tr><tr>
|
||||
<td colspan="2">
|
||||
<%= f.blabel :custom_fields_string, :en => "Custom Fields" %>
|
||||
<span style="font-size: 0.8em; color: #888;"><%= t('custom_fields_explanation', '(one per line, format: name=value)') %></span>
|
||||
<br/>
|
||||
<%= f.text_area :custom_fields_string, :style => "width: 550px; height: 30px;" %>
|
||||
</td>
|
||||
</tr><tr>
|
||||
<td colspan="2">
|
||||
<%= f.blabel :description, :en => "Description" %><br/>
|
||||
<%= f.text_area :description, :style => "width: 550px; height: 75px;" %>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody><tbody>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="button-container">
|
||||
<button class="btn cancel_button" type="button"><%= t "#buttons.cancel", "Cancel" %></button>
|
||||
<button class="btn btn-primary save_button" type="submit"><%= t "#buttons.save_tool_settings", "Save Tool Settings" %></button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="button-container">
|
||||
<button class="btn btn-primary add_tool_link"><i class="icon-add"></i> <%= t "#buttons.add_external_tool", "Add External Tool" %></button>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
<fieldset>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="external_tool_name">Name</label>
|
||||
<div class="controls">
|
||||
<input type="text" id="external_tool_name" value="{{name}}" name="name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="external_tool_consumer_key">Consumer Key</label>
|
||||
<div class="controls">
|
||||
<input type="text" id="external_tool_consumer_key" value="{{consumer_key}}" name="consumer_key">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="external_tool_shared_secret">Shared Secret</label>
|
||||
<div class="controls">
|
||||
<input type="text" id="external_tool_shared_secret" value="{{shared_secret}}" name="shared_secret">
|
||||
{{#if id}}
|
||||
<p class="help-block">{{#t "shared_secret_note"}}Enter a new value
|
||||
to change{{/t}}</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group {{#if id}}hide{{/if}}">
|
||||
<label class="control-label" for="external_tool_config_type">Configuration Type</label>
|
||||
<div class="controls">
|
||||
<select id="external_tool_config_type" name="config_type">
|
||||
<option value="manual">{{#t "manual"}}Manual Entry{{/t}}</option>
|
||||
<option value="by_url">{{#t "by_url"}}By URL{{/t}}</option>
|
||||
<option value="by_xml">{{#t "by_xml"}}Paste XML{{/t}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group config_type by_url">
|
||||
<label class="control-label" for="external_tool_config_url">
|
||||
{{#t "config_url"}}Configuration URL{{/t}}
|
||||
</label>
|
||||
<div class="controls">
|
||||
<input type="text" id="external_tool_config_url" value="{{config_url}}" name="config_url">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group config_type by_xml">
|
||||
<label class="control-label" for="external_tool_config_xml">
|
||||
{{#t "paste_xml"}}Paste XML Here{{/t}}
|
||||
</label>
|
||||
<div class="controls">
|
||||
<textarea id="external_tool_config_xml" name="config_xml"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group config_type manual">
|
||||
<label class="control-label" for="external_tool_url">
|
||||
{{#t "tool_url"}}URL{{/t}}
|
||||
</label>
|
||||
<div class="controls">
|
||||
<input type="text" id="external_tool_url" value="{{url}}" name="url">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group config_type manual">
|
||||
<label class="control-label" for="external_tool_domain">
|
||||
{{#t "tool_domain"}}Domain{{/t}}
|
||||
</label>
|
||||
<div class="controls">
|
||||
<input type="text" id="external_tool_domain" value="{{domain}}" name="domain">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group config_type manual">
|
||||
<label class="control-label" for="external_tool_privacy_level">
|
||||
{{#t "privacy"}}Privacy{{/t}}
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select id="external_tool_privacy_level" name="privacy_level">
|
||||
<option
|
||||
value="anonymous"
|
||||
{{#ifEqual privacy_level "anonymous"}}selected{{/ifEqual}}
|
||||
>{{#t "anonymous"}}Anonymous{{/t}}</option>
|
||||
<option
|
||||
value="email_only"
|
||||
{{#ifEqual privacy_level "email_only"}}selected{{/ifEqual}}
|
||||
>{{#t "email_only"}}E-Mail Only{{/t}}</option>
|
||||
<option
|
||||
value="name_only"
|
||||
{{#ifEqual privacy_level "name_only"}}selected{{/ifEqual}}
|
||||
>{{#t "name_only"}}Name Only{{/t}}</option>
|
||||
<option
|
||||
value="public"
|
||||
{{#ifEqual privacy_level "public"}}selected{{/ifEqual}}
|
||||
>{{#t "public"}}Public{{/t}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group config_type manual">
|
||||
<label class="control-label" for="external_tool_custom_fields_string">
|
||||
{{#t "custom_fields"}}Custom Fields{{/t}}
|
||||
</label>
|
||||
<div class="controls">
|
||||
<textarea
|
||||
id="external_tool_custom_fields_string"
|
||||
name="custom_fields_string"
|
||||
>{{custom_fields_string}}</textarea>
|
||||
<div class="help-block"> {{#t "custom_feilds_explanation"}}One per
|
||||
line. Format: name=value{{/t}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group config_type manual">
|
||||
<label class="control-label" for="external_tool_description">
|
||||
{{#t "description"}}Description{{/t}}
|
||||
</label>
|
||||
<div class="controls">
|
||||
<textarea
|
||||
id="external_tool_description"
|
||||
name="description"
|
||||
>{{description}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
|
@ -0,0 +1,83 @@
|
|||
<p>{{#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}}</p>
|
||||
|
||||
<p>{{#t "external_tools_references"}}Click <a href="https://lti-examples.heroku.com/index.html">here</a> to see some
|
||||
LTI tools that work great with Canvas. You can also check out the Canvas
|
||||
Community topics about LTI tools <a href="http://help.instructure.com/entries/20878626-lti-tools-and-examples">
|
||||
here</a>
|
||||
{{/t}}</p>
|
||||
|
||||
{{#each this}}
|
||||
<div id=external_tool_{{id}}
|
||||
class=external_tool
|
||||
data-id={{id}}
|
||||
data-workflow-state={{workflow_state}}>
|
||||
<div class="header clearfix">
|
||||
<div class="name">{{name}}</div>
|
||||
<div class="links">
|
||||
<a href="#"
|
||||
class="edit_tool_link"
|
||||
title="{{#t "edit_tool"}}Edit Tool{{/t}}"
|
||||
data-edit-external-tool={{id}}
|
||||
><i class="icon-edit btn"></i></a>
|
||||
<a href="#"
|
||||
class="delete_tool_link"
|
||||
title="{{#t "delete_tool"}}Delete Tool{{/t}}"
|
||||
data-delete-external-tool={{id}}
|
||||
><i class="icon-trash btn"></i></a>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
<div class="content">
|
||||
<table class="table-condensed">
|
||||
<tr>
|
||||
<td>{{#t "privacy"}}Privacy{{/t}}:</td>
|
||||
<td class="readable_state">{{titleize privacy_level}}</td>
|
||||
</tr><tr>
|
||||
<td>{{#t "consumer_key"}}Consumer Key{{/t}}:</td>
|
||||
<td class="consumer_key">{{consumer_key}}</td>
|
||||
</tr>
|
||||
{{#if url}}
|
||||
<tr class="tool_url">
|
||||
<td>{{#t "url"}}URL{{/t}}:</td>
|
||||
<td class="url">{{url}}</td>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{#if domain}}
|
||||
<tr class="tool_domain">
|
||||
<td>{{#t "domain"}}Domain{{/t}}:</td>
|
||||
<td class="domain">{{domain}}</td>
|
||||
</tr>
|
||||
{{/if}}
|
||||
<tr>
|
||||
<td>{{#t "description"}}Description{{/t}}:</td>
|
||||
<td class="description" style="font-size: 0.8em;">{{description}}</td>
|
||||
</tr>
|
||||
{{#if vendor_help_link}}
|
||||
<tr class="tool_vendor_help_link">
|
||||
<td>{{#t "tool_vendor_help_link"}}Help Link{{/t}}:</td>
|
||||
<td class="vendor_help_link"><a href="{{vendor_help_link}}"">{{vendor_help_link}}</a></td>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{#if extras}}
|
||||
<tr class="extras">
|
||||
<td>{{#t "extras"}}Extras{{/t}}:</td>
|
||||
<td>
|
||||
{{#each extras}}<div class="{{this.extension_type}}">{{this.text}}</div>{{/each}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/if}}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
|
||||
<div class="button-container">
|
||||
<button class="btn btn-primary add_tool_link">
|
||||
<i class="icon-add"></i>
|
||||
{{#t "add_external_tool"}}Add External Tool{{/t}}
|
||||
</button>
|
||||
</div>
|
|
@ -26,10 +26,11 @@ module Api::V1::ExternalTools
|
|||
end
|
||||
|
||||
def external_tool_json(tool, context, user, session, extension_types = ContextExternalTool::EXTENSION_TYPES)
|
||||
methods = %w[privacy_level custom_fields]
|
||||
methods = %w[privacy_level custom_fields workflow_state vendor_help_link]
|
||||
methods += extension_types
|
||||
json = api_json(tool, user, session,
|
||||
:only => %w(id name description url domain consumer_key created_at updated_at),
|
||||
:only => %w(id name description url domain consumer_key
|
||||
created_at updated_at description),
|
||||
:methods => methods
|
||||
)
|
||||
|
||||
|
|
|
@ -1,171 +0,0 @@
|
|||
define([
|
||||
'i18n!external_tools',
|
||||
'jquery' /* $ */,
|
||||
'jquery.instructure_forms' /* formSubmit, fillFormData */,
|
||||
'jqueryui/dialog',
|
||||
'compiled/jquery/fixDialogButtons' /* fix dialog formatting */,
|
||||
'jquery.instructure_misc_plugins' /* confirmDelete, showIf */,
|
||||
'jquery.templateData' /* fillTemplateData, getTemplateData */
|
||||
], function(I18n, $) {
|
||||
|
||||
$(document).ready(function() {
|
||||
var $dialog = $("#external_tools_dialog");
|
||||
|
||||
$(".add_tool_link").click(function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
var formData = {
|
||||
domain: "",
|
||||
url: "",
|
||||
config_url: "",
|
||||
config_xml: "",
|
||||
description: "",
|
||||
name: "",
|
||||
custom_fields_string: "",
|
||||
privacy: "anonymous",
|
||||
consumer_key: "",
|
||||
shared_secret: ""
|
||||
}
|
||||
|
||||
$dialog.dialog({
|
||||
title: I18n.t('titles.edit_external_tool', "Edit External Tool"),
|
||||
width: 600
|
||||
}).fixDialogButtons();
|
||||
$dialog.find(".shared_secret_note").hide();
|
||||
$dialog.find("form")
|
||||
.attr('method', 'POST')
|
||||
.attr('action', $dialog.find(".external_tools_url").attr('href'));
|
||||
$dialog.fillFormData(formData, {object_name: 'external_tool'});
|
||||
$dialog.find(".config_type_option").show();
|
||||
|
||||
$("#external_tool_match_by").val('domain').change();
|
||||
$("#external_tool_config_type").val('manual').change();
|
||||
});
|
||||
|
||||
/* Form Submit Summary */
|
||||
$dialog.find("form").formSubmit({
|
||||
beforeSubmit: function(data) {
|
||||
$(this).find("button")
|
||||
.attr('disabled', true)
|
||||
.filter('.save_button')
|
||||
.text(I18n.t('messages.saving_tool_settings', "Saving Tool Settings..."));
|
||||
},
|
||||
success: function(tool) {
|
||||
var $this = $(this);
|
||||
var $tool = $("#external_tool_" + tool.id);
|
||||
|
||||
$this.find("button")
|
||||
.attr('disabled', false)
|
||||
.filter('.save_button')
|
||||
.text(I18n.t('buttons.save_tool_settings', "Save Tool Settings"));
|
||||
|
||||
$dialog.dialog('close');
|
||||
|
||||
var tool_div_doesnt_exist = $tool.length == 0;
|
||||
if(tool_div_doesnt_exist) {
|
||||
$tool = $("#external_tool_blank").clone(true)
|
||||
.removeAttr('id');
|
||||
$("#external_tools").append($tool);
|
||||
}
|
||||
|
||||
$tool.fillTemplateData({
|
||||
data: tool,
|
||||
dataValues: ['id', 'workflow_state'],
|
||||
hrefValues: ['id', 'vendor_help_link'],
|
||||
id: 'external_tool_' + tool.id
|
||||
});
|
||||
|
||||
/* Clear the shared seceret input field because it never gets updated and
|
||||
* should no longer be visible after you update *
|
||||
*/
|
||||
$('#external_tool_shared_secret').val('');
|
||||
|
||||
$tool
|
||||
.toggleClass('has_editor_button', tool.has_editor_button)
|
||||
.toggleClass('has_resource_selection', tool.has_resource_selection)
|
||||
.toggleClass('has_course_navigation', tool.has_course_navigation)
|
||||
.toggleClass('has_homework_submission', tool.has_homework_submission)
|
||||
.toggleClass('has_user_navigation', tool.has_user_navigation)
|
||||
.toggleClass('has_account_navigation', tool.has_account_navigation);
|
||||
|
||||
$tool.find(".tool_url")
|
||||
.showIf(tool.url).end()
|
||||
.find(".tool_domain")
|
||||
.showIf(tool.domain);
|
||||
|
||||
$tool.find(".tool_vendor_help_link")
|
||||
.showIf(tool.vendor_help_link);
|
||||
|
||||
$tool.show();
|
||||
},
|
||||
error: function(data) {
|
||||
$(this).find("button")
|
||||
.attr('disabled', false)
|
||||
.filter('.save_button')
|
||||
.text(I18n.t('errors.save_tool_settings_failed', "Save Tool Settings Failed"));
|
||||
}
|
||||
});
|
||||
|
||||
$dialog.find(".cancel_button").click(function() {
|
||||
$dialog.dialog('close');
|
||||
$('#external_tool_shared_secret').val('');
|
||||
});
|
||||
|
||||
$("#external_tools").delegate('.edit_tool_link', 'click', function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
var $tool = $(this).parents(".external_tool");
|
||||
var data = $tool.getTemplateData({textValues: ['name', 'description', 'domain', 'url', 'consumer_key', 'custom_fields_string'], dataValues: ['id', 'workflow_state']});
|
||||
|
||||
data.privacy_level = data.workflow_state;
|
||||
|
||||
$("#external_tool_match_by").val(data.url ? 'url' : 'domain').change();
|
||||
|
||||
$dialog.find(".shared_secret_note").show();
|
||||
|
||||
$dialog.find("form")
|
||||
.attr('method', 'PUT')
|
||||
.attr('action', $tool.find(".update_tool_url").attr('rel'));
|
||||
|
||||
$dialog.fillFormData(data, {object_name: 'external_tool'});
|
||||
|
||||
$dialog.dialog({
|
||||
title: I18n.t('titles.edit_external_tool', "Edit External Tool"),
|
||||
width: 600
|
||||
});
|
||||
|
||||
$dialog.find(".config_type_option").hide();
|
||||
$("#external_tool_config_type").val('manual').change();
|
||||
|
||||
}).delegate('.delete_tool_link', 'click', function(event) {
|
||||
event.preventDefault();
|
||||
var $tool = $(this).parents(".external_tool");
|
||||
var url = $tool.find(".update_tool_url").attr('rel');
|
||||
$tool.confirmDelete({
|
||||
url: url,
|
||||
message: I18n.t('prompts.remove_tool', "Are you sure you want to remove this tool? Any courses using this tool will no longer work."),
|
||||
success: function() {
|
||||
$(this).slideUp(function() {
|
||||
$(this).remove();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#external_tool_match_by").change(function() {
|
||||
if($(this).val() == 'url') {
|
||||
$(this).parents("form").find(".tool_domain").hide().find(":text").val("").end().end()
|
||||
.find(".tool_url").show();
|
||||
} else {
|
||||
$(this).parents("form").find(".tool_url").hide().find(":text").val("").end().end()
|
||||
.find(".tool_domain").show();
|
||||
}
|
||||
});
|
||||
|
||||
$("#external_tool_config_type").change(function(event) {
|
||||
$("#external_tool_form .config_type").hide();
|
||||
$("#external_tool_form .config_type." + $(this).val()).show();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
|
@ -111,7 +111,7 @@ describe ExternalToolsController, :type => :integration do
|
|||
{:controller => 'external_tools', :action => 'show', :format => 'json',
|
||||
:"#{type}_id" => context.id.to_s, :external_tool_id => et.id.to_s})
|
||||
|
||||
json.should == example_json(et)
|
||||
json.diff(example_json(et)).should == {}
|
||||
end
|
||||
|
||||
def not_found_call(context, type="course")
|
||||
|
@ -128,7 +128,8 @@ describe ExternalToolsController, :type => :integration do
|
|||
{:controller => 'external_tools', :action => 'index', :format => 'json',
|
||||
:"#{type}_id" => context.id.to_s})
|
||||
|
||||
json.should == [example_json(et)]
|
||||
json.size.should == 1
|
||||
json.first.diff(example_json(et)).should == {}
|
||||
end
|
||||
|
||||
def create_call(context, type="course")
|
||||
|
@ -138,7 +139,7 @@ describe ExternalToolsController, :type => :integration do
|
|||
context.context_external_tools.count.should == 1
|
||||
|
||||
et = context.context_external_tools.last
|
||||
json.should == example_json(et)
|
||||
json.diff(example_json(et)).should == {}
|
||||
end
|
||||
|
||||
def update_call(context, type="course")
|
||||
|
@ -148,7 +149,7 @@ describe ExternalToolsController, :type => :integration do
|
|||
{:controller => 'external_tools', :action => 'update', :format => 'json',
|
||||
:"#{type}_id" => context.id.to_s, :external_tool_id => et.id.to_s}, post_hash)
|
||||
et.reload
|
||||
json.should == example_json(et)
|
||||
json.diff(example_json(et)).should == {}
|
||||
end
|
||||
|
||||
def destroy_call(context, type="course")
|
||||
|
@ -251,6 +252,7 @@ describe ExternalToolsController, :type => :integration do
|
|||
"domain"=>nil,
|
||||
"url"=>"http://www.example.com/ims/lti",
|
||||
"id"=>et ? et.id : nil,
|
||||
"workflow_state"=>"public",
|
||||
"resource_selection"=>
|
||||
{"text"=>"",
|
||||
"url"=>"http://www.example.com/ims/lti/resource",
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
define [
|
||||
'jquery'
|
||||
'compiled/views/ExternalTools/EditView'
|
||||
], ($, EditView) ->
|
||||
|
||||
module 'ExternalTools',
|
||||
setup: ->
|
||||
@view = new EditView()
|
||||
@view.render()
|
||||
|
||||
teardown: ->
|
||||
@view.$el.dialog 'close'
|
||||
|
||||
test 'adds errors', 6, ->
|
||||
@view.addError(@view.$('input').first(), 'Wrong!')
|
||||
equal $('.help-inline').size(), 1
|
||||
equal $('.error').size(), 1
|
||||
ok $(".help-inline:contains('Wrong!')").is ':visible'
|
||||
@view.addError(@view.$('input').last(), 'Also Wrong...')
|
||||
equal $('.help-inline').size(), 2
|
||||
equal $('.error').size(), 2
|
||||
ok $(".help-inline:contains('Also Wrong...')").is ':visible'
|
||||
|
||||
test 'removes all errors', 2, ->
|
||||
@view.addError(@view.$('input').first(), 'Wrong!')
|
||||
@view.addError(@view.$('input').last(), 'Also Wrong...')
|
||||
equal $('.help-inline').size(), 2
|
||||
@view.removeErrors()
|
||||
equal $('.help-inline').size(), 0
|
|
@ -285,7 +285,7 @@ describe ExternalToolsController do
|
|||
response.should_not be_success
|
||||
assigns[:tool].should be_new_record
|
||||
json = json_parse(response.body)
|
||||
json['errors']['base'][0]['message'].should == I18n.t(:invalid_xml_syntax, 'Invalid xml syntax')
|
||||
json['errors']['config_xml'][0]['message'].should == I18n.t(:invalid_xml_syntax, 'Invalid xml syntax')
|
||||
|
||||
course_with_teacher_logged_in(:active_all => true)
|
||||
xml = "<a><b>c</b></a>"
|
||||
|
@ -293,7 +293,7 @@ describe ExternalToolsController do
|
|||
response.should_not be_success
|
||||
assigns[:tool].should be_new_record
|
||||
json = json_parse(response.body)
|
||||
json['errors']['base'][0]['message'].should == I18n.t(:invalid_xml_syntax, 'Invalid xml syntax')
|
||||
json['errors']['config_xml'][0]['message'].should == I18n.t(:invalid_xml_syntax, 'Invalid xml syntax')
|
||||
end
|
||||
|
||||
it "should handle advanced xml configurations by URL retrieval" do
|
||||
|
@ -348,7 +348,7 @@ describe ExternalToolsController do
|
|||
response.should_not be_success
|
||||
assigns[:tool].should be_new_record
|
||||
json = json_parse(response.body)
|
||||
json['errors']['base'][0]['message'].should == I18n.t(:retrieve_timeout, 'could not retrieve configuration, the server response timed out')
|
||||
json['errors']['config_url'][0]['message'].should == I18n.t(:retrieve_timeout, 'could not retrieve configuration, the server response timed out')
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -55,7 +55,7 @@ describe "admin settings tabs" do
|
|||
it "should delete an external tool" do
|
||||
add_external_tool
|
||||
hover_and_click(".delete_tool_link:visible")
|
||||
driver.switch_to.alert.accept
|
||||
fj('.ui-dialog button:contains(Delete):visible').click
|
||||
wait_for_ajax_requests
|
||||
tool = ContextExternalTool.last
|
||||
tool.workflow_state.should == "deleted"
|
||||
|
@ -67,7 +67,7 @@ describe "admin settings tabs" do
|
|||
new_description = "a different description"
|
||||
hover_and_click(".edit_tool_link:visible")
|
||||
replace_content(f("#external_tool_description"), new_description)
|
||||
fj(".save_button:visible").click
|
||||
fj('.ui-dialog button:contains(Submit):visible').click
|
||||
wait_for_ajax_requests
|
||||
tool = ContextExternalTool.last
|
||||
tool.description.should == new_description
|
||||
|
|
|
@ -16,7 +16,7 @@ describe "editing external tools" do
|
|||
f('#external_tool_consumer_key').send_keys('fdjaklfjdaklfdjaslfjajfkljsalkjflas')
|
||||
f('#external_tool_shared_secret').send_keys('r08132ufio1jfj1iofj3j1kf3ljl1')
|
||||
f('#external_tool_domain').send_keys('instructure.com')
|
||||
submit_form('#external_tool_form')
|
||||
fj('.ui-dialog:visible .btn-primary').click()
|
||||
wait_for_ajaximations
|
||||
f("#external_tool_#{ContextExternalTool.find_by_name(tool_name).id} .edit_tool_link").click
|
||||
f('#external_tool_name').should have_attribute(:value, tool_name)
|
||||
|
@ -27,7 +27,6 @@ describe "editing external tools" do
|
|||
get "/courses/#{@course.id}/settings"
|
||||
f("#tab-tools-link").click
|
||||
add_external_tool
|
||||
f("#external_tools_dialog").should_not be_displayed
|
||||
tool = ContextExternalTool.last
|
||||
tool_elem = f("#external_tool_#{tool.id}")
|
||||
tool_elem.should be_displayed
|
||||
|
@ -44,18 +43,15 @@ describe "editing external tools" do
|
|||
get "/courses/#{@course.id}/settings"
|
||||
keep_trying_until { f("#tab-tools-link").should be_displayed }
|
||||
f("#tab-tools-link").click
|
||||
tool_elem = f("#external_tool_#{tool.id}")
|
||||
tool_elem.find_element(:css, ".edit_tool_link").click
|
||||
f("#external_tools_dialog").should be_displayed
|
||||
f("#external_tool_#{tool.id} .edit_tool_link").click
|
||||
replace_content(f("#external_tool_name"), "new tool (updated)")
|
||||
replace_content(f("#external_tool_consumer_key"), "key (updated)")
|
||||
replace_content(f("#external_tool_shared_secret"), "secret (updated)")
|
||||
replace_content(f("#external_tool_domain"), "example2.com")
|
||||
replace_content(f("#external_tool_custom_fields_string"), "a=9\nb=8")
|
||||
f("#external_tools_dialog .save_button").click
|
||||
fj('.ui-dialog:visible .btn-primary').click()
|
||||
wait_for_ajax_requests
|
||||
f("#external_tools_dialog").should_not be_displayed
|
||||
tool_elem = fj("#external_tools .external_tool:visible").should be_displayed
|
||||
tool_elem = fj("#external_tools .external_tool").should be_displayed
|
||||
tool_elem.should_not be_nil
|
||||
tool.reload
|
||||
tool.name.should == "new tool (updated)"
|
||||
|
|
|
@ -9,7 +9,6 @@ shared_examples_for "external tools tests" do
|
|||
secret = "secret"
|
||||
|
||||
f("#tab-tools .add_tool_link").click
|
||||
f("#external_tools_dialog").should be_displayed
|
||||
|
||||
f("#external_tool_name").send_keys(name)
|
||||
f("#external_tool_consumer_key").send_keys(key)
|
||||
|
@ -21,7 +20,7 @@ shared_examples_for "external tools tests" do
|
|||
else
|
||||
add_manual opts
|
||||
end
|
||||
submit_form("#external_tools_dialog")
|
||||
fj('.ui-dialog:visible .btn-primary').click()
|
||||
wait_for_ajax_requests
|
||||
# ContextExternalTool.count.should != 0
|
||||
tool = ContextExternalTool.last
|
||||
|
@ -38,24 +37,16 @@ shared_examples_for "external tools tests" do
|
|||
tool.url.should == url
|
||||
tool.workflow_state.should == "public"
|
||||
tool.description.should == "Description"
|
||||
tool.has_editor_button.should be_true
|
||||
tool.has_resource_selection.should be_true
|
||||
tool.has_course_navigation.should be_true
|
||||
tool.has_account_navigation.should be_true
|
||||
tool.has_user_navigation.should be_true
|
||||
f("#external_tool_#{tool.id} .url").text.should == url
|
||||
f("#external_tool_#{tool.id} .editor_button").should be_displayed
|
||||
f("#external_tool_#{tool.id} .resource_selection").should be_displayed
|
||||
f("#external_tool_#{tool.id} .course_navigation").should be_displayed
|
||||
f("#external_tool_#{tool.id} .user_navigation").should be_displayed
|
||||
f("#external_tool_#{tool.id} .account_navigation").should be_displayed
|
||||
f("#external_tool_#{tool.id} .readable_state").text.should == "Public"
|
||||
f("#external_tool_#{tool.id} .description").text.should == "Description"
|
||||
f("#external_tool_#{tool.id} .vendor_help_link").should be_displayed
|
||||
f("#external_tool_#{tool.id} .vendor_help_link").text.should == tool.vendor_help_link
|
||||
ContextExternalTool::EXTENSION_TYPES.each do |type|
|
||||
tool.extension_setting(type).should_not be_empty
|
||||
f("#external_tool_#{tool.id} .#{type}").should be_displayed
|
||||
end
|
||||
f("#external_tool_#{tool.id} .url").text.should eql url
|
||||
elsif opts.include? :url
|
||||
url = "https://lti-examples.heroku.com/tool_redirect"
|
||||
kitten_text = "pictures of kittens to your site"
|
||||
|
@ -100,7 +91,7 @@ shared_examples_for "external tools tests" do
|
|||
|
||||
def add_manual (opts)
|
||||
f("#external_tool_config_type option[value='manual']").click
|
||||
f("#external_tool_form .config_type.manual").should be_displayed
|
||||
f(".config_type.manual").should be_displayed
|
||||
f("#external_tool_config_url").should_not be_displayed
|
||||
f("#external_tool_config_xml").should_not be_displayed
|
||||
@custom_key = "value"
|
||||
|
@ -111,10 +102,8 @@ shared_examples_for "external tools tests" do
|
|||
f("#external_tool_description").send_keys(@description)
|
||||
if opts.include? :manual_url
|
||||
@manual_url = @domain+":80"
|
||||
f("#external_tool_match_by option[value='url']").click
|
||||
f("#external_tool_url").send_keys(@manual_url)
|
||||
else
|
||||
f("#external_tool_match_by option[value='domain']").click
|
||||
f("#external_tool_domain").send_keys(@domain)
|
||||
end
|
||||
|
||||
|
@ -139,7 +128,7 @@ shared_examples_for "external tools tests" do
|
|||
|
||||
def add_xml
|
||||
f("#external_tool_config_type option[value='by_xml']").click
|
||||
f("#external_tool_form .config_type.manual").should_not be_displayed
|
||||
f(".config_type.manual").should_not be_displayed
|
||||
f("#external_tool_config_url").should_not be_displayed
|
||||
f("#external_tool_config_xml").should be_displayed
|
||||
|
||||
|
|
Loading…
Reference in New Issue