summaryrefslogtreecommitdiffstats
path: root/lib/redmine/plugin.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/redmine/plugin.rb')
-rw-r--r--lib/redmine/plugin.rb137
1 files changed, 136 insertions, 1 deletions
diff --git a/lib/redmine/plugin.rb b/lib/redmine/plugin.rb
index 5c13e3d1f..1f57c4d0c 100644
--- a/lib/redmine/plugin.rb
+++ b/lib/redmine/plugin.rb
@@ -43,6 +43,9 @@ module Redmine #:nodoc:
#
# When rendered, the plugin settings value is available as the local variable +settings+
class Plugin
+ cattr_accessor :directory
+ self.directory = File.join(Rails.root, 'plugins')
+
cattr_accessor :public_directory
self.public_directory = File.join(Rails.root, 'public', 'plugin_assets')
@@ -70,9 +73,22 @@ module Redmine #:nodoc:
p.instance_eval(&block)
# Set a default name if it was not provided during registration
p.name(id.to_s.humanize) if p.name.nil?
+
# Adds plugin locales if any
# YAML translation files should be found under <plugin>/config/locales/
- ::I18n.load_path += Dir.glob(File.join(Rails.root, 'vendor', 'plugins', id.to_s, 'config', 'locales', '*.yml'))
+ ::I18n.load_path += Dir.glob(File.join(p.directory, 'config', 'locales', '*.yml'))
+
+ # Prepends the app/views directory of the plugin to the view path
+ view_path = File.join(p.directory, 'app', 'views')
+ if File.directory?(view_path)
+ ActionController::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
+
registered_plugins[id] = p
end
@@ -100,10 +116,38 @@ module Redmine #:nodoc:
registered_plugins[id.to_sym].present?
end
+ def self.load
+ Dir.glob(File.join(self.directory, '*')).sort.each do |directory|
+ if File.directory?(directory)
+ lib = File.join(directory, "lib")
+ if File.directory?(lib)
+ $:.unshift lib
+ ActiveSupport::Dependencies.autoload_paths += [lib]
+ end
+ initializer = File.join(directory, "init.rb")
+ if File.file?(initializer)
+ require initializer
+ end
+ end
+ end
+ end
+
def initialize(id)
@id = id.to_sym
end
+ def directory
+ File.join(self.class.directory, id.to_s)
+ end
+
+ def public_directory
+ File.join(self.class.public_directory, id.to_s)
+ end
+
+ def assets_directory
+ File.join(directory, 'assets')
+ end
+
def <=>(plugin)
self.id.to_s <=> plugin.id.to_s
end
@@ -277,5 +321,96 @@ module Redmine #:nodoc:
def configurable?
settings && settings.is_a?(Hash) && !settings[:partial].blank?
end
+
+ def mirror_assets
+ source = assets_directory
+ destination = public_directory
+ return unless File.directory?(source)
+
+ source_files = Dir[source + "/**/*"]
+ source_dirs = source_files.select { |d| File.directory?(d) }
+ source_files -= source_dirs
+
+ unless source_files.empty?
+ base_target_dir = File.join(destination, File.dirname(source_files.first).gsub(source, ''))
+ FileUtils.mkdir_p(base_target_dir)
+ end
+
+ source_dirs.each do |dir|
+ # strip down these paths so we have simple, relative paths we can
+ # add to the destination
+ target_dir = File.join(destination, dir.gsub(source, ''))
+ begin
+ FileUtils.mkdir_p(target_dir)
+ rescue Exception => e
+ raise "Could not create directory #{target_dir}: \n" + e
+ end
+ end
+
+ source_files.each do |file|
+ begin
+ target = File.join(destination, file.gsub(source, ''))
+ unless File.exist?(target) && FileUtils.identical?(file, target)
+ FileUtils.cp(file, target)
+ end
+ rescue Exception => e
+ raise "Could not copy #{file} to #{target}: \n" + e
+ end
+ end
+ end
+
+ # The directory containing this plugin's migrations (<tt>plugin/db/migrate</tt>)
+ def migration_directory
+ File.join(Rails.root, 'plugins', id.to_s, 'db', 'migrate')
+ end
+
+ # Returns the version number of the latest migration for this plugin. Returns
+ # nil if this plugin has no migrations.
+ def latest_migration
+ migrations.last
+ end
+
+ # Returns the version numbers of all migrations for this plugin.
+ def migrations
+ migrations = Dir[migration_directory+"/*.rb"]
+ migrations.map { |p| File.basename(p).match(/0*(\d+)\_/)[1].to_i }.sort
+ end
+
+ # Migrate this plugin to the given version
+ def migrate(version = nil)
+ Redmine::Plugin::Migrator.migrate_plugin(self, version)
+ end
+
+ class Migrator < ActiveRecord::Migrator
+ # We need to be able to set the 'current' plugin being migrated.
+ cattr_accessor :current_plugin
+
+ class << self
+ # Runs the migrations from a plugin, up (or down) to the version given
+ def migrate_plugin(plugin, version)
+ self.current_plugin = plugin
+ return if current_version(plugin) == version
+ migrate(plugin.migration_directory, version)
+ end
+
+ def current_version(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
+ 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
+ end
+
+ def record_version_state_after_migrating(version)
+ super(version.to_s + "-" + current_plugin.id.to_s)
+ end
+ end
end
end