Redmine 4.1.1

This commit is contained in:
Manuel Cillero 2020-11-22 21:20:06 +01:00
parent 33e7b881a5
commit 3d976f1b3b
1593 changed files with 36180 additions and 19489 deletions

View file

@ -1,5 +1,7 @@
# frozen_string_literal: true
# Redmine - project management software
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2006-2019 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@ -15,9 +17,12 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module Redmine #:nodoc:
module Redmine
# Exception raised when a plugin cannot be found given its id.
class PluginNotFound < StandardError; end
# Exception raised when a plugin requirement is not met.
class PluginRequirementError < StandardError; end
# Base class for Redmine plugins.
@ -42,10 +47,14 @@ module Redmine #:nodoc:
# In this example, the settings partial will be found here in the plugin directory: <tt>app/views/settings/_settings.rhtml</tt>.
#
# When rendered, the plugin settings value is available as the local variable +settings+
#
# See: http://www.redmine.org/projects/redmine/wiki/Plugin_Tutorial
class Plugin
# Absolute path to the directory where plugins are located
cattr_accessor :directory
self.directory = File.join(Rails.root, 'plugins')
# Absolute path to the plublic directory where plugins assets are copied
cattr_accessor :public_directory
self.public_directory = File.join(Rails.root, 'public', 'plugin_assets')
@ -69,7 +78,17 @@ module Redmine #:nodoc:
def_field :name, :description, :url, :author, :author_url, :version, :settings, :directory
attr_reader :id
# Plugin constructor
# Plugin constructor: instanciates a new Redmine::Plugin with given +id+
# and make it evaluate the given +block+
#
# Example
# Redmine::Plugin.register :example do
# name 'Example plugin'
# author 'John Smith'
# description 'This is an example plugin for Redmine'
# version '0.0.1'
# requires_redmine version_or_higher: '3.0.0'
# end
def self.register(id, &block)
p = new(id)
p.instance_eval(&block)
@ -79,6 +98,10 @@ module Redmine #:nodoc:
# Set a default directory if it was not provided during registration
p.directory(File.join(self.directory, id.to_s)) if p.directory.nil?
unless File.directory?(p.directory)
raise PluginNotFound, "Plugin not found. The directory for plugin #{p.id} should be #{p.directory}."
end
# Adds plugin locales if any
# YAML translation files should be found under <plugin>/config/locales/
Rails.application.config.i18n.load_path += Dir.glob(File.join(p.directory, 'config', 'locales', '*.yml'))
@ -90,10 +113,13 @@ module Redmine #:nodoc:
ActionMailer::Base.prepend_view_path(view_path)
end
# Adds the app/{controllers,helpers,models} directories of the plugin to the autoload path
Dir.glob File.expand_path(File.join(p.directory, 'app', '{controllers,helpers,models}')) do |dir|
ActiveSupport::Dependencies.autoload_paths += [dir]
end
# Add the plugin directories to rails autoload paths
engine_cfg = Rails::Engine::Configuration.new(p.directory)
engine_cfg.paths.add 'lib', eager_load: true
Rails.application.config.eager_load_paths += engine_cfg.eager_load_paths
Rails.application.config.autoload_once_paths += engine_cfg.autoload_once_paths
Rails.application.config.autoload_paths += engine_cfg.autoload_paths
ActiveSupport::Dependencies.autoload_paths += engine_cfg.eager_load_paths + engine_cfg.autoload_once_paths + engine_cfg.autoload_paths
# Defines plugin setting if present
if p.settings
@ -170,6 +196,7 @@ module Redmine #:nodoc:
id
end
# Returns the absolute path to the plugin assets directory
def assets_directory
File.join(directory, 'assets')
end
@ -248,7 +275,11 @@ module Redmine #:nodoc:
arg = { :version_or_higher => arg } unless arg.is_a?(Hash)
arg.assert_valid_keys(:version, :version_or_higher)
plugin = Plugin.find(plugin_name)
begin
plugin = Plugin.find(plugin_name)
rescue PluginNotFound
raise PluginRequirementError.new("#{id} plugin requires the #{plugin_name} plugin")
end
current = plugin.version.split('.').collect(&:to_i)
arg.each do |k, v|
@ -311,7 +342,7 @@ module Redmine #:nodoc:
# permission :say_hello, { :example => :say_hello }, :require => :member
def permission(name, actions, options = {})
if @project_module
Redmine::AccessControl.map {|map| map.project_module(@project_module) {|map|map.permission(name, actions, options)}}
Redmine::AccessControl.map {|map| map.project_module(@project_module) {|map| map.permission(name, actions, options)}}
else
Redmine::AccessControl.map {|map| map.permission(name, actions, options)}
end
@ -367,7 +398,7 @@ module Redmine #:nodoc:
# * :label - label for the formatter displayed in application settings
#
# Examples:
# wiki_format_provider(:custom_formatter, CustomFormatter, :label => "My custom formatter")
# wiki_format_provider(:custom_formatter, CustomFormatter, :label => "My custom formatter")
#
def wiki_format_provider(name, *args)
Redmine::WikiFormatting.register(name, *args)
@ -391,7 +422,7 @@ module Redmine #:nodoc:
base_target_dir = File.join(destination, File.dirname(source_files.first).gsub(source, ''))
begin
FileUtils.mkdir_p(base_target_dir)
rescue Exception => e
rescue => e
raise "Could not create directory #{base_target_dir}: " + e.message
end
end
@ -402,7 +433,7 @@ module Redmine #:nodoc:
target_dir = File.join(destination, dir.gsub(source, ''))
begin
FileUtils.mkdir_p(target_dir)
rescue Exception => e
rescue => e
raise "Could not create directory #{target_dir}: " + e.message
end
end
@ -413,7 +444,7 @@ module Redmine #:nodoc:
unless File.exist?(target) && FileUtils.identical?(file, target)
FileUtils.cp(file, target)
end
rescue Exception => e
rescue => e
raise "Could not copy #{file} to #{target}: " + e.message
end
end
@ -449,7 +480,6 @@ module Redmine #:nodoc:
# Migrate this plugin to the given version
def migrate(version = nil)
puts "Migrating #{id} (#{name})..."
Redmine::Plugin::Migrator.migrate_plugin(self, version)
end
@ -469,6 +499,36 @@ module Redmine #:nodoc:
end
end
class MigrationContext < ActiveRecord::MigrationContext
def up(target_version = nil)
selected_migrations =
if block_given?
migrations.select { |m| yield m }
else
migrations
end
Migrator.new(:up, selected_migrations, target_version).migrate
end
def down(target_version = nil)
selected_migrations =
if block_given?
migrations.select { |m| yield m }
else
migrations
end
Migrator.new(:down, selected_migrations, target_version).migrate
end
def run(direction, target_version)
Migrator.new(direction, migrations, target_version).run
end
def open
Migrator.new(:up, migrations, nil)
end
end
class Migrator < ActiveRecord::Migrator
# We need to be able to set the 'current' plugin being migrated.
cattr_accessor :current_plugin
@ -478,22 +538,29 @@ module Redmine #:nodoc:
def migrate_plugin(plugin, version)
self.current_plugin = plugin
return if current_version(plugin) == version
migrate(plugin.migration_directory, version)
MigrationContext.new(plugin.migration_directory).migrate(version)
end
def current_version(plugin=current_plugin)
def get_all_versions(plugin = current_plugin)
# Delete migrations that don't match .. to_i will work because the number comes first
::ActiveRecord::Base.connection.select_values(
"SELECT version FROM #{schema_migrations_table_name}"
).delete_if{ |v| v.match(/-#{plugin.id}$/) == nil }.map(&:to_i).max || 0
@all_versions ||= {}
@all_versions[plugin.id.to_s] ||= begin
sm_table = ::ActiveRecord::SchemaMigration.table_name
migration_versions = ActiveRecord::Base.connection.select_values("SELECT version FROM #{sm_table}")
versions_by_plugins = migration_versions.group_by { |version| version.match(/-(.*)$/).try(:[], 1) }
@all_versions = versions_by_plugins.transform_values! {|versions| versions.map!(&:to_i).sort! }
@all_versions[plugin.id.to_s] || []
end
end
def current_version(plugin = current_plugin)
get_all_versions(plugin).last || 0
end
end
def migrated
sm_table = self.class.schema_migrations_table_name
::ActiveRecord::Base.connection.select_values(
"SELECT version FROM #{sm_table}"
).delete_if{ |v| v.match(/-#{current_plugin.id}$/) == nil }.map(&:to_i).sort
def load_migrated
@migrated_versions = Set.new(self.class.get_all_versions(current_plugin))
end
def record_version_state_after_migrating(version)