Provide Default Icon for editor_button placement
Tools with the editor_button placement now have the right to remain silent as to an icon_url. Tools have the right to an icon; if they cannot afford an icon, one will be appointed for them. closes INTEROP-8426 flag=allow_lti_tools_editor_button_placement_without_icon Test plan: - Ensure the feature flag is on (default in dev) - Create LTI tools with no icon_url at the top level or in the editor button placement. You can try some of the following for the name of the tool (or 'title' in LTI 1.3). (Or you can test some of these out directly with the /lti/tool_default_icon endpoint, with the name parameter) - titles that start with some punctuation/whitespace - titles which are all punctuation/whitespace - titles with non-ASCII characters, e.g. ė, अ, 好, or 👍 - at least one simple title starting with a letter - Make at least one of the apps a favorite in the RCE (slider button in the apps list in the course/account settings) - Make sure the new default icons appear in all three locatons: - in the RCE favorite location - RCE dropdown - RCE "All Apps" listing (accessed from the dropdown) - Check that the default icons use the first number or letter-like (alphabetic, Chinese character, etc.) character in the tool editor button name, capitalized if applicable - Optional: Go to /lti/tool_default_icon?id=123&name=abc in Chrome, inspect element on the text, go to "Computed" and at the bottom "Rendered Fonts", and make sure "Inter Black" was rendered. - Optional: Install the same LTI 1.3 tool with no icon_url multiple times in one context and make sure both copies have identical icons. - rune 'rake doc:api' and check that the docs at doc/api/file.editor_button_placement.html have been updated to reflect the new icon_url behavior. - Turn the feature flag off - Edit an LTI 1.3 developer key 9that has at least one installation) to have an editor_button placement but not have any icon_url or canvas_icon_class in the editor_button placement configuration or top level configuration. - Run jobs and check that tool installations do not have an editor_button placement. Change-Id: I25f83c0995347ab0df887c844139ee74b814a571 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/340408 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Paul Gray <paul.gray@instructure.com> QA-Review: Paul Gray <paul.gray@instructure.com> Product-Review: Evan Battaglia <ebattaglia@instructure.com>
This commit is contained in:
parent
2bb315122e
commit
975ae3b313
|
@ -0,0 +1,44 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# Copyright (C) 2024 - present Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
module Lti
|
||||
class ToolDefaultIconController < ApplicationController
|
||||
# Generates an SVG icon for a tool based on its name and ID
|
||||
|
||||
# NOTE! If we ever change this file or the template, we'll need to
|
||||
# bust users' caches by changing the route in routes.rb or adding a
|
||||
# "version" parameter or similar to the URL. See
|
||||
# ContextExternalTool#default_icon_path for usage
|
||||
CACHE_MAX_AGE = 1.month.seconds
|
||||
|
||||
COLORS = %w[#fb5607 #3a86ff #5f9207 #8338ec #d81159 #390099 #9e0059].freeze
|
||||
|
||||
def show
|
||||
# Use first number/"letter-like" character ('0', 'a', 'é', '我', etc.), or none.
|
||||
@glyph = params[:name]&.match(/[0-9\p{Letter}]/)&.to_s&.upcase
|
||||
# Color based on hash of the developer key / tool (global) ID.
|
||||
@color = COLORS[params[:id].hash % COLORS.length]
|
||||
|
||||
response.headers["Cache-Control"] = "max-age=#{CACHE_MAX_AGE}"
|
||||
cancel_cache_buster
|
||||
|
||||
render content_type: "image/svg+xml", layout: false
|
||||
end
|
||||
end
|
||||
end
|
|
@ -539,9 +539,14 @@ module ApplicationHelper
|
|||
# force the YAML to be deserialized before caching, since it's expensive
|
||||
tools.each(&:settings)
|
||||
end
|
||||
|
||||
# Default tool icons need to have a hostname (cannot just be a path)
|
||||
# because they are used externally via ServicesApiController endpoints
|
||||
default_icon_base_url = "#{request.protocol}#{request.host_with_port}"
|
||||
|
||||
ContextExternalTool
|
||||
.shard(@context.shard)
|
||||
.editor_button_json(cached_tools.dup, @context, @current_user, session)
|
||||
.editor_button_json(cached_tools.dup, @context, @current_user, session, default_icon_base_url)
|
||||
end
|
||||
|
||||
def nbsp
|
||||
|
|
|
@ -224,18 +224,26 @@ class ContextExternalTool < ActiveRecord::Base
|
|||
false
|
||||
end
|
||||
|
||||
def editor_button_json(tools, context, user, session = nil)
|
||||
def editor_button_json(tools, context, user, session, default_tool_icon_base_url)
|
||||
tools.select! { |tool| visible?(tool.editor_button["visibility"], user, context, session) }
|
||||
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new({ link_attributes: { target: "_blank" } }))
|
||||
always_on_ids = Setting.get("rce_always_on_developer_key_ids", "").split(",").map(&:to_i)
|
||||
tools.map do |tool|
|
||||
canvas_icon_class = tool.editor_button(:canvas_icon_class)
|
||||
icon_url = tool.editor_button(:icon_url)
|
||||
if canvas_icon_class.blank? && icon_url.blank?
|
||||
# Default tool icons are served by canvas; some users of this method
|
||||
# may need a full URL rather than path.
|
||||
icon_url = default_tool_icon_base_url + tool.default_icon_path
|
||||
end
|
||||
|
||||
{
|
||||
name: tool.label_for(:editor_button, I18n.locale),
|
||||
id: tool.id,
|
||||
favorite: tool.is_rce_favorite_in_context?(context),
|
||||
url: tool.editor_button(:url),
|
||||
icon_url: tool.editor_button(:icon_url),
|
||||
canvas_icon_class: tool.editor_button(:canvas_icon_class),
|
||||
icon_url:,
|
||||
canvas_icon_class:,
|
||||
width: tool.editor_button(:selection_width),
|
||||
height: tool.editor_button(:selection_height),
|
||||
use_tray: tool.editor_button(:use_tray) == "true",
|
||||
|
@ -827,7 +835,9 @@ class ContextExternalTool < ActiveRecord::Base
|
|||
settings.delete(type) unless extension_setting(type, :url)
|
||||
end
|
||||
|
||||
unless root_account.feature_enabled?(:allow_lti_tools_editor_button_placement_without_icon)
|
||||
settings.delete(:editor_button) unless editor_button(:icon_url) || editor_button(:canvas_icon_class)
|
||||
end
|
||||
|
||||
sync_placements!(Lti::ResourcePlacement::PLACEMENTS.select { |type| settings[type] }.map(&:to_s))
|
||||
true
|
||||
|
@ -1587,6 +1597,15 @@ class ContextExternalTool < ActiveRecord::Base
|
|||
ContextExternalTool.associated_1_1_tool(self, context, launch_url || url || domain)
|
||||
end
|
||||
|
||||
# Icon for tools which don't provide one, based on the DeveloperKey or tool
|
||||
# id, and the tool name
|
||||
def default_icon_path
|
||||
Rails.application.routes.url_helpers.lti_tool_default_icon_path(
|
||||
id: global_developer_key_id || global_id,
|
||||
name:
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Locally and in OSS installations, this can be configured in config/dynamic_settings.yml.
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<style type="text/css">
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-weight: 1000;
|
||||
/* Contains only A-Z */
|
||||
src: url(data:font/woff2;base64,d09GMgABAAAAAAfMAA8AAAAAD/gAAAdxAAME3QAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbhTQcKgZgP1NUQVREADQRCAqQDIwIATYCJANsCzgABCAFg1YHIBu3DIgehU3ZM3bCI6ltNcnHBB0BtMaavVtENUSSaYYGmQTNPIqGBilrKDRrJEKU/zUAN+2SICGkIZWJ8xOr8asZTA2dVgwq9nSVtM6MMlNnqk87E2uAf//387/m6h36iZPXsSudR2iETCnfzmaOqdelhQZtCXHveNPQNWkphBLYxK2p5yDVK1nJnBe9EwGUA8gQCI0RCFCqtU7I6e59deDW4q1Z4IiA34t6q8l5WaBBCyYioyXCGBLwxZPhnQjhJwuEF9bQIFWU0+IKZX2F9RO9GJLqvyBthv/X/vIizIM5WlrozVtOKnBC7AiT3hm1tUYLiij3vZnFnUIvQZR6fbRu5t/FrVy9Q7slL0vt+MtyiU8tsmT7/y5245vtwgu3p3vY90CeJEn9+dEDb4MkRSNQPLkBIpYCEPAIMgwxEXce5CQ4z3gKwSZikhyhU8iTEgMDK9HTn9mobbUPbNhsWDsW+VE2iKFoUDIX8Ioh+RucVG5GnCPpJwIhXnJhsZ8WTmgMLV392wRUpyZXA+EOoBANdGMRCDQS1AdM/1TbqWYroQDW17KwFA+CSDgLCD2UDXDiVUIDgPQFZFbMgV0YFSJKJhJdu3Qm00QatM6IvHRko6/3fG2AIdALWSmQHwEgAr+eatoYcdwH5ZqyWOTFMG3lcpbpwIo13CQ8y/MNaUgWBdL18Xgcwyg4QVHEFTNWT9JDLrjX3K9FhculQ21tutS1e0yKW81VorKujmEpeYrTZIYROB3lkp0GUIHN575qzHRdc91jHA+eUNVPH7rV3K+97s4L7hPuXyerrt3Lm4vSsnJBIHnSHUTZk0RY+sRcQs9S7nMltk2+QVXeTH3kS+jlgkKikCqKOCknEW4mQRAl10OAsZuMCk+SaowUBFJhHZOmEwkxRe5ki8PqnIwpZIsZmptL743diuQyoerRPXbjA0y4/zincivkpWivX0YSR7kdRCqaPWj0ycnsUEOZXsQVC4+6ms7fkpr0ekC5znVmw9DjmFotpJQ2GB48MdbdfAUwyRcogeSOrqugSoRooF+bRSiKXVyxYkSS3k0rwxQKFnn6leQzZk4oLrZVKOLEckrgKYEsdCdnmzr2xDf4x03t4nO8/zLzYjNsoBV+R5cufTw/N2HPlpJRDbyqRVeLy+umrN4o1sy4O2ufyFFjTu2pSSwM0LOmR72yfaMNMbFjk8yvUP6MwIJunXJNg0t9BjqcYxN+b92f9OtQ2dh+RW2H9Ao1yzQBlKyylH3yQDv+fuOD78lanUHqOvTO60g3y+czM9phthdo7PC5Tz/OqNXfz5z+1OKqa3ZdfM7burq8t7fiFx+DDbTh43DOjuvr7Bs33WkAQ90/a1xTZ1Mlhq0MPxYycHBoRlON49jolJ9bt8f/PTx2cvnqXtqxw2NlLoejacNXjoVXWO+jv3/Vdv0zd6wnZ7Ff3Bma8m7/NEIxBzbQfLrvKX0QwBuXCABGzt6a+AAnT/gNX/3weLdxh9vipQ/Z18+iC4yK1gV5p6H/E/Yw3LRtdeQ47y6TfbrmzD77d9d4+zIk45FO0PPY3uzYuIYw1XbQlPd8U9vknnABs/nVShQUA8Uu+XUNZAN9QLvma1PxKti6wlDiOvqufsMjH2iXzkC7jrxrWF+fSuaK0O+O+7W/ym4PIXbfLZMl/0KsX/ptB7rduHVEM3LDC37z4gR7197lRlPvEnvnhGOQTCPs4dvmeUel3sZFrz42WfYylJy8jmLFgRC178g5ClXvdFWWPbBa+y7rldT64S3uzwj9ZiqkibbssYaM9gHdWK8XXfwsbXpyq1qtafU+f87Cnsdhdfpkg7wpIrljwY2eQHUcoS2frUkbUuAd1Y/gwruPDs1teNZbER7qhbc4cqX1qGUfv1lWHPPBKYhe1M5sNZxmSMZbw1LQFcOHuPvFHd2E3VIZxNzSvVCn8nkXq50+XRsbN02LNWPhPY/GM8t50Fu/EUvgss0pZNTYXvFex149C03veNn8LsOW49VwrJCHLcH3FAMWea02o21XtHtCyXIsnyxb2K4r2kYgL0ZGC6Xmwz1k+ItLbO1tcARGjmaPOc0VEO4KgADyeHtExyOLYxWRn+kmcgAfH/Y1AfjUD6z7/u23qmka2xhQiPSi/26mjhMn+BPoAYRhu21ln4KadkJdZIKNtqKkmIdNcg1MGdDZCkYdRAXmFKJF0S2UeoaAUZukn0LkpygpCmWOYf9ZpDoMLHKgb9E7129Tl9NQFSdBV2yGqmg5HCBAm1gSscR70eZtbBm71CDKALeAzBHqm8yROCs5io/t3UUUTizYvXCJ+qF+BUIBNviEHA8mhHZrwHlK4h8woTO6yJajSB6LVGZWSp2oIEZJZ2GTc3rJYpVWy6M0UJ5saZIlklfXyGdlli3Pf5Tas8RY5Wgx4fzH2FQWwiryJfCTKFsmf7KUCSgyWth7dlhp9c4yxEuUbrBkqfL1j84TwI/Kv8JEGEyrH60Iz4i+jIFE3i2yZVG2dF8lfanEC7KNjUqwZCoJXIxRfElarRWRmrutlCG6/yo4AQAAAA==) format('woff2');
|
||||
unicode-range: U+0000-00FF;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<rect width="24" height="24" rx="2" fill="<%= @color %>" />
|
||||
<path
|
||||
fill-rule="evenodd" clip-rule="evenodd" fill="white" fill-opacity="0.4"
|
||||
d="M19.6863 9.17647C19.6863 11.9012 17.4699 14.1176 14.7452 14.1176H11.9216C9.19692 14.1176 6.98045 11.9012 6.98045 9.17647V7.05882H19.6863V9.17647ZM22.5098 5.64706H21.0981H18.2745V0H16.8628V5.64706H9.80396V0H8.3922V5.64706H5.56867H4.1569V7.05882H5.56867V9.17647C5.56867 12.6791 8.41902 15.5294 11.9216 15.5294H12.6275V20.4706C12.6275 21.6381 11.6774 22.5882 10.5098 22.5882C9.34231 22.5882 8.3922 21.6381 8.3922 20.4706C8.3922 18.5252 6.8082 16.9412 4.86279 16.9412C2.91737 16.9412 1.33337 18.5252 1.33337 20.4706V24H2.74514V20.4706C2.74514 19.3031 3.69526 18.3529 4.86279 18.3529C6.03031 18.3529 6.98043 19.3031 6.98043 20.4706C6.98043 22.416 8.56443 24 10.5098 24C12.4567 24 14.0393 22.416 14.0393 20.4706V15.5294H14.7451C18.2477 15.5294 21.0981 12.6791 21.0981 9.17647V7.05882H22.5098V5.64706Z"
|
||||
/>
|
||||
<text x="50%" y="57%" dominant-baseline="middle" text-anchor="middle"
|
||||
font-size="20" font-family="Inter, Arial, sans-serif" font-weight="1000"
|
||||
fill="white"><%= @glyph %></text>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
|
@ -236,3 +236,21 @@ assignment_edit_placement_not_on_announcements:
|
|||
state: allowed_on
|
||||
ci:
|
||||
state: allowed_on
|
||||
allow_lti_tools_editor_button_placement_without_icon:
|
||||
state: hidden
|
||||
applies_to: RootAccount
|
||||
display_name: Allow LTI tools to have an editor button icon without placement (use default tool icon)
|
||||
description: |-
|
||||
When disabled, new or updated ContextExternalTool installations (including
|
||||
LTI 1.3 tools updated automatically due to updates to their developer keys)
|
||||
will have their editor_button placement removed if there is no icon_url or
|
||||
canvas_icon_class provided in the settings (legacy behavior). When enabled,
|
||||
the placement is not removed, so tools without icons will show a default
|
||||
icon (with a color / letter based on tool name and tool/developer key ID).
|
||||
environments:
|
||||
development:
|
||||
state: allowed_on
|
||||
test:
|
||||
state: allowed_on
|
||||
ci:
|
||||
state: allowed_on
|
||||
|
|
|
@ -2670,6 +2670,8 @@ CanvasRails::Application.routes.draw do
|
|||
|
||||
get "post_message_forwarding", controller: "lti/platform_storage", action: :post_message_forwarding, as: :lti_post_message_forwarding
|
||||
|
||||
get "lti/tool_default_icon" => "lti/tool_default_icon#show"
|
||||
|
||||
ApiRouteSet.draw(self, "/api/lti/v1") do
|
||||
post "tools/:tool_id/grade_passback", controller: :lti_api, action: :grade_passback, as: "lti_grade_passback_api"
|
||||
post "tools/:tool_id/ext_grade_passback", controller: :lti_api, action: :legacy_grade_passback, as: "blti_legacy_grade_passback_api"
|
||||
|
|
|
@ -69,12 +69,19 @@ All of these settings are contained for the **editor_button** placement:
|
|||
button. This can be overridden by language-specific settings if desired by
|
||||
using the labels setting. This is required if a text value is not set on the main tool configuration.
|
||||
|
||||
- icon_url <url> (required if not set on main tool configuration)
|
||||
- icon_url <url> (optional)
|
||||
|
||||
The URL for an icon that identifies your tool in the RCE toolbar. It is
|
||||
recommended that this icon be at least 16x16 px,in PNG or SVG format, and
|
||||
The url must be an https (SSL) URL. This setting is required if icon_url is
|
||||
not set on the main tool configuration.
|
||||
The URL for an icon that identifies your tool in the RCE toolbar. The icon
|
||||
will be shown at 16x16 pixels in the editor toolbar, and at 28x28 pixels in
|
||||
the editor's listing of all tools. It is recommended that this icon be in
|
||||
PNG or SVG format. The url must be an https (SSL) URL.
|
||||
|
||||
After April 2024, if a tool does not provide an icon_url on the
|
||||
editor_button placement or the main tool configuration, a default icon
|
||||
based on the first letter of the tool's name will be used. Before this
|
||||
change, if a tool does not provide an icon_url, the editor_button placement
|
||||
will be removed from the tool's install configuration, and the tool will not
|
||||
be shown in the editor_button placement.
|
||||
|
||||
- labels: <set of locale-label pairs> (optional)
|
||||
|
||||
|
|
|
@ -197,8 +197,9 @@ All of these settings are configurable for the "editor_button" placement in the
|
|||
|
||||
- icon_url: <url> (optional)
|
||||
|
||||
This is the URL of the icon that will be shown on the button in the rich editor. Icons should be 16x16 in size, and can be any standard web image format (png, gif, ico, etc.). It is recommended that this URL be over SSL (https).
|
||||
This is required if an icon_url is not set on the main tool configuration.
|
||||
The URL for an icon that identifies your tool in the RCE toolbar. The icon will be shown at 16x16 pixels in the editor toolbar, and at 28x28 pixels in the editor's listing of all tools. It is recommended that this icon be in PNG or SVG format. The url must be an https (SSL) URL.
|
||||
|
||||
After April 2024, if a tool does not provide an icon_url on the editor_button placement or the main tool configuration, a default icon based on the first letter of the tool's name will be used. Before this change, if a tool does not provide an icon_url, the editor_button placement will be removed from the tool install configuration, and the tool will not be shown in the editor_button placement.
|
||||
|
||||
- text: <text> (optional)
|
||||
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# Copyright (C) 2024 - present Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
describe Lti::ToolDefaultIconController do
|
||||
describe "#show" do
|
||||
render_views
|
||||
|
||||
it "generates an SVG icon" do
|
||||
get :show, params: { name: "test", id: 1 }
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.content_type).to eq("image/svg+xml; charset=utf-8")
|
||||
end
|
||||
|
||||
it 'uses the first number/"letter-like" character in the name, capitalized, or none' do
|
||||
expectations = {
|
||||
"abc" => "A",
|
||||
" def" => "D",
|
||||
"...1a" => "1",
|
||||
"...我a" => "我",
|
||||
"!!!..." => "",
|
||||
"😅" => ""
|
||||
}
|
||||
|
||||
expectations.each do |name, expected_glyph|
|
||||
get :show, params: { name:, id: 1 }
|
||||
expect(response.body).to include(">#{expected_glyph}</text>")
|
||||
end
|
||||
end
|
||||
|
||||
it "uses a color based on the hash of the developer key / tool (global) ID" do
|
||||
id = 1
|
||||
hash = id.to_s.hash
|
||||
color = Lti::ToolDefaultIconController::COLORS[hash % Lti::ToolDefaultIconController::COLORS.length]
|
||||
|
||||
get :show, params: { name: "test", id: }
|
||||
expect(response.body).to include("fill=\"#{color}\"")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -632,6 +632,20 @@ describe ApplicationHelper do
|
|||
|
||||
expect(editor_buttons).to be_empty
|
||||
end
|
||||
|
||||
it "passes in the base url for use with default tool icons" do
|
||||
@course = course_model
|
||||
@context = @course
|
||||
|
||||
expect(ContextExternalTool).to receive(:editor_button_json).with(
|
||||
an_instance_of(Array),
|
||||
anything,
|
||||
anything,
|
||||
anything,
|
||||
"http://test.host"
|
||||
)
|
||||
editor_buttons
|
||||
end
|
||||
end
|
||||
|
||||
describe "UI path checking" do
|
||||
|
|
|
@ -2565,12 +2565,27 @@ describe ContextExternalTool do
|
|||
expect(tool.editor_button).to be_nil
|
||||
tool.settings = { editor_button: { url: "http://www.example.com" } }
|
||||
tool.save
|
||||
expect(tool.editor_button).to be_nil
|
||||
# icon_url now optional, a default will be provided
|
||||
expect(tool.editor_button).not_to be_nil
|
||||
tool.settings = { editor_button: { url: "http://www.example.com", icon_url: "http://www.example.com", selection_width: 100, selection_height: 100 } }
|
||||
tool.save
|
||||
expect(tool.editor_button).not_to be_nil
|
||||
end
|
||||
|
||||
context "when allow_lti_tools_editor_button_placement_without_icon FF is disabled" do
|
||||
let(:ff) { :allow_lti_tools_editor_button_placement_without_icon }
|
||||
|
||||
before { @root_account.disable_feature! ff }
|
||||
after { @root_account.enable_feature! ff }
|
||||
|
||||
it "deletes the editor_button if icon_url is not present" do
|
||||
tool = new_external_tool
|
||||
tool.settings = { editor_button: { url: "http://www.example.com" } }
|
||||
tool.save
|
||||
expect(tool.editor_button).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
it "sets user_navigation if navigation configured" do
|
||||
tool = new_external_tool
|
||||
tool.settings = { user_navigation: { url: "http://www.example.com" } }
|
||||
|
@ -3446,41 +3461,86 @@ describe ContextExternalTool do
|
|||
|
||||
it "includes a boolean false for use_tray" do
|
||||
tool.editor_button = { use_tray: "false" }
|
||||
json = ContextExternalTool.editor_button_json([tool], @course, user_with_pseudonym)
|
||||
json = ContextExternalTool.editor_button_json([tool], @course, user_with_pseudonym, nil, "")
|
||||
expect(json[0][:use_tray]).to be false
|
||||
end
|
||||
|
||||
it "includes a boolean true for use_tray" do
|
||||
tool.editor_button = { use_tray: "true" }
|
||||
json = ContextExternalTool.editor_button_json([tool], @course, user_with_pseudonym)
|
||||
json = ContextExternalTool.editor_button_json([tool], @course, user_with_pseudonym, nil, "")
|
||||
expect(json[0][:use_tray]).to be true
|
||||
end
|
||||
|
||||
it "includes a boolean false for always_on" do
|
||||
Setting.set("rce_always_on_developer_key_ids", "90000000000001,90000000000002")
|
||||
json = ContextExternalTool.editor_button_json([tool], @course, user_with_pseudonym)
|
||||
json = ContextExternalTool.editor_button_json([tool], @course, user_with_pseudonym, nil, "")
|
||||
expect(json[0][:always_on]).to be false
|
||||
end
|
||||
|
||||
it "includes a boolean true for always_on" do
|
||||
Setting.set("rce_always_on_developer_key_ids", "90000000000001,#{tool.developer_key.global_id}")
|
||||
json = ContextExternalTool.editor_button_json([tool], @course, user_with_pseudonym)
|
||||
json = ContextExternalTool.editor_button_json([tool], @course, user_with_pseudonym, nil, "")
|
||||
expect(json[0][:always_on]).to be true
|
||||
end
|
||||
|
||||
describe "includes the description" do
|
||||
it "parsed into HTML" do
|
||||
tool.description = "the first paragraph.\n\nthe second paragraph."
|
||||
json = ContextExternalTool.editor_button_json([tool], @course, user_with_pseudonym)
|
||||
json = ContextExternalTool.editor_button_json([tool], @course, user_with_pseudonym, nil, "")
|
||||
expect(json[0][:description]).to eq "<p>the first paragraph.</p>\n\n<p>the second paragraph.</p>\n"
|
||||
end
|
||||
|
||||
it 'with target="_blank" on links' do
|
||||
tool.description = "[link text](http://the.url)"
|
||||
json = ContextExternalTool.editor_button_json([tool], @course, user_with_pseudonym)
|
||||
json = ContextExternalTool.editor_button_json([tool], @course, user_with_pseudonym, nil, "")
|
||||
expect(json[0][:description]).to eq "<p><a href=\"http://the.url\" target=\"_blank\">link text</a></p>\n"
|
||||
end
|
||||
end
|
||||
|
||||
describe "icon_url" do
|
||||
let(:base_url) { "https://myexampleschool.instructure.com" }
|
||||
|
||||
def editor_button_icon_url(tool)
|
||||
ContextExternalTool.editor_button_json([tool], @course, user_with_pseudonym, nil, base_url)[0][:icon_url]
|
||||
end
|
||||
|
||||
it "includes an icon_url when a tool has an top-level icon_url" do
|
||||
tool.editor_button = {}
|
||||
tool.settings[:icon_url] = "https://example.com/icon.png"
|
||||
expect(editor_button_icon_url(tool)).to eq("https://example.com/icon.png")
|
||||
end
|
||||
|
||||
it "includes an icon_url when a tool has an icon_url in editor_button" do
|
||||
tool.editor_button = { icon_url: "https://example.com/icon.png" }
|
||||
expect(editor_button_icon_url(tool)).to eq("https://example.com/icon.png")
|
||||
end
|
||||
|
||||
it "doesn't include an icon_url when the tool has a canvas_icon_class and no icon_url" do
|
||||
tool.editor_button = { canvas_icon_class: "icon_lti" }
|
||||
expect(editor_button_icon_url(tool)).to be_nil
|
||||
end
|
||||
|
||||
it "uses a default tool icon_url when the tool has no icon_url or canvas_icon_class" do
|
||||
tool.editor_button = {}
|
||||
expect(editor_button_icon_url(tool)).to match(
|
||||
%r{^https://myexampleschool.instructure.com/.*tool_default_icon.*name=editor.thing}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#default_icon_path" do
|
||||
it "references the lti_tool_default_icon_path, tool name, and tool developer key id" do
|
||||
tool = external_tool_1_3_model(opts: { name: "foo" })
|
||||
expect(tool.developer_key.global_id).to be_a(Integer)
|
||||
expect(tool.default_icon_path).to eq("/lti/tool_default_icon?id=#{tool.developer_key.global_id}&name=foo")
|
||||
end
|
||||
|
||||
it "uses tool ID if there is no developer key id" do
|
||||
tool = external_tool_model(opts: { name: "foo" })
|
||||
expect(tool.global_id).to be_a(Integer)
|
||||
expect(tool.default_icon_path).to eq("/lti/tool_default_icon?id=#{tool.global_id}&name=foo")
|
||||
end
|
||||
end
|
||||
|
||||
describe "is_rce_favorite" do
|
||||
|
|
Loading…
Reference in New Issue