# Redmine - project management software
# Copyright (C) 2006-2023  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
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

namespace :redmine do
  namespace :attachments do
    desc 'Removes uploaded files left unattached after one day.'
    task :prune => :environment do
      Attachment.prune
    end

    desc 'Moves attachments stored at the root of the file directory (ie. created before Redmine 2.3) to their subdirectories'
    task :move_to_subdirectories => :environment do
      Attachment.move_from_root_to_target_directory
    end

    desc 'Updates attachment digests to SHA256'
    task :update_digests => :environment do
      Attachment.update_digests_to_sha256
    end
  end

  namespace :tokens do
    desc 'Removes expired tokens.'
    task :prune => :environment do
      Token.destroy_expired
    end
  end

  namespace :users do
    desc 'Removes registered users that have not been activated after a number of days. Use DAYS to set the number of days, defaults to 30 days.'
    task :prune => :environment do
      days = 30
      env_days = ENV['DAYS']
      if env_days
        if env_days.to_i <= 0
          abort "Invalid DAYS #{env_days} given. The value must be a integer."
        else
          days = env_days.to_i
        end
      end
      User.prune(days.days)
    end
  end

  namespace :watchers do
    desc 'Removes watchers from what they can no longer view.'
    task :prune => :environment do
      Watcher.prune
    end
  end

  desc 'Fetch changesets from the repositories'
  task :fetch_changesets => :environment do
    Repository.fetch_changesets
  end

  desc 'Migrates and copies plugins assets.'
  task :plugins do
    Rake::Task["redmine:plugins:migrate"].invoke
    Rake::Task["redmine:plugins:assets"].invoke
  end

desc <<-DESC
FOR EXPERIMENTAL USE ONLY, Moves Redmine data from production database to the development database.
This task should only be used when you need to move data from one DBMS to a different one (eg. MySQL to PostgreSQL).
WARNING: All data in the development database is deleted.
DESC

  task :migrate_dbms => :environment do
    ActiveRecord::Base.establish_connection :development
    target_tables = ActiveRecord::Base.connection.tables
    ActiveRecord::Base.remove_connection

    ActiveRecord::Base.establish_connection :production
    tables = ActiveRecord::Base.connection.tables.sort - %w(schema_migrations plugin_schema_info)

    if (tables - target_tables).any?
      list = (tables - target_tables).map {|table| "* #{table}"}.join("\n")
      abort "The following table(s) are missing from the target database:\n#{list}"
    end

    tables.each do |table_name|
      Source = Class.new(ActiveRecord::Base)
      Target = Class.new(ActiveRecord::Base)
      Target.establish_connection(:development)

      [Source, Target].each do |klass|
        klass.table_name = table_name
        klass.reset_column_information
        klass.inheritance_column = "foo"
        klass.record_timestamps = false
      end
      Target.primary_key = (Target.column_names.include?("id") ? "id" : nil)

      source_count = Source.count
      puts "Migrating %6d records from #{table_name}..." % source_count

      Target.delete_all
      offset = 0
      while (objects = Source.offset(offset).limit(5000).order("1,2").to_a) && objects.any?
        offset += objects.size
        Target.transaction do
          objects.each do |object|
            new_object = Target.new(object.attributes)
            new_object.id = object.id if Target.primary_key
            new_object.save(:validate => false)
          end
        end
      end
      Target.connection.reset_pk_sequence!(table_name) if Target.primary_key
      target_count = Target.count
      abort "Some records were not migrated" unless source_count == target_count

      Object.send(:remove_const, :Target)
      Object.send(:remove_const, :Source)
    end
  end

  namespace :plugins do
    desc 'Migrates installed plugins.'
    task :migrate => :environment do
      name = ENV['NAME']
      version = nil
      version_string = ENV['VERSION']
      if version_string
        if version_string =~ /^\d+$/
          version = version_string.to_i
          if name.nil?
            abort "The VERSION argument requires a plugin NAME."
          end
        else
          abort "Invalid VERSION #{version_string} given."
        end
      end

      begin
        Redmine::Plugin.migrate(name, version)
      rescue Redmine::PluginNotFound
        abort "Plugin #{name} was not found."
      end

      Rake::Task["db:schema:dump"].invoke
    end

    desc 'Copies plugins assets into the public directory.'
    task :assets => :environment do
      name = ENV['NAME']

      begin
        Redmine::PluginLoader.mirror_assets(name)
      rescue Redmine::PluginNotFound
        abort "Plugin #{name} was not found."
      end
    end

    desc 'Runs the plugins tests.'
    task :test do
      Rake::Task["redmine:plugins:test:units"].invoke
      Rake::Task["redmine:plugins:test:functionals"].invoke
      Rake::Task["redmine:plugins:test:integration"].invoke
      Rake::Task["redmine:plugins:test:system"].invoke
    end

    namespace :test do
      desc 'Runs the plugins unit tests.'
      task :units => "db:test:prepare" do |t|
        $: << "test"
        Rails::TestUnit::Runner.rake_run ["plugins/#{ENV['NAME'] || '*'}/test/unit/**/*_test.rb"]
      end

      desc 'Runs the plugins functional tests.'
      task :functionals => "db:test:prepare" do |t|
        $: << "test"
        Rails::TestUnit::Runner.rake_run ["plugins/#{ENV['NAME'] || '*'}/test/functional/**/*_test.rb"]
      end

      desc 'Runs the plugins integration tests.'
      task :integration => "db:test:prepare" do |t|
        $: << "test"
        Rails::TestUnit::Runner.rake_run ["plugins/#{ENV['NAME'] || '*'}/test/integration/**/*_test.rb"]
      end

      desc 'Runs the plugins system tests.'
      task :system => "db:test:prepare" do |t|
        $: << "test"
        Rails::TestUnit::Runner.rake_run ["plugins/#{ENV['NAME'] || '*'}/test/system/**/*_test.rb"]
      end

      desc 'Runs the plugins ui tests.'
      task :ui => "db:test:prepare" do |t|
        $: << "test"
        Rails::TestUnit::Runner.rake_run ["plugins/#{ENV['NAME'] || '*'}/test/ui/**/*_test.rb"]
      end
    end
  end
end

# Load plugins' rake tasks
Dir[File.join(Rails.root, "plugins/*/lib/tasks/**/*.rake")].sort.each { |ext| load ext }
