Feature #39111 » 0001v2-Add-propshaft-to-enable-asset-pipeline.patch
| .gitignore | ||
|---|---|---|
| 24 | 24 |
/log/mongrel_debug |
| 25 | 25 |
/plugins/* |
| 26 | 26 |
!/plugins/README |
| 27 |
/public/assets/* |
|
| 27 | 28 |
/public/dispatch.* |
| 28 | 29 |
/public/plugin_assets/* |
| 29 | 30 |
/public/themes/* |
| .hgignore | ||
|---|---|---|
| 24 | 24 |
lib/redmine/scm/adapters/mercurial/redminehelper.pyo |
| 25 | 25 |
log/*.log* |
| 26 | 26 |
log/mongrel_debug |
| 27 |
public/assets/* |
|
| 27 | 28 |
public/dispatch.* |
| 28 | 29 |
public/plugin_assets/* |
| 29 | 30 |
tmp/* |
| Gemfile | ||
|---|---|---|
| 21 | 21 |
gem 'net-pop', '~> 0.1.2' |
| 22 | 22 |
gem 'net-smtp', '~> 0.3.3' |
| 23 | 23 |
gem 'rexml', require: false if Gem.ruby_version >= Gem::Version.new('3.0')
|
| 24 |
gem 'propshaft', '~> 0.8.0' |
|
| 24 | 25 | |
| 25 | 26 |
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem |
| 26 | 27 |
gem 'tzinfo-data', platforms: [:mingw, :x64_mingw, :mswin] |
| app/helpers/application_helper.rb | ||
|---|---|---|
| 1668 | 1668 |
plugin = options.delete(:plugin) |
| 1669 | 1669 |
sources = sources.map do |source| |
| 1670 | 1670 |
if plugin |
| 1671 |
"/plugin_assets/#{plugin}/stylesheets/#{source}"
|
|
| 1671 |
"plugin_assets/#{plugin}/#{source}"
|
|
| 1672 | 1672 |
elsif current_theme && current_theme.stylesheets.include?(source) |
| 1673 | 1673 |
current_theme.stylesheet_path(source) |
| 1674 | 1674 |
else |
| ... | ... | |
| 1685 | 1685 |
# |
| 1686 | 1686 |
def image_tag(source, options={})
|
| 1687 | 1687 |
if plugin = options.delete(:plugin) |
| 1688 |
source = "/plugin_assets/#{plugin}/images/#{source}"
|
|
| 1688 |
source = "plugin_assets/#{plugin}/#{source}"
|
|
| 1689 | 1689 |
elsif current_theme && current_theme.images.include?(source) |
| 1690 | 1690 |
source = current_theme.image_path(source) |
| 1691 | 1691 |
end |
| ... | ... | |
| 1702 | 1702 |
if plugin = options.delete(:plugin) |
| 1703 | 1703 |
sources = sources.map do |source| |
| 1704 | 1704 |
if plugin |
| 1705 |
"/plugin_assets/#{plugin}/javascripts/#{source}"
|
|
| 1705 |
"plugin_assets/#{plugin}/#{source}"
|
|
| 1706 | 1706 |
else |
| 1707 | 1707 |
source |
| 1708 | 1708 |
end |
| config/application.rb | ||
|---|---|---|
| 88 | 88 |
# Sets default plugin directory |
| 89 | 89 |
config.redmine_plugins_directory = 'plugins' |
| 90 | 90 | |
| 91 |
# Paths for plugin and theme assets. Nothing is set here, as the actual |
|
| 92 |
# configuration is performed in the initializer. |
|
| 93 |
config.assets.redmine_extension_paths = [] |
|
| 94 | ||
| 91 | 95 |
# Configure log level here so that additional environment file |
| 92 | 96 |
# can change it (environments/ENV.rb would take precedence over it) |
| 93 | 97 |
config.log_level = Rails.env.production? ? :info : :debug |
| config/environments/production.rb | ||
|---|---|---|
| 93 | 93 | |
| 94 | 94 |
# No email in production log |
| 95 | 95 |
config.action_mailer.logger = nil |
| 96 | ||
| 97 |
config.assets.redmine_detect_update = true |
|
| 96 | 98 |
end |
| config/initializers/10-patches.rb | ||
|---|---|---|
| 164 | 164 | |
| 165 | 165 |
Mime::SET << 'api' |
| 166 | 166 | |
| 167 |
# Adds asset_id parameters to assets like Rails 3 to invalidate caches in browser |
|
| 168 |
module ActionView |
|
| 169 |
module Helpers |
|
| 170 |
module AssetUrlHelper |
|
| 171 |
@@cache_asset_timestamps = Rails.env.production? |
|
| 172 |
@@asset_timestamps_cache = {}
|
|
| 173 |
@@asset_timestamps_cache_guard = Mutex.new |
|
| 174 | ||
| 175 |
def asset_path_with_asset_id(source, options = {})
|
|
| 176 |
asset_id = rails_asset_id(source, options) |
|
| 177 |
unless asset_id.blank? |
|
| 178 |
source += "?#{asset_id}"
|
|
| 179 |
end |
|
| 180 |
asset_path(source, options.merge(skip_pipeline: true)) |
|
| 181 |
end |
|
| 182 |
alias :path_to_asset :asset_path_with_asset_id |
|
| 183 | ||
| 184 |
def rails_asset_id(source, options = {})
|
|
| 185 |
if asset_id = ENV["RAILS_ASSET_ID"] |
|
| 186 |
asset_id |
|
| 187 |
else |
|
| 188 |
if @@cache_asset_timestamps && (asset_id = @@asset_timestamps_cache[source]) |
|
| 189 |
asset_id |
|
| 190 |
else |
|
| 191 |
extname = compute_asset_extname(source, options) |
|
| 192 |
path = File.join(Rails.public_path, "#{source}#{extname}")
|
|
| 193 |
exist = false |
|
| 194 |
if File.exist? path |
|
| 195 |
exist = true |
|
| 196 |
else |
|
| 197 |
path = File.join(Rails.public_path, public_compute_asset_path("#{source}#{extname}", options))
|
|
| 198 |
if File.exist? path |
|
| 199 |
exist = true |
|
| 200 |
end |
|
| 201 |
end |
|
| 202 |
asset_id = exist ? File.mtime(path).to_i.to_s : '' |
|
| 203 | ||
| 204 |
if @@cache_asset_timestamps |
|
| 205 |
@@asset_timestamps_cache_guard.synchronize do |
|
| 206 |
@@asset_timestamps_cache[source] = asset_id |
|
| 207 |
end |
|
| 208 |
end |
|
| 209 | ||
| 210 |
asset_id |
|
| 211 |
end |
|
| 212 |
end |
|
| 213 |
end |
|
| 214 |
end |
|
| 215 |
end |
|
| 216 |
end |
|
| 217 | ||
| 218 | 167 |
# https://github.com/rack/rack/pull/1703 |
| 219 | 168 |
# TODO: remove this when Rack is updated to 3.0.0 |
| 220 | 169 |
require 'rack' |
| ... | ... | |
| 228 | 177 |
end |
| 229 | 178 |
end |
| 230 | 179 |
end |
| 180 | ||
| 181 |
module Propshaft |
|
| 182 |
Assembly.prepend(Module.new do |
|
| 183 |
def initialize(config) |
|
| 184 |
super |
|
| 185 |
if Rails.application.config.assets.redmine_detect_update && (!manifest_path.exist? || manifest_outdated?) |
|
| 186 |
processor.process |
|
| 187 |
end |
|
| 188 |
end |
|
| 189 | ||
| 190 |
def manifest_outdated? |
|
| 191 |
!!load_path.asset_files.detect{|f| f.mtime > manifest_path.mtime}
|
|
| 192 |
end |
|
| 193 | ||
| 194 |
def load_path |
|
| 195 |
@load_path ||= Redmine::AssetLoadPath.new(config) |
|
| 196 |
end |
|
| 197 |
end) |
|
| 198 | ||
| 199 |
Helper.prepend(Module.new do |
|
| 200 |
def compute_asset_path(path, options = {})
|
|
| 201 |
super |
|
| 202 |
rescue MissingAssetError => e |
|
| 203 |
File.join Rails.application.assets.resolver.prefix, path |
|
| 204 |
end |
|
| 205 |
end) |
|
| 206 |
end |
|
| config/initializers/30-redmine.rb | ||
|---|---|---|
| 18 | 18 |
end |
| 19 | 19 | |
| 20 | 20 |
Redmine::PluginLoader.load |
| 21 |
plugin_assets_reloader = Redmine::PluginLoader.create_assets_reloader |
|
| 22 | ||
| 23 |
Rails.application.reloaders << plugin_assets_reloader |
|
| 24 |
unless Redmine::Configuration['mirror_plugins_assets_on_startup'] == false |
|
| 25 |
plugin_assets_reloader.execute |
|
| 26 |
end |
|
| 27 | 21 | |
| 28 | 22 |
Rails.application.config.to_prepare do |
| 23 |
default_paths = [] |
|
| 24 |
default_paths << Rails.public_path.join('javascripts')
|
|
| 25 |
default_paths << Rails.public_path.join('stylesheets')
|
|
| 26 |
default_paths << Rails.public_path.join('images')
|
|
| 27 |
Rails.application.config.assets.redmine_default_asset_path = Redmine::AssetPath.new(Rails.public_path, default_paths) |
|
| 28 | ||
| 29 | 29 |
Redmine::FieldFormat::RecordList.subclasses.each do |klass| |
| 30 | 30 |
klass.instance.reset_target_class |
| 31 | 31 |
end |
| 32 | 32 | |
| 33 |
plugin_assets_reloader.execute_if_updated |
|
| 33 |
Redmine::Plugin.all.each do |plugin| |
|
| 34 |
paths = plugin.asset_paths |
|
| 35 |
Rails.application.config.assets.redmine_extension_paths << paths if paths.present? |
|
| 36 |
end |
|
| 37 | ||
| 38 |
Redmine::Themes.themes.each do |theme| |
|
| 39 |
paths = theme.asset_paths |
|
| 40 |
Rails.application.config.assets.redmine_extension_paths << paths if paths.present? |
|
| 41 |
end |
|
| 34 | 42 |
end |
| lib/redmine/asset_path.rb | ||
|---|---|---|
| 1 |
# frozen_string_literal: true |
|
| 2 |
# Redmine - project management software |
|
| 3 |
# Copyright (C) 2006-2023 Jean-Philippe Lang |
|
| 4 |
# |
|
| 5 |
# This program is free software; you can redistribute it and/or |
|
| 6 |
# modify it under the terms of the GNU General Public License |
|
| 7 |
# as published by the Free Software Foundation; either version 2 |
|
| 8 |
# of the License, or (at your option) any later version. |
|
| 9 |
# |
|
| 10 |
# This program is distributed in the hope that it will be useful, |
|
| 11 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 12 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 13 |
# GNU General Public License for more details. |
|
| 14 |
# |
|
| 15 |
# You should have received a copy of the GNU General Public License |
|
| 16 |
# along with this program; if not, write to the Free Software |
|
| 17 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
| 18 | ||
| 19 |
module Redmine |
|
| 20 |
class AssetPath |
|
| 21 | ||
| 22 |
attr_reader :paths, :prefix, :version |
|
| 23 | ||
| 24 |
def initialize(base_dir, paths, prefix=nil) |
|
| 25 |
@base_dir = base_dir |
|
| 26 |
@paths = paths |
|
| 27 |
@prefix = prefix |
|
| 28 |
@transition = Transition.new(src: Set.new, dest: Set.new) |
|
| 29 |
@version = Rails.application.config.assets.version |
|
| 30 |
end |
|
| 31 | ||
| 32 |
def update(transition_map:, assets:) |
|
| 33 |
each_file do |file, intermediate_path, logical_path| |
|
| 34 |
@transition.add_src intermediate_path, logical_path |
|
| 35 |
@transition.add_dest intermediate_path, logical_path |
|
| 36 | ||
| 37 |
asset = file.extname == '.css' ? Redmine::Asset.new(file, logical_path: logical_path, version: version, transition_map: transition_map) |
|
| 38 |
: Propshaft::Asset.new(file, logical_path: logical_path, version: version) |
|
| 39 |
assets[asset.logical_path.to_s] ||= asset |
|
| 40 |
end |
|
| 41 |
@transition.update(transition_map) |
|
| 42 |
nil |
|
| 43 |
end |
|
| 44 | ||
| 45 |
def each_file |
|
| 46 |
paths.each do |path| |
|
| 47 |
without_dotfiles(all_files_from_tree(path)).each do |file| |
|
| 48 |
relative_path = file.relative_path_from(path).to_s |
|
| 49 |
logical_path = prefix ? File.join(prefix, relative_path) : relative_path |
|
| 50 |
intermediate_path = Pathname.new("/#{prefix}").join(file.relative_path_from(@base_dir))
|
|
| 51 |
yield file, intermediate_path, logical_path |
|
| 52 |
end |
|
| 53 |
end |
|
| 54 |
end |
|
| 55 | ||
| 56 |
private |
|
| 57 | ||
| 58 |
Transition = Struct.new(:src, :dest, keyword_init: true) do |
|
| 59 | ||
| 60 |
def add_src(file, logical_path) |
|
| 61 |
src.add path_pair(file, logical_path) if file.extname == '.css' |
|
| 62 |
end |
|
| 63 | ||
| 64 |
def add_dest(file, logical_path) |
|
| 65 |
return if file.extname == '.js' || file.extname == '.map' |
|
| 66 | ||
| 67 |
# No parent-child directories are needed in dest. |
|
| 68 |
dirname = file.dirname |
|
| 69 |
if child = dest.find{|d| child_path? dirname, d[0]}
|
|
| 70 |
dest.delete child |
|
| 71 |
dest.add path_pair(file, logical_path) |
|
| 72 |
elsif !dest.any?{|d| parent_path? dirname, d[0]}
|
|
| 73 |
dest.add path_pair(file, logical_path) |
|
| 74 |
end |
|
| 75 |
end |
|
| 76 | ||
| 77 |
def path_pair(file, logical_path) |
|
| 78 |
[file.dirname, Pathname.new("/#{logical_path}").dirname]
|
|
| 79 |
end |
|
| 80 | ||
| 81 |
def parent_path?(path, other) |
|
| 82 |
return nil if other == path |
|
| 83 | ||
| 84 |
path.ascend.any?{|v| v == other}
|
|
| 85 |
end |
|
| 86 | ||
| 87 |
def child_path?(path, other) |
|
| 88 |
return nil if path == other |
|
| 89 | ||
| 90 |
other.ascend.any?{|v| v == path}
|
|
| 91 |
end |
|
| 92 | ||
| 93 |
def update(transition_map) |
|
| 94 |
product = src.to_a.product(dest.to_a).select{|t| t[0] != t[1]}
|
|
| 95 |
maps = product.map do |t| |
|
| 96 |
AssetPathMap.new(src: t[0][0], dest: t[1][0], logical_src: t[0][1], logical_dest: t[1][1]) |
|
| 97 |
end |
|
| 98 | ||
| 99 |
maps.each do |m| |
|
| 100 |
if m.before != m.after |
|
| 101 |
transition_map[m.dirname] ||= {}
|
|
| 102 |
transition_map[m.dirname][m.before] = m.after |
|
| 103 |
end |
|
| 104 |
end |
|
| 105 |
end |
|
| 106 |
end |
|
| 107 | ||
| 108 |
AssetPathMap = Struct.new(:src, :dest, :logical_src, :logical_dest, keyword_init: true) do |
|
| 109 | ||
| 110 |
def dirname |
|
| 111 |
key = logical_src.to_s.sub('/', '')
|
|
| 112 |
key == '' ? '.' : key |
|
| 113 |
end |
|
| 114 | ||
| 115 |
def before |
|
| 116 |
dest.relative_path_from(src).to_s |
|
| 117 |
end |
|
| 118 | ||
| 119 |
def after |
|
| 120 |
logical_dest.relative_path_from(logical_src).to_s |
|
| 121 |
end |
|
| 122 |
end |
|
| 123 | ||
| 124 |
def without_dotfiles(files) |
|
| 125 |
files.reject { |file| file.basename.to_s.starts_with?(".") }
|
|
| 126 |
end |
|
| 127 | ||
| 128 |
def all_files_from_tree(path) |
|
| 129 |
path.children.flat_map { |child| child.directory? ? all_files_from_tree(child) : child }
|
|
| 130 |
end |
|
| 131 |
end |
|
| 132 | ||
| 133 |
class AssetLoadPath < Propshaft::LoadPath |
|
| 134 | ||
| 135 |
attr_reader :extension_paths, :default_asset_path, :transition_map |
|
| 136 | ||
| 137 |
def initialize(config) |
|
| 138 |
@extension_paths = config.redmine_extension_paths |
|
| 139 |
@default_asset_path = config.redmine_default_asset_path |
|
| 140 | ||
| 141 |
super(config.paths, version: config.version) |
|
| 142 |
end |
|
| 143 | ||
| 144 |
def asset_files |
|
| 145 |
Enumerator.new do |y| |
|
| 146 |
Rails.logger.info all_paths |
|
| 147 |
all_paths.each do |path| |
|
| 148 |
next unless path.exist? |
|
| 149 |
without_dotfiles(all_files_from_tree(path)).each do |file| |
|
| 150 |
y << file |
|
| 151 |
end |
|
| 152 |
end |
|
| 153 |
end |
|
| 154 |
end |
|
| 155 | ||
| 156 |
def assets_by_path |
|
| 157 |
merge_required = @cached_assets_by_path == nil |
|
| 158 | ||
| 159 |
super |
|
| 160 | ||
| 161 |
if merge_required |
|
| 162 |
@transition_map = {}
|
|
| 163 |
default_asset_path.update(assets: @cached_assets_by_path, transition_map: transition_map) |
|
| 164 | ||
| 165 |
extension_paths.each do |asset_path| |
|
| 166 |
# Support link from extension assets to assets in the application |
|
| 167 |
default_asset_path.each_file do |file, intermediate_path, logical_path| |
|
| 168 |
asset_path.instance_eval { @transition.add_dest intermediate_path, logical_path }
|
|
| 169 |
end |
|
| 170 |
asset_path.update(assets: @cached_assets_by_path, transition_map: transition_map) |
|
| 171 |
end |
|
| 172 |
end |
|
| 173 |
@cached_assets_by_path |
|
| 174 |
end |
|
| 175 | ||
| 176 |
def cache_sweeper |
|
| 177 |
@cache_sweeper ||= begin |
|
| 178 |
exts_to_watch = Mime::EXTENSION_LOOKUP.map(&:first) |
|
| 179 |
files_to_watch = Array(all_paths).collect { |dir| [ dir.to_s, exts_to_watch ] }.to_h
|
|
| 180 | ||
| 181 |
Rails.application.config.file_watcher.new([], files_to_watch) do |
|
| 182 |
clear_cache |
|
| 183 |
end |
|
| 184 |
end |
|
| 185 |
end |
|
| 186 | ||
| 187 |
def all_paths |
|
| 188 |
[paths, default_asset_path.paths, extension_paths.map{|path| path.paths}].flatten.compact
|
|
| 189 |
end |
|
| 190 | ||
| 191 |
def clear_cache |
|
| 192 |
@transition_map = nil |
|
| 193 |
super |
|
| 194 |
end |
|
| 195 |
end |
|
| 196 | ||
| 197 |
class Asset < Propshaft::Asset |
|
| 198 | ||
| 199 |
def initialize(file, logical_path:, version:, transition_map:) |
|
| 200 |
@transition_map = transition_map |
|
| 201 |
super(file, logical_path: logical_path, version: version) |
|
| 202 |
end |
|
| 203 | ||
| 204 |
def content |
|
| 205 |
if conversion = @transition_map[logical_path.dirname.to_s] |
|
| 206 |
convert_path super, conversion |
|
| 207 |
else |
|
| 208 |
super |
|
| 209 |
end |
|
| 210 |
end |
|
| 211 | ||
| 212 |
ASSET_URL_PATTERN = /(url\(\s*["']?([^"'\s)]+)\s*["']?\s*\))/ |
|
| 213 |
def convert_path(input, conversion) |
|
| 214 |
input.gsub(ASSET_URL_PATTERN) do |matched| |
|
| 215 |
conversion.each do |key, val| |
|
| 216 |
matched.sub!(key, val) |
|
| 217 |
end |
|
| 218 |
matched |
|
| 219 |
end |
|
| 220 |
end |
|
| 221 |
end |
|
| 222 |
end |
|
| lib/redmine/hook/view_listener.rb | ||
|---|---|---|
| 34 | 34 |
include ActionView::Helpers::TextHelper |
| 35 | 35 |
include Rails.application.routes.url_helpers |
| 36 | 36 |
include ApplicationHelper |
| 37 |
include Propshaft::Helper |
|
| 37 | 38 | |
| 38 | 39 |
# Default to creating links using only the path. Subclasses can |
| 39 | 40 |
# change this default as needed |
| lib/redmine/plugin.rb | ||
|---|---|---|
| 186 | 186 |
path.assets_dir |
| 187 | 187 |
end |
| 188 | 188 | |
| 189 |
def asset_prefix |
|
| 190 |
File.join(self.class.public_directory.basename, id.to_s) |
|
| 191 |
end |
|
| 192 | ||
| 193 |
def asset_paths |
|
| 194 |
if path.has_assets_dir? |
|
| 195 |
base_dir = Pathname.new(path.assets_dir) |
|
| 196 |
paths = base_dir.children.filter_map{|child| child if child.directory? }
|
|
| 197 |
Redmine::AssetPath.new(base_dir, paths, asset_prefix) |
|
| 198 |
end |
|
| 199 |
end |
|
| 200 | ||
| 189 | 201 |
def <=>(plugin) |
| 190 | 202 |
return nil unless plugin.is_a?(Plugin) |
| 191 | 203 | |
| lib/redmine/themes.rb | ||
|---|---|---|
| 91 | 91 |
end |
| 92 | 92 | |
| 93 | 93 |
def stylesheet_path(source) |
| 94 |
"/themes/#{dir}/stylesheets/#{source}"
|
|
| 94 |
"#{asset_prefix}#{source}"
|
|
| 95 | 95 |
end |
| 96 | 96 | |
| 97 | 97 |
def image_path(source) |
| 98 |
"/themes/#{dir}/images/#{source}"
|
|
| 98 |
"#{asset_prefix}#{source}"
|
|
| 99 | 99 |
end |
| 100 | 100 | |
| 101 | 101 |
def javascript_path(source) |
| 102 |
"/themes/#{dir}/javascripts/#{source}"
|
|
| 102 |
"#{asset_prefix}#{source}"
|
|
| 103 | 103 |
end |
| 104 | 104 | |
| 105 | 105 |
def favicon_path |
| 106 |
"/themes/#{dir}/favicon/#{favicon}"
|
|
| 106 |
"#{asset_prefix}#{favicon}"
|
|
| 107 |
end |
|
| 108 | ||
| 109 |
def asset_prefix |
|
| 110 |
"themes/#{dir}/"
|
|
| 111 |
end |
|
| 112 | ||
| 113 |
def asset_paths |
|
| 114 |
base_dir = Pathname.new(path) |
|
| 115 |
paths = base_dir.children.filter_map{|child| child if child.directory? &&
|
|
| 116 |
child.basename.to_s != "src" && |
|
| 117 |
!child.basename.to_s.start_with?('.') }
|
|
| 118 |
Redmine::AssetPath.new(base_dir, paths, asset_prefix) |
|
| 107 | 119 |
end |
| 108 | 120 | |
| 109 | 121 |
private |
| test/fixtures/asset_path/foo/images/baz/baz.svg | ||
|---|---|---|
| 1 |
<svg height="100" width="100"> |
|
| 2 |
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" /> |
|
| 3 |
</svg> |
|
| test/fixtures/asset_path/foo/images/foo.svg | ||
|---|---|---|
| 1 |
<svg height="100" width="100"> |
|
| 2 |
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" /> |
|
| 3 |
</svg> |
|
| test/fixtures/asset_path/foo/stylesheets/bar/bar.css | ||
|---|---|---|
| 1 |
.foo {
|
|
| 2 |
background-image: url("../../images/baz/baz.svg");
|
|
| 3 |
} |
|
| test/fixtures/asset_path/foo/stylesheets/foo.css | ||
|---|---|---|
| 1 |
.foo {
|
|
| 2 |
background-image: url("../images/foo.svg");
|
|
| 3 |
} |
|
| test/integration/lib/redmine/hook_test.rb | ||
|---|---|---|
| 102 | 102 |
assert_response :success |
| 103 | 103 |
assert_select 'p', :text => 'ContentForInsideHook content' |
| 104 | 104 |
assert_select 'head' do |
| 105 |
assert_select 'script[src="/plugin_assets/test_plugin/javascripts/test_plugin.js"]'
|
|
| 106 |
assert_select 'link[href="/plugin_assets/test_plugin/stylesheets/test_plugin.css"]'
|
|
| 105 |
assert_select 'script[src="/assets/plugin_assets/test_plugin/test_plugin.js"]'
|
|
| 106 |
assert_select 'link[href="/assets/plugin_assets/test_plugin/test_plugin.css"]'
|
|
| 107 | 107 |
end |
| 108 | 108 |
end |
| 109 | 109 | |
| test/unit/lib/redmine/asset_path_test.rb | ||
|---|---|---|
| 1 |
# frozen_string_literal: true |
|
| 2 | ||
| 3 |
# Redmine - project management software |
|
| 4 |
# Copyright (C) 2006-2023 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_relative '../../../test_helper' |
|
| 21 | ||
| 22 |
class Redmine::AssetPathTest < ActiveSupport::TestCase |
|
| 23 |
def setup |
|
| 24 |
assets_dir = Rails.root.join('test/fixtures/asset_path/foo')
|
|
| 25 |
@asset_path = Redmine::AssetPath.new(assets_dir, assets_dir.children.filter_map{|child| child if child.directory? }, 'plugin_assets/foo/')
|
|
| 26 |
@assets = {}
|
|
| 27 |
@transition_map = {}
|
|
| 28 |
@asset_path.update(transition_map: @transition_map, assets: @assets) |
|
| 29 |
end |
|
| 30 | ||
| 31 |
test "asset path size" do |
|
| 32 |
assert_equal 2, @asset_path.paths.size |
|
| 33 |
end |
|
| 34 | ||
| 35 |
test "@transition_map does not contain directories with parent-child relationships" do |
|
| 36 |
assert_equal '.', @transition_map['plugin_assets/foo']['../images'] |
|
| 37 |
assert_nil @transition_map['plugin_assets/foo/bar']['../../images/baz'] |
|
| 38 |
assert_equal '..', @transition_map['plugin_assets/foo/bar']['../../images'] |
|
| 39 |
end |
|
| 40 | ||
| 41 |
test "update assets" do |
|
| 42 |
assert_not_nil @assets['plugin_assets/foo/foo.css'] |
|
| 43 |
assert_not_nil @assets['plugin_assets/foo/foo.svg'] |
|
| 44 |
end |
|
| 45 |
end |
|