Feature #23980 » 0001-Add-tag-helper-patch.patch
app/helpers/application_helper.rb | ||
---|---|---|
27 | 27 |
include Redmine::SudoMode::Helper |
28 | 28 |
include Redmine::Themes::Helper |
29 | 29 |
include Redmine::Hook::Helper |
30 |
include Redmine::Icon::Helper |
|
30 | 31 |
include Redmine::Helpers::URL |
31 | 32 | |
32 | 33 |
extend Forwardable |
app/models/icon_set.rb | ||
---|---|---|
1 |
# frozen_string_literal: true |
|
2 | ||
3 |
# Redmine - project management software |
|
4 |
# Copyright (C) 2006-2022 Jean-Philippe Lang |
|
5 |
# |
|
6 |
# This program is free software; you can redistribute it and/or |
|
7 |
# modify it under the terms of the GNU General Public License |
|
8 |
# as published by the Free Software Foundation; either version 2 |
|
9 |
# of the License, or (at your option) any later version. |
|
10 |
# |
|
11 |
# This program is distributed in the hope that it will be useful, |
|
12 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 |
# GNU General Public License for more details. |
|
15 |
# |
|
16 |
# You should have received a copy of the GNU General Public License |
|
17 |
# along with this program; if not, write to the Free Software |
|
18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
19 | ||
20 |
class IconSet |
|
21 |
extend ActionView::Helpers::AssetUrlHelper |
|
22 |
extend Redmine::Icon::MapLoader |
|
23 | ||
24 |
def self.fetch(name) |
|
25 |
@icons ||= create_map |
|
26 |
@icons[name] |
|
27 |
end |
|
28 | ||
29 |
def self.create_map |
|
30 |
icon_map = load_icon_map |
|
31 | ||
32 |
Redmine::Plugin.all.each { |plugin| icon_map.merge! plugin.load_icon_map } |
|
33 | ||
34 |
current_theme = Redmine::Themes.theme(Setting.ui_theme) |
|
35 |
icon_map.merge! current_theme.load_icon_map if current_theme.present? |
|
36 | ||
37 |
icon_map.freeze |
|
38 |
end |
|
39 | ||
40 |
def self.icon_config_dir |
|
41 |
Rails.application.config.redmine_svg_icon_map |
|
42 |
end |
|
43 | ||
44 |
def self.icon_dir |
|
45 |
'/icons' |
|
46 |
end |
|
47 |
end |
config/application.rb | ||
---|---|---|
75 | 75 |
# for more options (same options as config.cache_store). |
76 | 76 |
config.redmine_search_cache_store = :memory_store |
77 | 77 | |
78 |
# Enable experimental svg icons |
|
79 |
config.redmine_enable_svg_icon = false |
|
80 | ||
81 |
config.redmine_svg_icon_source = File.join(__dir__, 'icon_source.yml') |
|
82 | ||
83 |
config.redmine_svg_icon_map = File.join(__dir__, 'icons') |
|
84 | ||
78 | 85 |
# Configure log level here so that additional environment file |
79 | 86 |
# can change it (environments/ENV.rb would take precedence over it) |
80 | 87 |
config.log_level = Rails.env.production? ? :info : :debug |
lib/redmine/icon.rb | ||
---|---|---|
1 |
# frozen_string_literal: true |
|
2 | ||
3 |
# Redmine - project management software |
|
4 |
# Copyright (C) 2006-2021 Jean-Philippe Lang |
|
5 |
# |
|
6 |
# This program is free software; you can redistribute it and/or |
|
7 |
# modify it under the terms of the GNU General Public License |
|
8 |
# as published by the Free Software Foundation; either version 2 |
|
9 |
# of the License, or (at your option) any later version. |
|
10 |
# |
|
11 |
# This program is distributed in the hope that it will be useful, |
|
12 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 |
# GNU General Public License for more details. |
|
15 |
# |
|
16 |
# You should have received a copy of the GNU General Public License |
|
17 |
# along with this program; if not, write to the Free Software |
|
18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
19 | ||
20 |
require 'yaml' |
|
21 | ||
22 |
module Redmine |
|
23 |
module Icon |
|
24 |
module Helper |
|
25 |
def expander |
|
26 |
tag.span ' ', class: ['expander', 'icon', 'icon-expanded'], onclick: 'toggleRowGroup(this);' |
|
27 |
end |
|
28 | ||
29 |
def collapsible(is_new_record) |
|
30 |
css_class = is_new_record ? 'icon-expanded' : 'icon-collapsed' |
|
31 |
['icon', css_class] |
|
32 |
end |
|
33 | ||
34 |
def preload_icon_link(name) |
|
35 |
if Rails.application.config.redmine_enable_svg_icon |
|
36 |
icon_path = "#{Redmine::Utils.relative_url_root}#{icon_set_path(name)}" |
|
37 |
preload_link_tag(icon_path, as: 'fetch', id: "#{name}_icon_path", crossorigin: "anonymous", type: "application/json") |
|
38 |
else |
|
39 |
'' |
|
40 |
end |
|
41 |
end |
|
42 |
end |
|
43 | ||
44 |
module Patch |
|
45 |
# link_to 'test', '/test', class: "icon icon-add" |
|
46 |
# => <a class="icon icon-add icon-svg" href="/test"> |
|
47 |
# <svg class="s16" xmlns="http://www.w3.org/2000/svg"><use href="/icons/plus-circle.svg#icon"></use></svg>test |
|
48 |
# </a> |
|
49 |
# |
|
50 |
# link_to 'test.rb', '/test.rb', class: "icon icon-file text-x-ruby" |
|
51 |
# => <a class="icon icon-file text-x-ruby icon-svg" href="/test.rb"> |
|
52 |
# <svg class="s16" xmlns="http://www.w3.org/2000/svg"><use href="/icons/language-ruby.svg#icon"></use></svg>test.rb |
|
53 |
# </a> |
|
54 |
# |
|
55 |
# # when svg icon is not found |
|
56 |
# |
|
57 |
# link_to 'test', '/test', class: "icon icon-foo" |
|
58 |
# => <a href="/test" class="icon icon-foo"><!-- SVG icon-foo not found -->test</a> |
|
59 |
# |
|
60 |
def content_tag_string(name, content, options, escape = true) |
|
61 |
return super unless need_process?(name, options) |
|
62 | ||
63 |
processed = content_with_icon(content, options, escape) |
|
64 |
super name, processed, options, escape |
|
65 |
end |
|
66 |
DEFAULT_SVG_CSS_CLASS = 's16' |
|
67 |
FILE_TYPE_REGEXP = /(text-|image-|application-)/.freeze |
|
68 |
XMLNS = 'http://www.w3.org/2000/svg' |
|
69 | ||
70 |
private |
|
71 | ||
72 |
def need_process?(name, options) |
|
73 |
Rails.application.config.redmine_enable_svg_icon && |
|
74 |
name != :use && name != :svg && options.present? && |
|
75 |
(options[:class].present? || options['class'].present?) |
|
76 |
end |
|
77 | ||
78 |
def content_with_icon(content, options, escape) |
|
79 |
options.symbolize_keys! |
|
80 |
classes = split_tokens options[:class] |
|
81 |
icon = classes.include?('icon') |
|
82 |
icon_only = classes.include?('icon-only') |
|
83 |
return content unless icon || icon_only |
|
84 | ||
85 |
svg = create_svg_icon(classes, options) |
|
86 |
(icon_only ? svg : "#{svg}#{content}").html_safe |
|
87 |
end |
|
88 | ||
89 |
# copy from ActionView::Helpers::TagHelper#token_list |
|
90 |
def split_tokens(*args) |
|
91 |
ActionView::Helpers::TagHelper.build_tag_values(*args).flat_map { |value| value.to_s.split(/\s+/) }.uniq |
|
92 |
end |
|
93 | ||
94 |
def create_svg_icon(classes, options) |
|
95 |
icon_name = classes.find { |c| c != 'icon-only' && c.start_with?('icon-') } |
|
96 |
file_type = classes.find { |c| c.start_with?(FILE_TYPE_REGEXP) } |
|
97 |
svg = fetch_svg(icon_name, file_type) |
|
98 |
if svg.present? |
|
99 |
classes << 'icon-svg' |
|
100 |
options[:class] = classes.join(' ') |
|
101 |
svg |
|
102 |
else |
|
103 |
"<!-- SVG #{file_type || icon_name} not found -->" |
|
104 |
end |
|
105 |
end |
|
106 | ||
107 |
def fetch_svg(icon_name, file_type) |
|
108 |
if icon_name == 'icon-file' && file_type |
|
109 |
svg_tag(file_type, DEFAULT_SVG_CSS_CLASS, IconSet.fetch('file')) |
|
110 |
else |
|
111 |
svg_tag(icon_name, DEFAULT_SVG_CSS_CLASS, IconSet.fetch('common')) |
|
112 |
end |
|
113 |
end |
|
114 | ||
115 |
def svg_tag(name, css_class, icon_map) |
|
116 |
return '' unless icon_map.key?(name) |
|
117 | ||
118 |
content = content_tag_string(:use, nil, { :href => "#{icon_map[name]}#icon" }) |
|
119 |
content_tag_string(:svg, content, { :class => css_class, :xmlns => XMLNS }) |
|
120 |
end |
|
121 |
end |
|
122 | ||
123 |
module MapLoader |
|
124 |
def load_icon_map |
|
125 |
pattern = File.join icon_config_dir, '*.{yaml,yml}' |
|
126 |
Dir.glob(pattern).each_with_object({}) do |f, o| |
|
127 |
name = File.basename f, '.*' |
|
128 |
value = YAML.safe_load(File.open(Rails.root.join(f))) |
|
129 |
o[name] = value.transform_values { |e| path_to_asset File.join(icon_dir, e) } |
|
130 |
end |
|
131 |
end |
|
132 |
end |
|
133 |
end |
|
134 |
end |
lib/redmine/plugin.rb | ||
---|---|---|
50 | 50 |
# |
51 | 51 |
# See: http://www.redmine.org/projects/redmine/wiki/Plugin_Tutorial |
52 | 52 |
class Plugin |
53 |
include Redmine::Icon::MapLoader |
|
54 | ||
53 | 55 |
# Absolute path to the directory where plugins are located |
54 | 56 |
cattr_accessor :directory |
55 | 57 |
self.directory = PluginLoader.directory |
... | ... | |
525 | 527 |
super(version.to_s + "-" + current_plugin.id.to_s) |
526 | 528 |
end |
527 | 529 |
end |
530 | ||
531 |
def icon_config_dir |
|
532 |
File.join directory, 'config', 'icons' |
|
533 |
end |
|
534 | ||
535 |
def icon_dir |
|
536 |
File.join public_directory.sub(Rails.public_path.to_s, ''), 'icons' |
|
537 |
end |
|
528 | 538 |
end |
529 | 539 |
end |
lib/redmine/preparation.rb | ||
---|---|---|
23 | 23 |
ActiveRecord::Base.include Redmine::Acts::Positioned |
24 | 24 |
ActiveRecord::Base.include Redmine::Acts::Mentionable |
25 | 25 |
ActiveRecord::Base.include Redmine::I18n |
26 |
ActionView::Helpers::TagHelper::TagBuilder.prepend Redmine::Icon::Patch |
|
26 | 27 | |
27 | 28 |
Scm::Base.add "Subversion" |
28 | 29 |
Scm::Base.add "Mercurial" |
lib/redmine/themes.rb | ||
---|---|---|
43 | 43 | |
44 | 44 |
# Class used to represent a theme |
45 | 45 |
class Theme |
46 |
include Redmine::Icon::MapLoader |
|
47 | ||
46 | 48 |
attr_reader :path, :name, :dir |
47 | 49 | |
48 | 50 |
def initialize(path) |
... | ... | |
104 | 106 |
"/themes/#{dir}/favicon/#{favicon}" |
105 | 107 |
end |
106 | 108 | |
109 |
def icon_config_dir |
|
110 |
"/themes/#{dir}" |
|
111 |
end |
|
112 | ||
113 |
def icon_dir |
|
114 |
Rails.public_path.join("#{icon_config_dir}/icons") |
|
115 |
end |
|
116 | ||
107 | 117 |
private |
108 | 118 | |
109 | 119 |
def assets(dir, ext=nil) |
lib/redmine/views/other_formats_builder.rb | ||
---|---|---|
27 | 27 |
def link_to(name, options={}) |
28 | 28 |
url = {:format => name.to_s.downcase}.merge(options.delete(:url) || {}).except('page') |
29 | 29 |
caption = options.delete(:caption) || name |
30 |
html_options = {:class => name.to_s.downcase, :rel => 'nofollow'}.merge(options)
|
|
30 |
html_options = {:class => icon(name), :rel => 'nofollow'}.merge(options)
|
|
31 | 31 |
@view.content_tag('span', @view.link_to(caption, url, html_options)) |
32 | 32 |
end |
33 | 33 | |
... | ... | |
37 | 37 |
url = {:params => params, :page => nil, :format => name.to_s.downcase}.merge(url) |
38 | 38 | |
39 | 39 |
caption = options.delete(:caption) || name |
40 |
html_options = {:class => name.to_s.downcase, :rel => 'nofollow'}.merge(options)
|
|
40 |
html_options = {:class => icon(name), :rel => 'nofollow'}.merge(options)
|
|
41 | 41 |
@view.content_tag('span', @view.link_to(caption, url, html_options)) |
42 | 42 |
end |
43 | ||
44 |
def icon(name) |
|
45 |
icon_map = IconSet.fetch('common') |
|
46 |
format_name = name.to_s.downcase |
|
47 |
icon_map["icon-#{format_name}"] ? "icon icon-#{format_name}" : format_name |
|
48 |
end |
|
43 | 49 |
end |
44 | 50 |
end |
45 | 51 |
end |
public/stylesheets/application.css | ||
---|---|---|
160 | 160 |
#sidebar a.selected:hover {text-decoration:none;} |
161 | 161 |
#sidebar .query.default {font-weight: bold;} |
162 | 162 |
#admin-menu a {line-height:1.7em;} |
163 |
#admin-menu a.selected {padding-left: 20px !important; background-position: 2px 40%;} |
|
163 |
#admin-menu a.selected {background-position: 2px 40%;} |
|
164 |
#admin-menu a.selected:not(.icon-svg) {padding-left: 20px !important;} |
|
164 | 165 | |
165 | 166 |
a.collapsible {padding-left: 12px; } |
166 | 167 | |
... | ... | |
571 | 572 |
#issue-form .assign-to-me-link { padding-left: 5px; } |
572 | 573 | |
573 | 574 |
fieldset.collapsible {border-width: 1px 0 0 0;} |
574 |
fieldset.collapsible>legend { cursor:pointer; padding-left: 18px; background-position: 4px;} |
|
575 |
fieldset.collapsible>legend { cursor:pointer;} |
|
576 |
fieldset.collapsible>legend:not(.icon-svg) { padding-left: 18px; background-position: 4px;} |
|
575 | 577 | |
576 | 578 |
fieldset#date-range p { margin: 2px 0 2px 0; } |
577 | 579 |
fieldset#filters table { border-collapse: collapse; } |
... | ... | |
722 | 724 |
padding-top: 0.5em; |
723 | 725 |
} |
724 | 726 |
#projects-index a.icon-user, a.icon-bookmarked-project {padding-left:0; padding-right:20px; background-position:98% 50%;} |
725 |
#projects-index a.icon-user.icon-bookmarked-project { |
|
727 |
#projects-index a.icon-user.icon-bookmarked-project:not(.icon-svg) {
|
|
726 | 728 |
background-image: url(../images/tag_blue.png), url(../images/user.png); |
727 | 729 |
background-position: bottom 0px right 0px, bottom 0px right 20px; |
728 | 730 |
padding-right: 40px; |
... | ... | |
963 | 965 |
p.other-formats { text-align: right; font-size:0.9em; color: #666; } |
964 | 966 |
.other-formats span + span:before { content: "| "; } |
965 | 967 | |
966 |
a.atom { background: url(../images/feed.png) no-repeat 1px 50%; padding: 2px 0px 3px 16px; }
|
|
968 |
.icon-atom:not(.icon-svg) { background: url(../images/feed.png) no-repeat 1px 50%; padding: 2px 0px 3px 16px; }
|
|
967 | 969 | |
968 | 970 |
em.info {font-style:normal;display:block;font-size:90%;color:#888;} |
969 | 971 |
em.info.error {padding-left:20px; background:url(../images/exclamation.png) no-repeat 0 50%;} |
... | ... | |
1521 | 1523 |
white-space: pre-wrap; |
1522 | 1524 |
} |
1523 | 1525 | |
1526 |
/***** SVGs ******/ |
|
1527 |
.s16 { |
|
1528 |
width: 16px; |
|
1529 |
height: 16px; |
|
1530 |
vertical-align: text-bottom; |
|
1531 |
} |
|
1532 | ||
1524 | 1533 |
/***** Icons *****/ |
1525 |
.icon { |
|
1534 |
.icon:not(.icon-svg) {
|
|
1526 | 1535 |
background-position: 0% 50%; |
1527 | 1536 |
background-repeat: no-repeat; |
1528 | 1537 |
padding-left: 20px; |
1529 | 1538 |
} |
1530 |
.icon-only { |
|
1539 | ||
1540 |
a svg, .icon-svg svg { |
|
1541 |
color: var(--color-link-icon); |
|
1542 |
margin-right: 4px; |
|
1543 |
} |
|
1544 |
a:hover svg, a:active svg { |
|
1545 |
color: var(--color-active-icon); |
|
1546 |
} |
|
1547 | ||
1548 |
.icon-only:not(.icon-svg) { |
|
1531 | 1549 |
background-position: 0% 50%; |
1532 | 1550 |
background-repeat: no-repeat; |
1533 | 1551 |
padding-left: 16px; |
... | ... | |
1544 | 1562 |
content: "\a0"; |
1545 | 1563 |
} |
1546 | 1564 | |
1547 |
.icon-add { background-image: url(../images/add.png); } |
|
1548 |
.icon-edit { background-image: url(../images/edit.png); } |
|
1549 |
.icon-copy { background-image: url(../images/copy.png); } |
|
1550 |
.icon-duplicate { background-image: url(../images/duplicate.png); } |
|
1551 |
.icon-del { background-image: url(../images/delete.png); } |
|
1552 |
.icon-move { background-image: url(../images/move.png); } |
|
1553 |
.icon-save { background-image: url(../images/save.png); } |
|
1554 |
.icon-download { background-image: url(../images/download.png); } |
|
1555 |
.icon-cancel { background-image: url(../images/cancel.png); } |
|
1556 |
.icon-multiple { background-image: url(../images/table_multiple.png); } |
|
1557 |
.icon-folder { background-image: url(../images/folder.png); } |
|
1558 |
.open .icon-folder { background-image: url(../images/folder_open.png); } |
|
1559 |
.icon-package { background-image: url(../images/package.png); } |
|
1560 |
.icon-user { background-image: url(../images/user.png); } |
|
1561 |
.icon-project, .icon-projects { background-image: url(../images/projects.png); } |
|
1562 |
.icon-help { background-image: url(../images/help.png); } |
|
1563 |
.icon-attachment { background-image: url(../images/attachment.png); } |
|
1564 |
.icon-history { background-image: url(../images/history.png); } |
|
1565 |
.icon-time-entry, .icon-time { background-image: url(../images/time.png); } |
|
1566 |
.icon-time-add { background-image: url(../images/time_add.png); } |
|
1567 |
.icon-stats { background-image: url(../images/stats.png); } |
|
1568 |
.icon-warning { background-image: url(../images/warning.png); } |
|
1569 |
.icon-error { background-image: url(../images/exclamation.png); } |
|
1570 |
.icon-fav { background-image: url(../images/fav.png); } |
|
1571 |
.icon-fav-off { background-image: url(../images/fav_off.png); } |
|
1572 |
.icon-reload { background-image: url(../images/reload.png); } |
|
1573 |
.icon-lock, .icon-locked { background-image: url(../images/locked.png); } |
|
1574 |
.icon-unlock { background-image: url(../images/unlock.png); } |
|
1575 |
.icon-checked { background-image: url(../images/toggle_check.png); } |
|
1576 |
.icon-report { background-image: url(../images/report.png); } |
|
1577 |
.icon-comment, .icon-comments { background-image: url(../images/comment.png); } |
|
1578 |
.icon-summary { background-image: url(../images/lightning.png); } |
|
1579 |
.icon-server-authentication { background-image: url(../images/server_key.png); } |
|
1580 |
.icon-issue { background-image: url(../images/ticket.png); } |
|
1581 |
.icon-zoom-in { background-image: url(../images/zoom_in.png); } |
|
1582 |
.icon-zoom-out { background-image: url(../images/zoom_out.png); } |
|
1583 |
.icon-magnifier { background-image: url(../images/magnifier.png); } |
|
1584 |
.icon-passwd { background-image: url(../images/textfield_key.png); } |
|
1585 |
.icon-arrow-right, .icon-test, .icon-sticky { background-image: url(../images/bullet_go.png); } |
|
1586 |
.icon-email { background-image: url(../images/email.png); } |
|
1587 |
.icon-email-disabled { background-image: url(../images/email_disabled.png); } |
|
1588 |
.icon-email-add { background-image: url(../images/email_add.png); } |
|
1589 |
.icon-ok { background-image: url(../images/true.png); } |
|
1590 |
.icon-not-ok { background-image: url(../images/false.png); } |
|
1591 |
.icon-link-break { background-image: url(../images/link_break.png); } |
|
1592 |
.icon-list { background-image: url(../images/text_list_bullets.png); } |
|
1593 |
.icon-close { background-image: url(../images/close.png); } |
|
1594 |
.icon-close:hover { background-image: url(../images/close_hl.png); } |
|
1595 |
.icon-settings { background-image: url(../images/changeset.png); } |
|
1596 |
.icon-group, .icon-groupnonmember, .icon-groupanonymous { background-image: url(../images/group.png); } |
|
1597 |
.icon-roles { background-image: url(../images/database_key.png); } |
|
1598 |
.icon-issue-edit { background-image: url(../images/ticket_edit.png); } |
|
1599 |
.icon-workflows { background-image: url(../images/ticket_go.png); } |
|
1600 |
.icon-custom-fields { background-image: url(../images/textfield.png); } |
|
1601 |
.icon-plugins { background-image: url(../images/plugin.png); } |
|
1602 |
.icon-news { background-image: url(../images/news.png); } |
|
1603 |
.icon-issue-closed { background-image: url(../images/ticket_checked.png); } |
|
1604 |
.icon-issue-note { background-image: url(../images/ticket_note.png); } |
|
1605 |
.icon-changeset { background-image: url(../images/changeset.png); } |
|
1606 |
.icon-message { background-image: url(../images/message.png); } |
|
1607 |
.icon-reply { background-image: url(../images/comments.png); } |
|
1608 |
.icon-wiki-page { background-image: url(../images/wiki_edit.png); } |
|
1609 |
.icon-document { background-image: url(../images/document.png); } |
|
1610 |
.icon-project { background-image: url(../images/projects.png); } |
|
1611 |
.icon-add-bullet { background-image: url(../images/bullet_add.png); } |
|
1612 |
.icon-shared { background-image: url(../images/link.png); } |
|
1613 |
.icon-actions { background-image: url(../images/3_bullets.png); } |
|
1614 |
.icon-sort-handle { background-image: url(../images/reorder.png); } |
|
1615 |
.icon-expanded { background-image: url(../images/arrow_down.png); } |
|
1616 |
.icon-collapsed { background-image: url(../images/arrow_right.png); } |
|
1617 |
.icon-bookmark { background-image: url(../images/tag_blue_delete.png); } |
|
1618 |
.icon-bookmark-off { background-image: url(../images/tag_blue_add.png); } |
|
1619 |
.icon-bookmarked-project { background-image: url(../images/tag_blue.png); } |
|
1620 |
.icon-sorted-asc { background-image: url(../images/arrow_down.png); } |
|
1621 |
.icon-sorted-desc { background-image: url(../images/arrow_up.png); } |
|
1622 |
.icon-toggle-plus { background-image: url(../images/bullet_toggle_plus.png) } |
|
1623 |
.icon-toggle-minus { background-image: url(../images/bullet_toggle_minus.png) } |
|
1624 |
.icon-clear-query { background-image: url(../images/close_hl.png); } |
|
1625 |
.icon-import { background-image: url(../images/database_go.png); } |
|
1565 |
.icon-add:not(.icon-svg) { background-image: url(../images/add.png); } |
|
1566 |
.icon-edit:not(.icon-svg) { background-image: url(../images/edit.png); } |
|
1567 |
.icon-copy:not(.icon-svg) { background-image: url(../images/copy.png); } |
|
1568 |
.icon-duplicate:not(.icon-svg) { background-image: url(../images/duplicate.png); } |
|
1569 |
.icon-del:not(.icon-svg) { background-image: url(../images/delete.png); } |
|
1570 |
.icon-move:not(.icon-svg) { background-image: url(../images/move.png); } |
|
1571 |
.icon-save:not(.icon-svg) { background-image: url(../images/save.png); } |
|
1572 |
.icon-download:not(.icon-svg) { background-image: url(../images/download.png); } |
|
1573 |
.icon-cancel:not(.icon-svg) { background-image: url(../images/cancel.png); } |
|
1574 |
.icon-multiple:not(.icon-svg) { background-image: url(../images/table_multiple.png); } |
|
1575 |
.icon-folder:not(.icon-svg) { background-image: url(../images/folder.png); } |
|
1576 |
.open .icon-folder:not(.icon-svg) { background-image: url(../images/folder_open.png); } |
|
1577 |
.icon-package:not(.icon-svg) { background-image: url(../images/package.png); } |
|
1578 |
.icon-user:not(.icon-svg) { background-image: url(../images/user.png); } |
|
1579 |
.icon-project:not(.icon-svg), .icon-projects:not(.icon-svg) { background-image: url(../images/projects.png); } |
|
1580 |
.icon-help:not(.icon-svg) { background-image: url(../images/help.png); } |
|
1581 |
.icon-attachment:not(.icon-svg) { background-image: url(../images/attachment.png); } |
|
1582 |
.icon-history:not(.icon-svg) { background-image: url(../images/history.png); } |
|
1583 |
.icon-time-entry:not(.icon-svg), .icon-time:not(.icon-svg) { background-image: url(../images/time.png); } |
|
1584 |
.icon-time-add:not(.icon-svg) { background-image: url(../images/time_add.png); } |
|
1585 |
.icon-stats:not(.icon-svg) { background-image: url(../images/stats.png); } |
|
1586 |
.icon-warning:not(.icon-svg) { background-image: url(../images/warning.png); } |
|
1587 |
.icon-warning.icon-svg svg { color: var(--color-warning-icon) } |
|
1588 |
.icon-error:not(.icon-svg) { background-image: url(../images/exclamation.png); } |
|
1589 |
.icon-error.icon-svg svg { color: var(--color-error-icon) } |
|
1590 |
.icon-fav:not(.icon-svg) { background-image: url(../images/fav.png); } |
|
1591 |
.icon-fav-off:not(.icon-svg) { background-image: url(../images/fav_off.png); } |
|
1592 |
.icon-reload:not(.icon-svg) { background-image: url(../images/reload.png); } |
|
1593 |
.icon-lock:not(.icon-svg), .icon-locked:not(.icon-svg) { background-image: url(../images/locked.png); } |
|
1594 |
.icon-unlock:not(.icon-svg) { background-image: url(../images/unlock.png); } |
|
1595 |
.icon-checked:not(.icon-svg) { background-image: url(../images/toggle_check.png); } |
|
1596 |
.icon-checked.icon-svg svg { color: var(--color-success-icon) } |
|
1597 |
.icon-report:not(.icon-svg) { background-image: url(../images/report.png); } |
|
1598 |
.icon-comment:not(.icon-svg), .icon-comments:not(.icon-svg) { background-image: url(../images/comment.png); } |
|
1599 |
.icon-summary:not(.icon-svg) { background-image: url(../images/lightning.png); } |
|
1600 |
.icon-server-authentication:not(.icon-svg) { background-image: url(../images/server_key.png); } |
|
1601 |
.icon-issue:not(.icon-svg) { background-image: url(../images/ticket.png); } |
|
1602 |
.icon-zoom-in:not(.icon-svg) { background-image: url(../images/zoom_in.png); } |
|
1603 |
.icon-zoom-out:not(.icon-svg) { background-image: url(../images/zoom_out.png); } |
|
1604 |
.icon-magnifier:not(.icon-svg) { background-image: url(../images/magnifier.png); } |
|
1605 |
.icon-passwd:not(.icon-svg) { background-image: url(../images/textfield_key.png); } |
|
1606 |
.icon-arrow-right:not(.icon-svg), .icon-test:not(.icon-svg), .icon-sticky:not(.icon-svg) { background-image: url(../images/bullet_go.png); } |
|
1607 |
.icon-email:not(.icon-svg) { background-image: url(../images/email.png); } |
|
1608 |
.icon-email-disabled:not(.icon-svg) { background-image: url(../images/email_disabled.png); } |
|
1609 |
.icon-email-add:not(.icon-svg) { background-image: url(../images/email_add.png); } |
|
1610 |
.icon-ok:not(.icon-svg) { background-image: url(../images/true.png); } |
|
1611 |
.icon-ok.icon-svg svg { color: var(--color-success-icon) } |
|
1612 |
.icon-not-ok:not(.icon-svg) { background-image: url(../images/false.png); } |
|
1613 |
.icon-not-ok.icon-svg svg { color: var(--color-error-icon) } |
|
1614 |
.icon-link-break:not(.icon-svg) { background-image: url(../images/link_break.png); } |
|
1615 |
.icon-list:not(.icon-svg) { background-image: url(../images/text_list_bullets.png); } |
|
1616 |
.icon-close:not(.icon-svg) { background-image: url(../images/close.png); } |
|
1617 |
.icon-close:hover:not(.icon-svg) { background-image: url(../images/close_hl.png); } |
|
1618 |
.icon-settings:not(.icon-svg) { background-image: url(../images/changeset.png); } |
|
1619 |
.icon-group:not(.icon-svg), .icon-groupnonmember:not(.icon-svg), .icon-groupanonymous:not(.icon-svg) { background-image: url(../images/group.png); } |
|
1620 |
.icon-roles:not(.icon-svg) { background-image: url(../images/database_key.png); } |
|
1621 |
.icon-issue-edit:not(.icon-svg) { background-image: url(../images/ticket_edit.png); } |
|
1622 |
.icon-workflows:not(.icon-svg) { background-image: url(../images/ticket_go.png); } |
|
1623 |
.icon-custom-fields:not(.icon-svg) { background-image: url(../images/textfield.png); } |
|
1624 |
.icon-plugins:not(.icon-svg) { background-image: url(../images/plugin.png); } |
|
1625 |
.icon-news:not(.icon-svg) { background-image: url(../images/news.png); } |
|
1626 |
.icon-issue-closed:not(.icon-svg) { background-image: url(../images/ticket_checked.png); } |
|
1627 |
.icon-issue-note:not(.icon-svg) { background-image: url(../images/ticket_note.png); } |
|
1628 |
.icon-changeset:not(.icon-svg) { background-image: url(../images/changeset.png); } |
|
1629 |
.icon-message:not(.icon-svg) { background-image: url(../images/message.png); } |
|
1630 |
.icon-reply:not(.icon-svg) { background-image: url(../images/comments.png); } |
|
1631 |
.icon-wiki-page:not(.icon-svg) { background-image: url(../images/wiki_edit.png); } |
|
1632 |
.icon-document:not(.icon-svg) { background-image: url(../images/document.png); } |
|
1633 |
.icon-project:not(.icon-svg) { background-image: url(../images/projects.png); } |
|
1634 |
.icon-add-bullet:not(.icon-svg) { background-image: url(../images/bullet_add.png); } |
|
1635 |
.icon-shared:not(.icon-svg) { background-image: url(../images/link.png); } |
|
1636 |
.icon-actions:not(.icon-svg) { background-image: url(../images/3_bullets.png); } |
|
1637 |
.icon-sort-handle:not(.icon-svg) { background-image: url(../images/reorder.png); } |
|
1638 |
.icon-expanded:not(.icon-svg) { background-image: url(../images/arrow_down.png); } |
|
1639 |
.icon-collapsed:not(.icon-svg) { background-image: url(../images/arrow_right.png); } |
|
1640 |
.icon-bookmark:not(.icon-svg) { background-image: url(../images/tag_blue_delete.png); } |
|
1641 |
.icon-bookmark-off:not(.icon-svg) { background-image: url(../images/tag_blue_add.png); } |
|
1642 |
.icon-bookmarked-project:not(.icon-svg) { background-image: url(../images/tag_blue.png); } |
|
1643 |
.icon-sorted-asc:not(.icon-svg) { background-image: url(../images/arrow_down.png); } |
|
1644 |
.icon-sorted-desc:not(.icon-svg) { background-image: url(../images/arrow_up.png); } |
|
1645 |
.icon-toggle-plus:not(.icon-svg) { background-image: url(../images/bullet_toggle_plus.png) } |
|
1646 |
.icon-toggle-minus:not(.icon-svg) { background-image: url(../images/bullet_toggle_minus.png) } |
|
1647 |
.icon-clear-query:not(.icon-svg) { background-image: url(../images/close_hl.png); } |
|
1648 |
.icon-import:not(.icon-svg) { background-image: url(../images/database_go.png); } |
|
1626 | 1649 | |
1627 | 1650 |
.icon-file { background-image: url(../images/files/default.png); } |
1628 | 1651 |
.icon-file.text-plain { background-image: url(../images/files/text.png); } |
... | ... | |
1642 | 1665 |
.icon-file.application-pdf { background-image: url(../images/files/pdf.png); } |
1643 | 1666 |
.icon-file.application-zip { background-image: url(../images/files/zip.png); } |
1644 | 1667 |
.icon-file.application-gzip { background-image: url(../images/files/zip.png); } |
1645 |
.icon-copy-link { background-image: url(../images/copy_link.png); } |
|
1668 |
.icon-copy-link:not(.icon-svg) { background-image: url(../images/copy_link.png); }
|
|
1646 | 1669 | |
1647 | 1670 |
.sort-handle.ajax-loading { background-image: url(../images/loading.gif); } |
1648 | 1671 |
tr.ui-sortable-helper { border:1px solid #e4e4e4; } |
... | ... | |
1883 | 1906 |
display: inline; |
1884 | 1907 |
opacity: 1; |
1885 | 1908 |
} |
1909 | ||
1910 |
:root { |
|
1911 |
--color-link-icon: #169; |
|
1912 |
--color-active-icon: #c61a1a; |
|
1913 |
--color-success-icon: #55ab4f; |
|
1914 |
--color-warning-icon: #ffae42; |
|
1915 |
--color-error-icon: #EC0B19; |
|
1916 |
} |
test/functional/issues_controller_test.rb | ||
---|---|---|
764 | 764 |
def test_index_should_omit_page_param_in_export_links |
765 | 765 |
get(:index, :params => {:page => 2}) |
766 | 766 |
assert_response :success |
767 |
assert_select 'a.atom[href="/issues.atom"]' |
|
767 |
assert_select 'a.icon-atom[href="/issues.atom"]'
|
|
768 | 768 |
assert_select 'a.csv[href="/issues.csv"]' |
769 | 769 |
assert_select 'a.pdf[href="/issues.pdf"]' |
770 | 770 |
assert_select 'form#csv-export-form[action="/issues.csv"]' |
test/unit/lib/redmine/icon_test.rb | ||
---|---|---|
1 |
# frozen_string_literal: true |
|
2 | ||
3 |
# Redmine - project management software |
|
4 |
# Copyright (C) 2006-2022 Jean-Philippe Lang |
|
5 |
# |
|
6 |
# This program is free software; you can redistribute it and/or |
|
7 |
# modify it under the terms of the GNU General Public License |
|
8 |
# as published by the Free Software Foundation; either version 2 |
|
9 |
# of the License, or (at your option) any later version. |
|
10 |
# |
|
11 |
# This program is distributed in the hope that it will be useful, |
|
12 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 |
# GNU General Public License for more details. |
|
15 |
# |
|
16 |
# You should have received a copy of the GNU General Public License |
|
17 |
# along with this program; if not, write to the Free Software |
|
18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
19 | ||
20 |
require File.expand_path('../../../../test_helper', __FILE__) |
|
21 | ||
22 |
class IconsHelperTest < Redmine::HelperTest |
|
23 |
def setup |
|
24 |
Rails.application.config.redmine_enable_svg_icon = true |
|
25 |
end |
|
26 | ||
27 |
def teardown |
|
28 |
Rails.application.config.redmine_enable_svg_icon = false |
|
29 |
end |
|
30 | ||
31 |
def test_common_svg_icon |
|
32 |
html1 = Regexp.quote '<a class="icon icon-add icon-svg" href="/test"><svg class="s16" xmlns="http://www.w3.org/2000/svg"><use href="/icons/circle-plus.svg?' |
|
33 |
html2 = Regexp.quote '#icon"></use></svg>test</a>' |
|
34 |
assert_match /#{html1}\d{10}#{html2}/, link_to('test', '/test', class: "icon icon-add") |
|
35 |
end |
|
36 | ||
37 |
def test_class_attrs_as_array |
|
38 |
html1 = Regexp.quote '<a class="icon icon-add icon-svg" href="/test"><svg class="s16" xmlns="http://www.w3.org/2000/svg"><use href="/icons/circle-plus.svg?' |
|
39 |
html2 = Regexp.quote '#icon"></use></svg>test</a>' |
|
40 |
assert_match /#{html1}\d{10}#{html2}/, link_to('test', '/test', class: %w(icon icon-add)) |
|
41 |
end |
|
42 | ||
43 |
def test_file_svg_icon |
|
44 |
html1 = Regexp.quote '<a class="icon icon-file text-x-ruby icon-svg" href="/test.rb"><svg class="s16" xmlns="http://www.w3.org/2000/svg"><use href="/icons/language-ruby.svg?' |
|
45 |
html2 = Regexp.quote '#icon"></use></svg>test.rb</a>' |
|
46 |
assert_match /#{html1}\d{10}#{html2}/, link_to('test.rb', '/test.rb', class: "icon icon-file text-x-ruby") |
|
47 |
end |
|
48 | ||
49 |
def test_unknown_svg_icon |
|
50 |
html = '<a class="icon icon-foo" href="/test"><!-- SVG icon-foo not found -->test</a>' |
|
51 |
assert_equal html, link_to('test', '/test', class: "icon icon-foo") |
|
52 |
end |
|
53 |
end |