Feature #32938 » alt-0001-Extract-Redmine-PluginLoader-from-Redmine-Plugin.patch
| config/initializers/30-redmine.rb | ||
|---|---|---|
| 5 | 5 |
I18n.config.available_locales = nil |
| 6 | 6 | |
| 7 | 7 |
require 'redmine' |
| 8 |
require 'redmine/plugin_loader' |
|
| 8 | 9 | |
| 9 | 10 |
# Load the secret token from the Redmine configuration file |
| 10 | 11 |
secret = Redmine::Configuration['secret_token'] |
| ... | ... | |
| 17 | 18 |
OpenIdAuthentication.store = openid_authentication_store.presence || :memory |
| 18 | 19 |
end |
| 19 | 20 | |
| 20 |
Redmine::Plugin.load |
|
| 21 |
Redmine::PluginLoader.load |
|
| 22 |
plugin_assets_reloader = Redmine::PluginLoader.create_assets_reloader |
|
| 21 | 23 | |
| 22 |
plugin_assets_dirs = {}
|
|
| 23 |
Redmine::Plugin.all.each do |plugin| |
|
| 24 |
plugin_assets_dirs[plugin.assets_directory] = ["*"] |
|
| 25 |
end |
|
| 26 |
plugin_assets_reloader = ActiveSupport::FileUpdateChecker.new([], plugin_assets_dirs) do |
|
| 27 |
Redmine::Plugin.mirror_assets |
|
| 28 |
end |
|
| 29 | 24 |
Rails.application.reloaders << plugin_assets_reloader |
| 30 | 25 |
unless Redmine::Configuration['mirror_plugins_assets_on_startup'] == false |
| 31 | 26 |
plugin_assets_reloader.execute |
| lib/redmine/plugin.rb | ||
|---|---|---|
| 52 | 52 |
class Plugin |
| 53 | 53 |
# Absolute path to the directory where plugins are located |
| 54 | 54 |
cattr_accessor :directory |
| 55 |
self.directory = File.join(Rails.root, 'plugins')
|
|
| 55 |
self.directory = PluginLoader.directory
|
|
| 56 | 56 | |
| 57 | 57 |
# Absolute path to the plublic directory where plugins assets are copied |
| 58 | 58 |
cattr_accessor :public_directory |
| 59 |
self.public_directory = File.join(Rails.root, 'public', 'plugin_assets')
|
|
| 59 |
self.public_directory = PluginLoader.public_directory
|
|
| 60 | 60 | |
| 61 | 61 |
@registered_plugins = {}
|
| 62 | 62 |
@used_partials = {}
|
| 63 | 63 | |
| 64 |
attr_accessor :path |
|
| 65 | ||
| 64 | 66 |
class << self |
| 65 | 67 |
attr_reader :registered_plugins |
| 66 | 68 |
private :new |
| ... | ... | |
| 102 | 104 |
raise PluginNotFound, "Plugin not found. The directory for plugin #{p.id} should be #{p.directory}."
|
| 103 | 105 |
end |
| 104 | 106 | |
| 107 |
p.path = PluginLoader.directories{ |d| d.dir == p.directory }
|
|
| 108 | ||
| 105 | 109 |
# Adds plugin locales if any |
| 106 | 110 |
# YAML translation files should be found under <plugin>/config/locales/ |
| 107 | 111 |
Rails.application.config.i18n.load_path += Dir.glob(File.join(p.directory, 'config', 'locales', '*.yml')) |
| ... | ... | |
| 113 | 117 |
ActionMailer::Base.prepend_view_path(view_path) |
| 114 | 118 |
end |
| 115 | 119 | |
| 116 |
# Add the plugin directories to rails autoload paths |
|
| 117 |
engine_cfg = Rails::Engine::Configuration.new(p.directory) |
|
| 118 |
engine_cfg.paths.add 'lib', eager_load: true |
|
| 119 |
Rails.application.config.eager_load_paths += engine_cfg.eager_load_paths |
|
| 120 |
Rails.application.config.autoload_once_paths += engine_cfg.autoload_once_paths |
|
| 121 |
Rails.application.config.autoload_paths += engine_cfg.autoload_paths |
|
| 122 |
ActiveSupport::Dependencies.autoload_paths += |
|
| 123 |
engine_cfg.eager_load_paths + engine_cfg.autoload_once_paths + engine_cfg.autoload_paths |
|
| 124 | ||
| 125 | 120 |
# Defines plugin setting if present |
| 126 | 121 |
if p.settings |
| 127 | 122 |
Setting.define_plugin_setting p |
| ... | ... | |
| 174 | 169 |
registered_plugins[id.to_sym].present? |
| 175 | 170 |
end |
| 176 | 171 | |
| 177 |
def self.load |
|
| 178 |
Dir.glob(File.join(self.directory, '*')).sort.each do |directory| |
|
| 179 |
if File.directory?(directory) |
|
| 180 |
lib = File.join(directory, "lib") |
|
| 181 |
if File.directory?(lib) |
|
| 182 |
$:.unshift lib |
|
| 183 |
ActiveSupport::Dependencies.autoload_paths += [lib] |
|
| 184 |
end |
|
| 185 |
initializer = File.join(directory, "init.rb") |
|
| 186 |
if File.file?(initializer) |
|
| 187 |
require initializer |
|
| 188 |
end |
|
| 189 |
end |
|
| 190 |
end |
|
| 191 |
Redmine::Hook.call_hook :after_plugins_loaded |
|
| 192 |
end |
|
| 193 | ||
| 194 | 172 |
def initialize(id) |
| 195 | 173 |
@id = id.to_sym |
| 196 | 174 |
end |
| ... | ... | |
| 205 | 183 | |
| 206 | 184 |
# Returns the absolute path to the plugin assets directory |
| 207 | 185 |
def assets_directory |
| 208 |
File.join(directory, 'assets')
|
|
| 186 |
path.assedts_dir
|
|
| 209 | 187 |
end |
| 210 | 188 | |
| 211 | 189 |
def <=>(plugin) |
| ... | ... | |
| 441 | 419 |
settings && settings.is_a?(Hash) && !settings[:partial].blank? |
| 442 | 420 |
end |
| 443 | 421 | |
| 444 |
def mirror_assets |
|
| 445 |
source = assets_directory |
|
| 446 |
destination = public_directory |
|
| 447 |
return unless File.directory?(source) |
|
| 448 | ||
| 449 |
source_files = Dir[source + "/**/*"] |
|
| 450 |
source_dirs = source_files.select {|d| File.directory?(d)}
|
|
| 451 |
source_files -= source_dirs |
|
| 452 | ||
| 453 |
unless source_files.empty? |
|
| 454 |
base_target_dir = File.join(destination, File.dirname(source_files.first).gsub(source, '')) |
|
| 455 |
begin |
|
| 456 |
FileUtils.mkdir_p(base_target_dir) |
|
| 457 |
rescue => e |
|
| 458 |
raise "Could not create directory #{base_target_dir}: " + e.message
|
|
| 459 |
end |
|
| 460 |
end |
|
| 461 | ||
| 462 |
source_dirs.each do |dir| |
|
| 463 |
# strip down these paths so we have simple, relative paths we can |
|
| 464 |
# add to the destination |
|
| 465 |
target_dir = File.join(destination, dir.gsub(source, '')) |
|
| 466 |
begin |
|
| 467 |
FileUtils.mkdir_p(target_dir) |
|
| 468 |
rescue => e |
|
| 469 |
raise "Could not create directory #{target_dir}: " + e.message
|
|
| 470 |
end |
|
| 471 |
end |
|
| 472 | ||
| 473 |
source_files.each do |file| |
|
| 474 |
begin |
|
| 475 |
target = File.join(destination, file.gsub(source, '')) |
|
| 476 |
unless File.exist?(target) && FileUtils.identical?(file, target) |
|
| 477 |
FileUtils.cp(file, target) |
|
| 478 |
end |
|
| 479 |
rescue => e |
|
| 480 |
raise "Could not copy #{file} to #{target}: " + e.message
|
|
| 481 |
end |
|
| 482 |
end |
|
| 483 |
end |
|
| 484 | ||
| 485 |
# Mirrors assets from one or all plugins to public/plugin_assets |
|
| 486 |
def self.mirror_assets(name=nil) |
|
| 487 |
if name.present? |
|
| 488 |
find(name).mirror_assets |
|
| 489 |
else |
|
| 490 |
all.each do |plugin| |
|
| 491 |
plugin.mirror_assets |
|
| 492 |
end |
|
| 493 |
end |
|
| 494 |
end |
|
| 495 | ||
| 496 | 422 |
# The directory containing this plugin's migrations (<tt>plugin/db/migrate</tt>) |
| 497 | 423 |
def migration_directory |
| 498 | 424 |
File.join(directory, 'db', 'migrate') |
| lib/redmine/plugin_loader.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 |
module Redmine |
|
| 21 |
class PluginPath |
|
| 22 |
attr_reader :assets_dir, :initializer |
|
| 23 | ||
| 24 |
def initialize(dir) |
|
| 25 |
@dir = dir |
|
| 26 |
@assets_dir = File.join dir, 'assets' |
|
| 27 |
@initializer = File.join dir, 'init.rb' |
|
| 28 |
end |
|
| 29 | ||
| 30 |
def run_initializer |
|
| 31 |
load initializer if has_initializer? |
|
| 32 |
end |
|
| 33 | ||
| 34 |
def to_s |
|
| 35 |
@dir |
|
| 36 |
end |
|
| 37 | ||
| 38 |
def mirror_assets |
|
| 39 |
return unless has_assets_dir? |
|
| 40 | ||
| 41 |
source_files = Dir["#{assets_dir}/**/*"]
|
|
| 42 |
source_dirs = source_files.select { |d| File.directory?(d)}
|
|
| 43 |
source_files -= source_dirs |
|
| 44 |
unless source_files.empty? |
|
| 45 |
base_target_dir = File.join(PluginLoader.public_directory, File.dirname(source_files.first).gsub(assets_dir, '')) |
|
| 46 |
begin |
|
| 47 |
FileUtils.mkdir_p(base_target_dir) |
|
| 48 |
rescue => e |
|
| 49 |
raise "Could not create directory #{base_target_dir}: " + e.message
|
|
| 50 |
end |
|
| 51 |
end |
|
| 52 |
source_dirs.each do |dir| |
|
| 53 |
# strip down these paths so we have simple, relative paths we can |
|
| 54 |
# add to the destination |
|
| 55 |
target_dir = File.join(PluginLoader.public_directory, dir.gsub(assets_dir, '')) |
|
| 56 |
begin |
|
| 57 |
FileUtils.mkdir_p(target_dir) |
|
| 58 |
rescue => e |
|
| 59 |
raise "Could not create directory #{target_dir}: " + e.message
|
|
| 60 |
end |
|
| 61 |
end |
|
| 62 |
source_files.each do |file| |
|
| 63 |
target = File.join(PluginLoader.public_directory, file.gsub(assets_dir, '')) |
|
| 64 |
unless File.exist?(target) && FileUtils.identical?(file, target) |
|
| 65 |
FileUtils.cp(file, target) |
|
| 66 |
end |
|
| 67 |
rescue => e |
|
| 68 |
raise "Could not copy #{file} to #{target}: " + e.message
|
|
| 69 |
end |
|
| 70 |
end |
|
| 71 | ||
| 72 |
def has_assets_dir? |
|
| 73 |
File.directory?(@assets_dir) |
|
| 74 |
end |
|
| 75 | ||
| 76 |
def has_initializer? |
|
| 77 |
File.file?(@initializer) |
|
| 78 |
end |
|
| 79 |
end |
|
| 80 | ||
| 81 |
class PluginLoader |
|
| 82 |
# Absolute path to the directory where plugins are located |
|
| 83 |
cattr_accessor :directory |
|
| 84 |
self.directory = Rails.root.join('plugins')
|
|
| 85 | ||
| 86 |
# Absolute path to the plublic directory where plugins assets are copied |
|
| 87 |
cattr_accessor :public_directory |
|
| 88 |
self.public_directory = Rails.root.join('public/plugin_assets')
|
|
| 89 | ||
| 90 |
def self.create_assets_reloader |
|
| 91 |
plugin_assets_dirs = {}
|
|
| 92 |
@plugin_directories.each do |dir| |
|
| 93 |
plugin_assets_dirs[dir.assets_dir] = ['*'] |
|
| 94 |
end |
|
| 95 |
ActiveSupport::FileUpdateChecker.new([], plugin_assets_dirs) do |
|
| 96 |
mirror_assets |
|
| 97 |
end |
|
| 98 |
end |
|
| 99 | ||
| 100 |
def self.load |
|
| 101 |
setup |
|
| 102 |
add_autoload_paths |
|
| 103 | ||
| 104 |
Rails.application.config.to_prepare do |
|
| 105 |
PluginLoader.directories.each(&:run_initializer) |
|
| 106 | ||
| 107 |
Redmine::Hook.call_hook :after_plugins_loaded |
|
| 108 |
end |
|
| 109 |
end |
|
| 110 | ||
| 111 |
def self.setup |
|
| 112 |
@plugin_directories = [] |
|
| 113 | ||
| 114 |
Dir.glob(File.join(directory, '*')).sort.each do |directory| |
|
| 115 |
next unless File.directory?(directory) |
|
| 116 | ||
| 117 |
@plugin_directories << PluginPath.new(directory) |
|
| 118 |
end |
|
| 119 |
end |
|
| 120 | ||
| 121 |
def self.add_autoload_paths |
|
| 122 |
directories.each do |directory| |
|
| 123 |
# Add the plugin directories to rails autoload paths |
|
| 124 |
engine_cfg = Rails::Engine::Configuration.new(directory.to_s) |
|
| 125 |
engine_cfg.paths.add 'lib', eager_load: true |
|
| 126 |
Rails.application.config.eager_load_paths += engine_cfg.eager_load_paths |
|
| 127 |
Rails.application.config.autoload_once_paths += engine_cfg.autoload_once_paths |
|
| 128 |
Rails.application.config.autoload_paths += engine_cfg.autoload_paths |
|
| 129 |
end |
|
| 130 |
end |
|
| 131 | ||
| 132 |
def self.directories |
|
| 133 |
@plugin_directories |
|
| 134 |
end |
|
| 135 | ||
| 136 |
def self.mirror_assets |
|
| 137 |
directories.each(&:mirror_assets) |
|
| 138 |
end |
|
| 139 |
end |
|
| 140 |
end |
|