Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEATURE: support custom icons in themes #7155

Merged
merged 6 commits into from Mar 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 9 additions & 6 deletions app/controllers/svg_sprite_controller.rb
Expand Up @@ -27,13 +27,16 @@ def show
end

def search
keyword = params.require(:keyword)
data = SvgSprite.search(keyword)
RailsMultisite::ConnectionManagement.with_hostname(params[:hostname]) do

keyword = params.require(:keyword)
data = SvgSprite.search(keyword)

if data.blank?
render body: nil, status: 404
else
render plain: data.inspect, disposition: nil, content_type: 'text/plain'
if data.blank?
render body: nil, status: 404
else
render plain: data.inspect, disposition: nil, content_type: 'text/plain'
end
end
end
end
1 change: 1 addition & 0 deletions app/models/theme_field.rb
Expand Up @@ -9,6 +9,7 @@ class ThemeField < ActiveRecord::Base

after_commit do |field|
SvgSprite.expire_cache if field.target_id == Theme.targets[:settings]
SvgSprite.expire_cache if field.name == SvgSprite.theme_sprite_variable_name
end

scope :find_by_theme_ids, ->(theme_ids) {
Expand Down
52 changes: 46 additions & 6 deletions lib/svg_sprite/svg_sprite.rb
Expand Up @@ -189,8 +189,28 @@ module SvgSprite

FA_ICON_MAP = { 'far fa-' => 'far-', 'fab fa-' => 'fab-', 'fas fa-' => '', 'fa-' => '' }

SVG_SPRITE_PATHS = Dir.glob(["#{Rails.root}/vendor/assets/svg-icons/**/*.svg",
"#{Rails.root}/plugins/*/svg-icons/*.svg"])
CORE_SVG_SPRITES = Dir.glob("#{Rails.root}/vendor/assets/svg-icons/**/*.svg")

THEME_SPRITE_VAR_NAME = "icons-sprite"

def self.custom_svg_sprites(theme_ids = [])
custom_sprite_paths = Dir.glob("#{Rails.root}/plugins/*/svg-icons/*.svg")

ThemeField.where(type_id: ThemeField.types[:theme_upload_var], name: THEME_SPRITE_VAR_NAME, theme_id: Theme.transform_ids(theme_ids))
.pluck(:upload_id).each do |upload_id|

upload = Upload.find(upload_id)
original_path = Discourse.store.path_for(upload)
if original_path.blank?
external_copy = Discourse.store.download(upload) rescue nil
original_path = external_copy.try(:path)
end

custom_sprite_paths << Discourse.store.path_for(upload) if original_path.present?
end

custom_sprite_paths
end

def self.all_icons(theme_ids = [])
get_set_cache("icons_#{Theme.transform_ids(theme_ids).join(',')}") do
Expand All @@ -200,6 +220,7 @@ def self.all_icons(theme_ids = [])
.merge(badge_icons)
.merge(group_icons)
.merge(theme_icons(theme_ids))
.merge(custom_icons(theme_ids))
.delete_if { |i| i.blank? || i.include?("/") }
.map! { |i| process(i.dup) }
.merge(SVG_ICONS)
Expand All @@ -221,19 +242,21 @@ def self.expire_cache
cache&.clear
end

def self.sprite_sources(theme_ids)
CORE_SVG_SPRITES | custom_svg_sprites(theme_ids)
end

def self.bundle(theme_ids = [])
icons = all_icons(theme_ids)

doc = File.open("#{Rails.root}/vendor/assets/svg-icons/fontawesome/solid.svg") { |f| Nokogiri::XML(f) }

svg_subset = """<!--
Discourse SVG subset of Font Awesome Free by @fontawesome - https://fontawesome.com
License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
-->
<svg xmlns='http://www.w3.org/2000/svg' style='display: none;'>
""".dup

SVG_SPRITE_PATHS.each do |fname|
sprite_sources(theme_ids).each do |fname|
svg_file = Nokogiri::XML(File.open(fname)) do |config|
config.options = Nokogiri::XML::ParseOptions::NOBLANKS
end
Expand All @@ -256,7 +279,7 @@ def self.bundle(theme_ids = [])
def self.search(searched_icon)
searched_icon = process(searched_icon.dup)

SVG_SPRITE_PATHS.each do |fname|
sprite_sources([SiteSetting.default_theme_id]).each do |fname|
svg_file = Nokogiri::XML(File.open(fname))
svg_filename = "#{File.basename(fname, ".svg")}"

Expand All @@ -274,6 +297,10 @@ def self.search(searched_icon)
false
end

def self.theme_sprite_variable_name
THEME_SPRITE_VAR_NAME
end

def self.prepare_symbol(symbol, svg_filename)
icon_id = symbol.attr('id')

Expand Down Expand Up @@ -331,6 +358,19 @@ def self.theme_icons(theme_ids)
theme_icon_settings
end

def self.custom_icons(theme_ids)
# Automatically register icons in sprites added via themes or plugins
icons = []
custom_svg_sprites(theme_ids).each do |fname|
svg_file = Nokogiri::XML(File.open(fname))

svg_file.css('symbol').each do |sym|
icons << sym.attributes['id'].value
end
end
icons
end

def self.fa4_shim_file
"#{Rails.root}/lib/svg_sprite/fa4-renames.json"
end
Expand Down
1 change: 1 addition & 0 deletions lib/upload_creator.rb
Expand Up @@ -37,6 +37,7 @@ def create_for(user_id)

is_image = FileHelper.is_supported_image?(@filename)
is_image ||= @image_info && FileHelper.is_supported_image?("test.#{@image_info.type}")
is_image = false if @opts[:for_theme]

if is_image
extract_image_info!
Expand Down
13 changes: 13 additions & 0 deletions spec/components/svg_sprite/svg_sprite_spec.rb
Expand Up @@ -99,6 +99,19 @@
expect(SvgSprite.all_icons([parent_theme.id])).to include("dragon")
end

it 'includes custom icons from a sprite in a theme' do
theme = Fabricate(:theme)
fname = "custom-theme-icon-sprite.svg"

upload = UploadCreator.new(file_from_fixtures(fname), fname, for_theme: true).create_for(-1)

theme.set_field(target: :common, name: SvgSprite.theme_sprite_variable_name, upload_id: upload.id, type: :theme_upload_var)
theme.save!

expect(Upload.where(id: upload.id)).to be_exist
expect(SvgSprite.bundle([theme.id])).to match(/my-custom-theme-icon/)
end

it 'includes icons from SiteSettings' do
SiteSetting.svg_icon_subset = "blender|drafting-compass|fab-bandcamp"

Expand Down
6 changes: 6 additions & 0 deletions spec/fixtures/images/custom-theme-icon-sprite.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions spec/requests/svg_sprite_controller_spec.rb
Expand Up @@ -47,5 +47,23 @@
get "/svg-sprite/search/fa-not-a-valid-icon"
expect(response.status).to eq(404)
end

it "should find a custom icon in default theme" do
theme = Fabricate(:theme)
fname = "custom-theme-icon-sprite.svg"

upload = UploadCreator.new(file_from_fixtures(fname), fname, for_theme: true).create_for(-1)

theme.set_field(target: :common, name: SvgSprite.theme_sprite_variable_name, upload_id: upload.id, type: :theme_upload_var)
theme.save!

SiteSetting.default_theme_id = theme.id

user = sign_in(Fabricate(:user))

get "/svg-sprite/search/fa-my-custom-theme-icon"
expect(response.status).to eq(200)
expect(response.body).to include('my-custom-theme-icon')
end
end
end