Make LTI configurations accept canvas icon class

Support passing a class for an icon for styling goodness.

added support for `canvas_icon_class` allowing use od css classes from canvas
style guide which can make it look better and maintain our styling
standards.

Fixes PLAT-1222

Test Plan:

prereq's
- "LOR External Tools" feature flag enabled
- (notice the lack of lti example tool, you dont need it)

A new commons icon has been added so you should probably run the
following
`bundle exec rake brand_configs:clean canvas:compile_assets`

Add a new LTI tool to the account using [this xml](https://gist.github.com/defektive/dbd182cb04500e236bde)
check placements to make sure they show a fancy new icon instead of a
crappy star. course_home_sub_navigation should show up as the commons icon

placements to look at
:assignment_menu - cog on an assignment
:course_home_sub_navigation - right side of page
:course_settings_sub_navigation
:discussion_topic_menu
:file_menu (no icon)
:global_navigation (no icon)
:module_menu
:quiz_menu
:wiki_page_menu

Change-Id: Ib462e928a5a3811df490f9bf2441b755db2882a9
Reviewed-on: https://gerrit.instructure.com/62492
Tested-by: Jenkins
Reviewed-by: Nathan Mills <nathanm@instructure.com>
QA-Review: August Thornton <august@instructure.com>
Product-Review: Brad Horrocks <bhorrocks@instructure.com>
This commit is contained in:
Brad Horrocks 2015-09-03 11:09:37 -06:00
parent 214cc361d5
commit a11fdec9f6
37 changed files with 288 additions and 128 deletions

View File

@ -1,7 +1,7 @@
{
"checksum": {
"previous": "348f51d5e794468e11a57af4c5fe610cc6c673a5e45538fb058946d99836c6ab",
"current": "348f51d5e794468e11a57af4c5fe610cc6c673a5e45538fb058946d99836c6ab"
"previous": "2e26ccf447e726d45ebc10e81051358dbb016e7193463a6ffd9785df95970a5b",
"current": "2e26ccf447e726d45ebc10e81051358dbb016e7193463a6ffd9785df95970a5b"
},
"fonts": [
"public/fonts/canvas/canvas-icons.ttf",
@ -118,6 +118,10 @@
"codepoint": 61722,
"source": "public/fonts/icons/collection-save.svg"
},
"commons": {
"codepoint": 61879,
"source": "public/fonts/icons/commons.svg"
},
"complete": {
"codepoint": 61723,
"source": "public/fonts/icons/complete.svg"

View File

@ -151,22 +151,28 @@ class ApplicationController < ActionController::Base
return [] if context.is_a?(Group)
context = context.account if context.is_a?(User)
tools = ContextExternalTool.all_tools_for(context, {:type => type,
tools = ContextExternalTool.all_tools_for(context, {:placements => type,
:root_account => @domain_root_account, :current_user => @current_user})
extension_settings = [:icon_url] + custom_settings
tools.map do |tool|
hash = {
:title => tool.label_for(type, I18n.locale),
:base_url => named_context_url(context, :context_external_tool_path, tool, :launch_type => type)
}
extension_settings.each do |setting|
hash[setting] = tool.extension_setting(type, setting)
end
hash
external_tool_display_hash(tool, type, context, custom_settings)
end
end
def external_tool_display_hash(tool, type, context=@context, custom_settings=[])
hash = {
:title => tool.label_for(type, I18n.locale),
:base_url => polymorphic_url([context, :external_tool], id: tool.id, launch_type: type)
}
extension_settings = [:icon_url, :canvas_icon_class] | custom_settings
extension_settings.each do |setting|
hash[setting] = tool.extension_setting(type, setting)
end
hash
end
helper_method :external_tool_display_hash
def k12?
@domain_root_account && @domain_root_account.feature_enabled?(:k12)
end

View File

@ -128,7 +128,7 @@ class AssignmentsController < ApplicationController
end
if @assignment.submission_types.include?("online_upload") || @assignment.submission_types.include?("online_url")
@external_tools = ContextExternalTool.all_tools_for(@context, :user => @current_user, :type => :homework_submission)
@external_tools = ContextExternalTool.all_tools_for(@context, :user => @current_user, :placements => :homework_submission)
else
@external_tools = []
end

View File

@ -154,7 +154,7 @@ class ContentMigrationsController < ApplicationController
options = @plugins.map{|p| {:label => p.metadata(:select_text), :id => p.id}}
external_tools = ContextExternalTool.all_tools_for(@context, :type => :migration_selection, :root_account => @domain_root_account, :current_user => @current_user)
external_tools = ContextExternalTool.all_tools_for(@context, :placements => :migration_selection, :root_account => @domain_root_account, :current_user => @current_user)
options.concat(external_tools.map do |et|
{
id: et.asset_string,

View File

@ -1573,7 +1573,7 @@ class CoursesController < ApplicationController
@recent_feedback = (@current_user && @current_user.recent_feedback(:contexts => @contexts)) || []
end
@course_home_sub_navigation_tools = ContextExternalTool.all_tools_for(@context, :type => :course_home_sub_navigation, :root_account => @domain_root_account, :current_user => @current_user)
@course_home_sub_navigation_tools = ContextExternalTool.all_tools_for(@context, :placements => :course_home_sub_navigation, :root_account => @domain_root_account, :current_user => @current_user)
unless @context.grants_right?(@current_user, session, :manage_content)
@course_home_sub_navigation_tools.reject! { |tool| tool.course_home_sub_navigation(:visibility) == 'admins' }
end

View File

@ -643,14 +643,14 @@ class ExternalToolsController < ApplicationController
#
# This would create a tool on this course with two custom fields and a course navigation tab
# curl 'https://<canvas>/api/v1/courses/<course_id>/external_tools' \
# -H "Authorization: Bearer <token>" \
# -F 'name=LTI Example' \
# -F 'consumer_key=asdfg' \
# -F 'shared_secret=lkjh' \
# -H "Authorization: Bearer <token>" \
# -F 'name=LTI Example' \
# -F 'consumer_key=asdfg' \
# -F 'shared_secret=lkjh' \
# -F 'url=https://example.com/ims/lti' \
# -F 'privacy_level=name_only' \
# -F 'custom_fields[key1]=value1' \
# -F 'custom_fields[key2]=value2' \
# -F 'privacy_level=name_only' \
# -F 'custom_fields[key1]=value1' \
# -F 'custom_fields[key2]=value2' \
# -F 'course_navigation[text]=Course Materials' \
# -F 'course_navigation[default]=false'
# -F 'course_navigation[enabled]=true'
@ -659,12 +659,12 @@ class ExternalToolsController < ApplicationController
#
# This would create a tool on the account with navigation for the user profile page
# curl 'https://<canvas>/api/v1/accounts/<account_id>/external_tools' \
# -H "Authorization: Bearer <token>" \
# -F 'name=LTI Example' \
# -F 'consumer_key=asdfg' \
# -F 'shared_secret=lkjh' \
# -H "Authorization: Bearer <token>" \
# -F 'name=LTI Example' \
# -F 'consumer_key=asdfg' \
# -F 'shared_secret=lkjh' \
# -F 'url=https://example.com/ims/lti' \
# -F 'privacy_level=name_only' \
# -F 'privacy_level=name_only' \
# -F 'user_navigation[url]=https://example.com/ims/lti/user_endpoint' \
# -F 'user_navigation[text]=Something Cool'
# -F 'user_navigation[enabled]=true'
@ -673,11 +673,11 @@ class ExternalToolsController < ApplicationController
#
# This would create a tool on the account with configuration pulled from an external URL
# curl 'https://<canvas>/api/v1/accounts/<account_id>/external_tools' \
# -H "Authorization: Bearer <token>" \
# -F 'name=LTI Example' \
# -F 'consumer_key=asdfg' \
# -F 'shared_secret=lkjh' \
# -F 'config_type=by_url' \
# -H "Authorization: Bearer <token>" \
# -F 'name=LTI Example' \
# -F 'consumer_key=asdfg' \
# -F 'shared_secret=lkjh' \
# -F 'config_type=by_url' \
# -F 'config_url=https://example.com/ims/lti/tool_config.xml'
def create
if authorized_action(@context, @current_user, :update)
@ -705,8 +705,8 @@ class ExternalToolsController < ApplicationController
#
# This would update the specified keys on this external tool
# curl -X PUT 'https://<canvas>/api/v1/courses/<course_id>/external_tools/<external_tool_id>' \
# -H "Authorization: Bearer <token>" \
# -F 'name=Public Example' \
# -H "Authorization: Bearer <token>" \
# -F 'name=Public Example' \
# -F 'privacy_level=public'
def update
@tool = @context.context_external_tools.active.find(params[:id] || params[:external_tool_id])
@ -776,7 +776,7 @@ class ExternalToolsController < ApplicationController
def set_tool_attributes(tool, params)
attrs = ContextExternalTool::EXTENSION_TYPES
attrs += [:name, :description, :url, :icon_url, :domain, :privacy_level, :consumer_key, :shared_secret,
attrs += [:name, :description, :url, :icon_url, :canvas_icon_class, :domain, :privacy_level, :consumer_key, :shared_secret,
:custom_fields, :custom_fields_string, :text, :config_type, :config_url, :config_xml, :not_selectable, :app_center_id]
attrs.each do |prop|
tool.send("#{prop}=", params[prop]) if params.has_key?(prop)

View File

@ -459,6 +459,7 @@ module ApplicationHelper
:id => tool.id,
:url => tool.editor_button(:url),
:icon_url => tool.editor_button(:icon_url),
:canvas_icon_class => tool.editor_button(:canvas_icon_class),
:width => tool.editor_button(:selection_width),
:height => tool.editor_button(:selection_height)
}

View File

@ -80,7 +80,7 @@ class ContextExternalTool < ActiveRecord::Base
hash[:enabled] = Canvas::Plugin.value_to_boolean(hash[:enabled]) if hash[:enabled]
settings[type] = {}.with_indifferent_access
extension_keys = [:custom_fields, :default, :display_type, :enabled, :icon_url,
extension_keys = [:custom_fields, :default, :display_type, :enabled, :icon_url, :canvas_icon_class,
:selection_height, :selection_width, :text, :url, :message_type]
if custom_keys = CUSTOM_EXTENSION_KEYS[type]
extension_keys += custom_keys
@ -275,6 +275,13 @@ class ContextExternalTool < ActiveRecord::Base
def icon_url
settings[:icon_url]
end
def canvas_icon_class=(i_url)
settings[:canvas_icon_class] = i_url
end
def canvas_icon_class
settings[:canvas_icon_class]
end
def text=(val)
settings[:text] = val
@ -349,7 +356,7 @@ class ContextExternalTool < ActiveRecord::Base
end
end
settings.delete(:editor_button) if !editor_button(:icon_url)
settings.delete(:editor_button) unless editor_button(:icon_url) || editor_button(:canvas_icon_class)
sync_placements!(EXTENSION_TYPES.select{|type| !!settings[type]}.map(&:to_s))
true

View File

@ -1,11 +1,27 @@
///
/// How to update Icons
//
// Add your new svg(s) to app/icons/
// Edit the template file. Important!! Edit file in public/fonts/_canvas-icons.scss
// Compile your fonts. In your project in terminal run: bundle exec rake icons:compile
// Check the styleguide to make sure they're pulling in /styleguide
// Done!
// TL;DR
//
// Add your new svg(s) to "public/fonts/icons/"
// Add your new icon markup to this template public/fonts/icons/_canvas-icons.scss
// Rebuild icon font with `bundle exec rake icons:compile`
//
// Details
//
// Help! I cant see my icon in "/styleguide"!
// make sure you updated the template above
//
// I can see my entry in "/styleguide", but it looks like an unsupported unicode character (an empty rectangle or a square with hex in it)
// Try doing a clean, rebuild, then restart your server
// `bundle exec rake brand_configs:clean canvas:compile_assets`
//
// I can see my entry in "/styleguide", but it is completely empty where the icon should be
// Your stylesheet isn't being loaded properly.
// make sure your svg should 512x512
// clear your browser cache
//
// More info can be found at <https://gollum.instructure.com/frontend-basics/canvas-icon-font>
///
/* @styleguide Icons
@ -287,6 +303,7 @@ Either use `<a>` with icon desired icon class added or insert `<i>` inside `<but
<span class="span3"><i class="icon-export"></i> icon-export</span>
<span class="span3"><i class="icon-equella"></i> icon-equella</span>
<span class="span3"><i class="icon-equation"></i> icon-equation</span>
<span class="span3"><i class="icon-commons"></i> icon-commons</span>
</div>
*/
@ -296,8 +313,7 @@ Either use `<a>` with icon desired icon class added or insert `<i>` inside `<but
src: url('/fonts/canvas/canvas-icons.eot');
src: url('/fonts/canvas/canvas-icons.eot?#iefix') format('embedded-opentype'),
url('/fonts/canvas/canvas-icons.woff') format('woff'),
url('/fonts/canvas/canvas-icons.ttf') format('truetype'),
url('/fonts/canvas/canvas-icons.svg#canvas-icons') format('svg');
url('/fonts/canvas/canvas-icons.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@ -407,6 +423,7 @@ h1, h2, h3, h4, .h1, .h2, .h3, .h4, p {
.icon-collapse:before { content: "\f118"; }
.icon-collection:before { content: "\f119"; }
.icon-collection-save:before { content: "\f11a"; }
.icon-commons:before { content: "\f1b7"; }
.icon-complete:before { content: "\f11b"; }
.icon-compose:before { content: "\f11c"; }
.icon-copy-course:before { content: "\f11d"; }

View File

@ -57,7 +57,7 @@
<% @assignment_menu_tools.each do |tool_hash| %>
<li role="presentation">
<a class="menu_tool_link" href="<%= tool_hash[:base_url] %>&assignments[]=<%= @assignment.id %>" tabindex="-1" role="menuitem">
<% if tool_hash[:icon_url] %><img class="icon" alt="" src="<%= tool_hash[:icon_url] %>" /><% end %>
<%= render(partial: 'external_tools/icon', locals: {tool: tool_hash, settings_key: :assignment_menu}) %>
<%= tool_hash[:title] %>
</a>
</li>

View File

@ -155,7 +155,7 @@
<% tool_path = course_external_tool_path(@context, tool, launch_type: 'module_menu',
:modules => [context_module ? context_module.id : "{{ id }}"]) %>
<a class="menu_tool_link" href="<%= tool_path %>">
<% if icon_url = tool.extension_setting(:module_menu, :icon_url) %><img class="icon" alt="" src="<%= icon_url %>" /><% end %>
<%= render(partial: 'external_tools/icon', locals: {tool: tool, settings_key: :module_menu}) %>
<%= tool.label_for(:module_menu, I18n.locale) %>
</a>
</li>

View File

@ -171,7 +171,7 @@
<li role="presentation" class="<%= menu_type %>">
<% tool_path = course_external_tool_path(@context, tool, launch_options) %>
<a class="menu_tool_link" href="<%= tool_path %>">
<% if icon_url = tool.extension_setting(menu_type, :icon_url) %><img class="icon" alt="" src="<%= icon_url %>" /><% end %>
<%= render(partial: 'external_tools/icon', locals: {tool: tool, settings_key: menu_type}) %>
<%= tool.label_for(menu_type, I18n.locale) %>
</a>
</li>

View File

@ -2,7 +2,9 @@
<% @course_settings_sub_navigation_tools.each do |tool| %>
<a class='btn button-sidebar-wide course-settings-sub-navigation-lti'
href="<%= course_external_tool_path(@context, tool, launch_type: 'course_settings_sub_navigation') %>">
<img class="icon" alt="" src="<%= tool.course_settings_sub_navigation(:icon_url) %>" />
<%= render(partial: 'external_tools/icon', locals: {tool: tool, settings_key: :course_settings_sub_navigation}) %>
<%= tool.label_for(:course_settings_sub_navigation, I18n.locale) %>
<% if tool.integration_type == 'lor' %>
<span class="badge new_badge pull-right"><%= t('links.new_badge', 'New') %></span>

View File

@ -51,7 +51,7 @@
<% @course_home_sub_navigation_tools.each do |tool| %>
<a class="btn button-sidebar-wide course-home-sub-navigation-lti"
href="<%= course_external_tool_path(@context, tool, launch_type: 'course_home_sub_navigation') %>">
<img class="icon" alt="" src="<%= tool.course_home_sub_navigation(:icon_url) %>" />
<%= render(partial: 'external_tools/icon', locals: {tool: tool, settings_key: :course_home_sub_navigation}) %>
<%= tool.label_for(:course_home_sub_navigation, I18n.locale) %>
</a>
<% end %>
@ -86,7 +86,7 @@
<% @course_home_sub_navigation_tools.each do |tool| %>
<a class="btn button-sidebar-wide course-home-sub-navigation-lti"
href="<%= course_external_tool_path(@context, tool, launch_type: 'course_home_sub_navigation') %>">
<img class="icon" alt="" src="<%= tool.course_home_sub_navigation(:icon_url) %>" />
<%= render(partial: 'external_tools/icon', locals: {tool: tool, settings_key: :course_home_sub_navigation}) %>
<%= tool.label_for(:course_home_sub_navigation, I18n.locale) %>
</a>
<% end %>

View File

@ -144,7 +144,7 @@
<% (@discussion_topic_menu_tools || []).each do |tool_hash| %>
<li>
<a class="menu_tool_link" href="<%= tool_hash[:base_url] %>&discussion_topics[]=<%= @topic.id %>" tabindex="-1" role="menuitem">
<% if tool_hash[:icon_url] %><img class="icon" alt="" src="<%= tool_hash[:icon_url] %>" /><% end %>
<%= render(partial: 'external_tools/icon', locals: {tool: tool_hash, settings_key: :discussion_topic_menu}) %>
<%= tool_hash[:title] %>
</a>
</li>

View File

@ -0,0 +1,10 @@
<% if tool && settings_key %>
<% tool = external_tool_display_hash(tool, settings_key) if tool.respond_to?(:extension_setting) %>
<% if tool[:canvas_icon_class] %>
<i class="<%= tool[:canvas_icon_class] %>"></i>
<% elsif tool[:icon_url] %>
<img class="icon" alt="" src="<%= tool[:icon_url] %>" />
<% end %>
<% end %>

View File

@ -133,14 +133,7 @@
{{/if}}
{{/if}}
{{#if ENV.permissions.manage_content}}
{{#each discussion_topic_menu_tools}}
<li>
<a class="menu_tool_link" href="{{url}}">
{{#if icon_url}}<img class="icon" alt="" src="{{icon_url}}" />{{/if}}
{{title}}
</a>
</li>
{{/each}}
{{>ExternalTools/external_tools_menu discussion_topic_menu_tools}}
{{/if}}
</ul>
{{/if}}

View File

@ -0,0 +1,10 @@
{{#if this}}
<a class="menu_tool_link" href="{{url}}">
{{#if canvas_icon_class}}
<i class="{{canvas_icon_class}}"></i>
{{else}}
{{#if icon_url}}<img class="icon" alt="" src="{{icon_url}}" />{{/if}}
{{/if}}
{{title}}
</a>
{{/if}}

View File

@ -0,0 +1,8 @@
{{#if this}}
{{#each this}}
<li role="presentation">
{{>ExternalTools/external_tool_menuitem this}}
</li>
{{/each}}
</ul>
{{/if}}

View File

@ -106,14 +106,7 @@
>{{#t "move"}}Move To…{{/t}}</a>
</li>
{{/if}}
{{#each menu_tools}}
<li role="presentation">
<a class="menu_tool_link" href="{{url}}">
{{#if icon_url}}<img class="icon" alt="" src="{{icon_url}}" />{{/if}}
{{title}}
</a>
</li>
{{/each}}
{{>ExternalTools/external_tools_menu menu_tools}}
</ul>
</div>
</div>

View File

@ -39,14 +39,7 @@
<li role="presentation">
<a href="{{url}}" id="ui-id-{{id}}-3" class="delete-item icon-trash" tabindex="-1" role="menuitem" title='{{#t "title_delete"}}Delete Quiz{{/t}}'>{{#t "links.delete"}}Delete{{/t}}</a>
</li>
{{#each quiz_menu_tools}}
<li role="presentation">
<a class="menu_tool_link" href="{{url}}">
{{#if icon_url}}<img class="icon" alt="" src="{{icon_url}}" />{{/if}}
{{title}}
</a>
</li>
{{/each}}
{{>ExternalTools/external_tools_menu quiz_menu_tools}}
</ul>
</div>
</div>

View File

@ -48,14 +48,7 @@
{{#if CAN.READ_REVISIONS}}
<li><a href="{{wiki_page_history_path}}" class="icon-clock view_page_history">{{#t "view_page_history_wiki"}}View Page History{{/t}}</a></li>
{{/if}}
{{#each wiki_page_menu_tools}}
<li role="presentation">
<a class="menu_tool_link" href="{{url}}">
{{#if icon_url}}<img class="icon" alt="" src="{{icon_url}}" />{{/if}}
{{title}}
</a>
</li>
{{/each}}
{{>ExternalTools/external_tools_menu wiki_page_menu_tools}}
</ul>
</div>
{{/if}}

View File

@ -20,14 +20,7 @@
{{#unless front_page}}
<li><a href="#" class="icon-document use-as-front-page-menu-item{{#unless published}} disabled{{/unless}}" title="{{#t 'menu.use_front_page'}}Use as Front Page{{/t}}" {{#unless published}}aria-disabled="true"{{/unless}}>{{#t 'menu.use_front_page'}}Use as Front Page{{/t}}</a></li>
{{/unless}}
{{#each wiki_page_menu_tools}}
<li>
<a class="menu_tool_link" href="{{url}}">
{{#if icon_url}}<img class="icon" alt="" src="{{icon_url}}" />{{/if}}
{{title}}
</a>
</li>
{{/each}}
{{>ExternalTools/external_tools_menu wiki_page_menu_tools}}
</ul>
</div>
</td>

View File

@ -221,7 +221,7 @@
<% (@quiz_menu_tools || []).each do |tool_hash| %>
<li role="presentation">
<a class="menu_tool_link" href="<%= tool_hash[:base_url] %>&quizzes[]=<%= @quiz.id %>" tabindex="-1" role="menuitem">
<% if tool_hash[:icon_url] %><img class="icon" alt="" src="<%= tool_hash[:icon_url] %>" /><% end %>
<%= render(partial: 'external_tools/icon', locals: {tool: tool_hash, settings_key: :quiz_menu}) %>
<%= tool_hash[:title] %>
</a>
</li>

View File

@ -1,11 +1,13 @@
# lib/icons.rake
require 'lib/brandable_css'
namespace :icons do
task :compile do
puts "Compiling icons..."
puts %x(bundle exec fontcustom compile --force)
puts "Compiling stylesheets..."
puts %x(bundle exec npm run compile-sass)
BrandableCSS.compile_all!
puts "Compiling styleguide..."
puts %x(bundle exec rake css:styleguide)
end

Binary file not shown.

View File

@ -1,13 +1,13 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<!--
2015-6-11: Created with FontForge (http://fontforge.org)
2015-9-11: Created with FontForge (http://fontforge.org)
-->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<metadata>
Created by FontForge 20150504 at Thu Jun 11 15:36:49 2015
By Jennifer Stern
Copyright (c) 2015, Jennifer Stern
Created by FontForge 20150715 at Fri Sep 11 16:12:17 2015
By Brad Horrocks
Copyright (c) 2015, Brad Horrocks
</metadata>
<defs>
<font id="canvas-icons" horiz-adv-x="512" >
@ -22,7 +22,7 @@ Copyright (c) 2015, Jennifer Stern
bbox="-32 -64.416 513.008 449.376"
underline-thickness="25.6"
underline-position="-51.2"
unicode-range="U+0020-F1B6"
unicode-range="U+0020-F1B7"
/>
<missing-glyph />
<glyph glyph-name="space" unicode=" " horiz-adv-x="200"
@ -272,10 +272,11 @@ c0 42.3203 -35.9365 76.6074 -78.2246 76.6074c-42.3193 0 -76.6074 -34.2871 -76.60
<glyph glyph-name="uniF12A" unicode="&#xf12a;"
d="M471.072 340.64c20.0156 -20.0156 0.719727 -35.04 0.719727 -35.04l-38.4316 -39.2158l-99.5684 97.1201l39.4883 42.5283c20.752 20.7354 38.4316 -3.39258 38.4316 -3.39258zM66.2236 99.04l239.296 241.04l105.425 -99.8076l-236.801 -234.097zM0 -64
l12.9443 44.7041l40.3193 -37.2959zM23.4883 9.34375l13.4717 59.8086l106.208 -89.0723l-58.0156 -22.9277z" />
<glyph glyph-name="uniF1AA" unicode="&#xf1aa;"
d="M445.472 267.968l0.0322266 -0.015625c38.1602 -9.28027 66.4961 -43.6963 66.4961 -84.7363c0 -48.1123 -38.9443 -87.1201 -87.0078 -87.2158h-104.992v-96h-128v96h-95.1357c-53.5049 0 -96.8643 43.3916 -96.8643 96.9277s43.3594 96.9287 96.8643 96.9287
c5.90332 0 11.6631 -0.560547 17.2793 -1.56836c-0.448242 3.08789 -0.6875 6.25586 -0.6875 9.47168c0 36.9287 29.9033 66.8643 66.8154 66.8643c21.5039 0 40.624 -10.1924 52.8486 -25.9844c21.792 27.6162 55.5195 45.3604 93.4238 45.3604
c64.7041 0 117.312 -51.6797 118.928 -116.032zM287.968 128h64l-96 96l-96 -96h64v-96h64v96z" />
<glyph glyph-name="uniF1B7" unicode="&#xf1b7;"
d="M75.2637 192.012v0c0 -62.4834 51.2002 -113.699 114.688 -113.699h93.1836v-75.2881h-93.1836c-104.448 0 -189.952 84.5068 -189.952 188.987c0 104.481 85.5039 188.988 189.952 188.988h93.6963v-75.2881h-93.1846c-63.4883 0 -115.2 -51.2158 -115.2 -113.7z
M75.2637 192.012v0c0 -62.4834 51.2002 -113.699 114.688 -113.699h93.1836v-75.2881h-93.1836c-104.448 0 -189.952 84.5068 -189.952 188.987c0 104.481 85.5039 188.988 189.952 188.988h93.6963v-75.2881h-93.1846c-63.4883 0 -115.2 -51.2158 -115.2 -113.7z
M512 192.012v0l-153.088 -135.21v90.1396h-161.28c-25.0879 0 -45.0557 19.9746 -45.0557 45.0703c0 25.0967 19.9678 45.0703 45.0557 45.0703h161.28v90.1406zM512 192.012v0l-153.088 -135.21v90.1396h-161.28c-25.0879 0 -45.0557 19.9746 -45.0557 45.0703
c0 25.0967 19.9678 45.0703 45.0557 45.0703h161.28v90.1406z" />
<glyph glyph-name="uniF180" unicode="&#xf180;"
d="M416.88 30.7041l67.8242 -68.2402h-190.288v191.424l70.5283 -70.6553c28.0801 27.8232 45.8555 66.0479 45.8555 108.783c0 72.1445 -49.8242 132.208 -116.368 149.265v75.5996c107.521 -18.2402 190.433 -111.824 190.433 -225.072
c0 -63.2158 -26.4326 -119.855 -67.9678 -161.088zM27.168 192c0 63.2158 26.3682 119.216 67.8877 160.448l-67.7441 67.3916h190.288v-189.728l-70.5273 70.6719c-28.0801 -27.8242 -45.8564 -66.0479 -45.8564 -108.784c0 -72.1436 49.8242 -132.208 116.384 -149.264
@ -316,6 +317,10 @@ c79.5674 0 147.824 67.6797 147.824 146.032c0 81.1035 -69.5684 148.128 -147.824 1
c-16.6875 -2.76758 -12.6875 16.3037 -12.6875 16.3037l0.160156 205.072c0.335938 24.4639 22.6875 -10.2725 22.6875 -10.2725l55.4404 -59.2637s42.3037 47.9844 120.704 46.6396z" />
<glyph glyph-name="uniF106" unicode="&#xf106;"
d="M256 64l-192 289.136h384z" />
<glyph glyph-name="uniF1AA" unicode="&#xf1aa;"
d="M445.472 267.968l0.0322266 -0.015625c38.1602 -9.28027 66.4961 -43.6963 66.4961 -84.7363c0 -48.1123 -38.9443 -87.1201 -87.0078 -87.2158h-104.992v-96h-128v96h-95.1357c-53.5049 0 -96.8643 43.3916 -96.8643 96.9277s43.3594 96.9287 96.8643 96.9287
c5.90332 0 11.6631 -0.560547 17.2793 -1.56836c-0.448242 3.08789 -0.6875 6.25586 -0.6875 9.47168c0 36.9287 29.9033 66.8643 66.8154 66.8643c21.5039 0 40.624 -10.1924 52.8486 -25.9844c21.792 27.6162 55.5195 45.3604 93.4238 45.3604
c64.7041 0 117.312 -51.6797 118.928 -116.032zM287.968 128h64l-96 96l-96 -96h64v-96h64v96z" />
<glyph glyph-name="uniF12B" unicode="&#xf12b;"
d="M430.976 50.9277v-0.015625c-19.6953 -41.0244 -54.6074 -73.5518 -87.4873 -73.5518c-25.2158 0 -60.0322 29.4072 -87.4883 29.4072c-29.9199 0.848633 -46.3838 -31.9355 -87.4883 -29.4072c-31.3281 1.93555 -58.8154 52.5918 -72.8955 73.5518
s-58.3359 74.5283 -58.3359 174.399c0 64.2402 66.4473 109.696 116.655 117.681c25.1367 3.9834 44.5762 -2.68848 63.2646 -9.44043c-43.9844 27.1045 -102.641 41.6318 -123.584 40.7998c25.6641 37.3604 75.6475 34.5283 102.08 29.4082

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

View File

@ -1,11 +1,27 @@
///
/// How to update Icons
//
// Add your new svg(s) to app/icons/
// Edit the template file. Important!! Edit file in public/fonts/_canvas-icons.scss
// Compile your fonts. In your project in terminal run: bundle exec rake icons:compile
// Check the styleguide to make sure they're pulling in /styleguide
// Done!
// TL;DR
//
// Add your new svg(s) to "public/fonts/icons/"
// Add your new icon markup to this template public/fonts/icons/_canvas-icons.scss
// Rebuild icon font with `bundle exec rake icons:compile`
//
// Details
//
// Help! I cant see my icon in "/styleguide"!
// make sure you updated the template above
//
// I can see my entry in "/styleguide", but it looks like an unsupported unicode character (an empty rectangle or a square with hex in it)
// Try doing a clean, rebuild, then restart your server
// `bundle exec rake brand_configs:clean canvas:compile_assets`
//
// I can see my entry in "/styleguide", but it is completely empty where the icon should be
// Your stylesheet isn't being loaded properly.
// make sure your svg should 512x512
// clear your browser cache
//
// More info can be found at <https://gollum.instructure.com/frontend-basics/canvas-icon-font>
///
/* @styleguide Icons
@ -287,6 +303,7 @@ Either use `<a>` with icon desired icon class added or insert `<i>` inside `<but
<span class="span3"><i class="icon-export"></i> icon-export</span>
<span class="span3"><i class="icon-equella"></i> icon-equella</span>
<span class="span3"><i class="icon-equation"></i> icon-equation</span>
<span class="span3"><i class="icon-commons"></i> icon-commons</span>
</div>
*/

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="512px" height="512px" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->
<title>Slice 1</title>
<desc>Created with Sketch.</desc>
<defs>
<path id="path-1" d="M75.264,188.987838 C75.264,126.504054 126.976,75.2878378 190.464,75.2878378 L283.648,75.2878378 L283.648,0 L189.952,0 C85.504,0 0,84.5067568 0,188.987838 C0,293.468919 85.504,377.975676 189.952,377.975676 L283.136,377.975676 L283.136,302.687838 L189.952,302.687838 C126.464,302.687838 75.264,251.471622 75.264,188.987838 L75.264,188.987838 Z"></path>
<path id="path-2" d="M512,188.987838 L358.912,53.777027 L358.912,143.917568 L197.632,143.917568 C172.544,143.917568 152.576,163.891892 152.576,188.987838 C152.576,214.083784 172.544,234.058108 197.632,234.058108 L358.912,234.058108 L358.912,324.198649 L512,188.987838 L512,188.987838 Z"></path>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="icon_commons" sketch:type="MSLayerGroup" transform="translate(0.000000, 67.000000)">
<g id="Group">
<g id="Shape">
<use fill="#000000" fill-rule="evenodd" sketch:type="MSShapeGroup" xlink:href="#path-1"></use>
<use fill="none" xlink:href="#path-1"></use>
</g>
<g id="Shape">
<use fill="#000000" fill-rule="evenodd" sketch:type="MSShapeGroup" xlink:href="#path-2"></use>
<use fill="none" xlink:href="#path-2"></use>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -64,12 +64,20 @@ define([
* complete with title, cmd, image, and classes
*/
buttonConfig: function(button){
return {
var config = {
title: button.name,
cmd: 'instructureExternalButton' + button.id,
image: button.icon_url,
classes: 'widget btn instructure_external_tool_button'
};
if (button.canvas_icon_class) {
config.icon = 'hack-to-avoid-mce-prefix ' + button.canvas_icon_class;
} else {
// default to image
config.image = button.icon_url;
}
return config;
},
/**
@ -93,8 +101,16 @@ define([
*/
clumpedButtonMapping: function(clumpedButtons, onClickHandler){
return clumpedButtons.reduce(function(items, button){
var key = "<img src='" + htmlEscape(button.icon_url) +
"'/>&nbsp;" + htmlEscape(button.name);
var key;
// added data-tool-id='"+ button.id +"' to make elements unique when the have the same name
if (button.canvas_icon_class) {
key = "<i class='"+ htmlEscape(button.canvas_icon_class) +"' data-tool-id='"+ button.id +"'></i>";
} else {
// icon_url is implied
key = "<img src='"+ htmlEscape(button.icon_url) +"' data-tool-id='"+ button.id +"'/>";
}
key += "&nbsp;" + htmlEscape(button.name);
items[key] = function() { onClickHandler(button); };
return items;
}, {});

View File

@ -30,17 +30,17 @@ define [
equal(Object.keys(items).length, 1)
test "clumpedButtonMapping uses a callback for onclick handlers", ->
clump = [{icon_url: 'some/url', name: "somebutton"}]
clump = [{icon_url: 'some/url', name: "somebutton", id: 42}]
calls = 0
callback = (()-> calls = calls + 1)
items = ExternalTools.clumpedButtonMapping(clump, callback)
items["<img src='some/url'/>&nbsp;somebutton"]()
items["<img src='some/url' data-tool-id='42'/>&nbsp;somebutton"]()
equal(calls, 1)
test "clumpedButtonMapping escapes extremely unlikely XSS attacks", ->
clump = [{icon_url: 'some/url', name: "<script>alert('attacked')</script>Name"}]
clump = [{icon_url: 'some/url', name: "<script>alert('attacked')</script>Name", id: 42}]
items = ExternalTools.clumpedButtonMapping(clump, (->))
escapedKey = "<img src='some/url'/>&nbsp;&lt;script&gt;alert('attacked')&lt;/script&gt;Name"
escapedKey = "<img src='some/url' data-tool-id='42'/>&nbsp;&lt;script&gt;alert('attacked')&lt;/script&gt;Name"
equal(Object.keys(items)[0], escapedKey)
test "attachClumpedDropdown sets up dropdown closure when clicked out of", ->

View File

@ -586,23 +586,87 @@ describe ApplicationController do
tool.account_navigation = {:url => "http://example.com", :icon_url => "http://example.com", :enabled => true}
tool.save!
controller.stubs(:named_context_url).returns("http://example.com")
controller.stubs(:polymorphic_url).returns("http://example.com")
external_tools = controller.external_tools_display_hashes(:account_navigation, @group)
expect(external_tools).to eq([])
end
it 'returns array of tools if context is not group' do
@course = course_model
tool = @course.context_external_tools.new(:name => "bob", :consumer_key => "test", :shared_secret => "secret", :url => "http://example.com")
tool.account_navigation = {:url => "http://example.com", :icon_url => "http://example.com", :enabled => true, :canvas_icon_class => 'icon-commons'}
tool.save!
controller.stubs(:polymorphic_url).returns("http://example.com")
external_tools = controller.external_tools_display_hashes(:account_navigation, @course)
expect(external_tools).to eq([{:title=>"bob", :base_url=>"http://example.com", :icon_url=>"http://example.com", :canvas_icon_class => 'icon-commons'}])
end
end
it 'returns array of tools if context is not group' do
@course = course_model
tool = @course.context_external_tools.new(:name => "bob", :consumer_key => "test", :shared_secret => "secret", :url => "http://example.com")
tool.account_navigation = {:url => "http://example.com", :icon_url => "http://example.com", :enabled => true}
tool.save!
describe 'external_tool_display_hash' do
def tool_settings(setting, include_class=false)
settings_hash = {
url: "http://example.com/?#{setting.to_s}",
icon_url: "http://example.com/icon.png?#{setting.to_s}",
enabled: true
}
controller.stubs(:named_context_url).returns("http://example.com")
external_tools = controller.external_tools_display_hashes(:account_navigation, @course)
settings_hash[:canvas_icon_class] = "icon-#{setting.to_s}" if include_class
settings_hash
end
expect(external_tools).to eq([{:title=>"bob", :base_url=>"http://example.com", :icon_url=>"http://example.com"}])
before :once do
@course = course_model
@group = @course.groups.create!(:name => "some group")
@tool = @course.context_external_tools.new(:name => "bob", :consumer_key => "test", :shared_secret => "secret", :url => "http://example.com")
@tool_settings = [
:user_navigation, :course_navigation, :account_navigation, :resource_selection,
:editor_button, :homework_submission, :migration_selection, :course_home_sub_navigation,
:course_settings_sub_navigation, :global_navigation,
:assignment_menu, :file_menu, :discussion_topic_menu, :module_menu, :quiz_menu, :wiki_page_menu,
:tool_configuration, :link_selection, :assignment_selection, :post_grades
]
@tool_settings.each do |setting|
@tool.send("#{setting}=", tool_settings(setting))
end
@tool.save!
end
before :each do
controller.stubs(:request).returns(ActionDispatch::TestRequest.new)
controller.instance_variable_set(:@context, @course)
end
it 'returns a hash' do
hash = controller.external_tool_display_hash(@tool, :account_navigation)
left_over_keys = hash.keys - [:base_url, :title, :icon_url, :canvas_icon_class]
expect(left_over_keys).to eq []
end
it 'all settings are correct' do
@tool_settings.each do |setting|
hash = controller.external_tool_display_hash(@tool, setting)
expect(hash[:base_url]).to eq "http://test.host/courses/#{@course.id}/external_tools/#{@tool.id}?launch_type=#{setting.to_s}"
expect(hash[:icon_url]).to eq "http://example.com/icon.png?#{setting.to_s}"
expect(hash[:canvas_icon_class]).to be nil
end
end
it 'all settings return canvas_icon_class if set' do
@tool_settings.each do |setting|
@tool.send("#{setting}=", tool_settings(setting, true))
@tool.save!
hash = controller.external_tool_display_hash(@tool, setting)
expect(hash[:base_url]).to eq "http://test.host/courses/#{@course.id}/external_tools/#{@tool.id}?launch_type=#{setting.to_s}"
expect(hash[:icon_url]).to eq "http://example.com/icon.png?#{setting.to_s}"
expect(hash[:canvas_icon_class]).to eq "icon-#{setting.to_s}"
end
end
end
end

View File

@ -569,22 +569,22 @@ describe ApplicationHelper do
@course = course_model
@group = @course.groups.create!(:name => "some group")
tool = @course.context_external_tools.new(:name => "bob", :consumer_key => "test", :shared_secret => "secret", :url => "http://example.com")
tool.editor_button = {:url => "http://example.com", :icon_url => "http://example.com"}
tool.editor_button = {:url => "http://example.com", :icon_url => "http://example.com", :canvas_icon_class => 'icon-commons'}
tool.save!
@context = @group
expect(editor_buttons).to eq([{:name=>"bob", :id=>tool.id, :url=>"http://example.com", :icon_url=>"http://example.com", :width=>800, :height=>400}])
expect(editor_buttons).to eq([{:name=>"bob", :id=>tool.id, :url=>"http://example.com", :icon_url=>"http://example.com", :canvas_icon_class => 'icon-commons', :width=>800, :height=>400}])
end
it "should return hash of tools if in course" do
@course = course_model
tool = @course.context_external_tools.new(:name => "bob", :consumer_key => "test", :shared_secret => "secret", :url => "http://example.com")
tool.editor_button = {:url => "http://example.com", :icon_url => "http://example.com"}
tool.editor_button = {:url => "http://example.com", :icon_url => "http://example.com", :canvas_icon_class => 'icon-commons'}
tool.save!
controller.stubs(:group_external_tool_path).returns('http://dummy')
@context = @course
expect(editor_buttons).to eq([{:name=>"bob", :id=>tool.id, :url=>"http://example.com", :icon_url=>"http://example.com", :width=>800, :height=>400}])
expect(editor_buttons).to eq([{:name=>"bob", :id=>tool.id, :url=>"http://example.com", :icon_url=>"http://example.com", :canvas_icon_class => 'icon-commons', :width=>800, :height=>400}])
end
it "should not include tools from the domain_root_account for users" do

View File

@ -117,6 +117,7 @@ module RSpec::Rails
real_controller = controller_class.new
real_controller.instance_variable_set(:@_request, @controller.request)
real_controller.instance_variable_set(:@context, @controller.instance_variable_get(:@context))
@controller.real_controller = real_controller
# just calling "render 'path/to/view'" by default looks for a partial

View File

@ -75,6 +75,7 @@ describe "courses/_settings_sidebar.html.erb" do
before do
view_context(@course, @user)
assigns[:current_user] = @user
@controller.instance_variable_set(:@context, @course)
end
it "should display all configured tools" do