import-cooperation.net 7.02 KB
#!/usr/bin/ruby
require File.dirname(__FILE__) + '/../config/environment'

include ActionController::UrlWriter

SCRIPT_TYPE = File.basename($PROGRAM_NAME)

IMPORT_DIR = ENV['IMPORT_DIR'] || '/home/terceiro/src/cooperation-migration/data/export'

if ARGV.size == 0
  puts "usage: %s <username> [ <username> [ <username> ... ] ]" % $PROGRAM_NAME
  exit(1)
end

def import_environment(domain_name)
  env = Environment.default
  env.domains << get_domain(domain_name)
  return Environment.default
end

def get_domain(envname)
  suffix = (ENV['RAILS_ENV'] == 'production') ? '' : '.local'
  domain = Domain.find_or_create_by_name(envname + suffix)
end

TinyMceArticle # forces loading the Noosfero class before adding stuff to it
class TinyMceArticle
  attr_accessor :is_homepage
end
UploadedFile # force loading Noosfero class before addinf stuff to it
class UploadedFile
  attr_accessor :is_homepage, :filesystem_location
end

Person

class FileData < StringIO
  attr_reader :original_filename, :content_type
  def initialize(actual_filename, name, content_type)
    @original_filename = name
    @content_type = content_type
    super(File.read(actual_filename))
  end
end

class Progress
  DEBUG = true
  class << self
    def start(n)
      @total = n
      @current = 0
    end
    def step
      @current += 1
    end
    def step_done
      say('=> done', true)
    end
    def step_failed
      say('=> FAILED', true)
    end
    def say(msg, force = Progress::DEBUG)
      puts("[%d/%d] %s" %  [@current, @total, msg]) if force
    end
  end
end

FORBIDDEN_LOGINS = %w[
  info
]
Progress.start(ARGV.size)
for username in ARGV
  Progress.step
  begin
    User.transaction do
      # guess environment
      domain_name = username.gsub(/^.*@/, '')
      environment = import_environment(domain_name)

      login = username.gsub(/@.*$/, '')
      if FORBIDDEN_LOGINS.include?(login)
        $stderr.puts "E: not importing #{username}, #{login} is not an allowed login"
        next
      end

      profile = nil 
      if SCRIPT_TYPE == 'import-cooperation.net-groups'
        # import as a group
        imported_data = Hash.from_xml(File.read(File.join(IMPORT_DIR, username + '.xml')))['person']
        profile = Community.create!(
          :identifier => login,
          :name => login,
          :contact_email => imported_data['email'],
          :address => imported_data['address'],
          :environment => environment,
          :preferred_domain_id => get_domain(domain_name).id
        )
      elsif SCRIPT_TYPE == 'import-cooperation.net-articles'
        # only load the articles
        profile = Profile[login]
      else
        # import as a user

        # create user
        user = User.new(:login => login, :email => username, :environment => environment)
        user.enable_email = true

        # import person data
        imported_data = Hash.from_xml(File.read(File.join(IMPORT_DIR, username + '.xml')))['person']
        user.crypted_password = imported_data.delete(:crypted_password.to_s)
        user.salt = imported_data.delete(:salt.to_s)
        user.password_type = imported_data.delete(:password_type.to_s)

        user.person_data = imported_data.merge('preferred_domain_id' => get_domain(domain_name).id)
        user.save!
        Progress.say "I: #{username} data imported"

        profile = user.person
      end

      # import articles
      Dir.glob(File.join(IMPORT_DIR, username, 'articles', '*.xml')) do |xml|
        Progress.say "I: Trying to import #{username}'s article in #{xml} ..."
        article = TinyMceArticle.new
        article.from_xml(File.read(xml))
        article.profile = profile
        if article.valid?
          article.save!
          Progress.say "I: #{username}'s article #{article.name.inspect} imported"
        else
          $stderr.puts "W: #{username}'s article #{article.name.inspect} cannot be saved. Errors: #{article.errors.full_messages.join(', ')}"
          next
        end

        if article.is_homepage
          profile.home_page = article
          profile.save!
          Progress.say "I: Article #{article.name.inspect} is #{username}'s homepage!"
        end

        orig_article_number = File.basename(xml).sub(/\.xml$/, '')

        File.open("tmp/rewrite-articles.txt", 'a') do |file|
          file.puts("#{orig_article_number}\t\t\t#{url_for(article.url)}")
          Progress.say "I: Article with id = #{article.id} redirected to #{url_for(article.url)}"
        end

        # import attachments
        attachments_dir = xml.gsub(/\.xml$/, '')
        Dir.glob(File.join(attachments_dir, 'attachments', '*.xml')).each do |attachment_xml|
          file = UploadedFile.new
          file.from_xml(File.read(attachment_xml))
          if !File.exist?(file.filesystem_location)
            Progress.say "W: skipping attachment pointing to unexisting file"
            next
          end
          Progress.say "I: about to read data from #{file.filesystem_location} (xml: #{attachment_xml})"
          file.uploaded_data = FileData.new(file.filesystem_location, file.filename, file.content_type)
          file.parent = article
          file.profile = profile
          Progress.say "I: Trying to save attachment \"#{file.filename}/#{file.slug}\""
          file.save!
          Progress.say "I: attachment added to article #{article.id}"

          file.reload
          if file.image?
            file.parent.body ||= ''
            file.parent.body = "<img align='left' style='margin-right: 5px; margin-bottom: 5px;' src='/#{login}/#{file.path}'/> " + file.parent.body
          else
            if file.parent.body
              file.parent.body += "<hr/><div><a href='/#{login}/#{file.path}'>#{file.abstract}</a></div>"
            end
          end
          file.parent.save

          orig_file_number = File.basename(attachment_xml).gsub(/\.xml$/, '')
          File.open('tmp/rewrite-files.txt', 'a') do |outfile|
            outfile.puts("#{orig_file_number}\t\t\t#{url_for(file.url)}")
            Progress.say "I: Attachment with id = #{orig_article_number} redirected to #{url_for(file.url)}"
          end

        end
      end

      if SCRIPT_TYPE != 'import-cooperation.net-articles'
        # import menus
        for i in [1,2]
          links = []
          data = Hash.from_xml(File.read(File.join(IMPORT_DIR, username, "menu#{i}.xml")))
          data['menu']['items'].each do |item|
            links << { :name => item['title'], :address => item['url'] }
          end
          block = profile.blocks.select { |block| block.class == LinkListBlock }[i-1]
          block.title = data['menu']['title']
          block.links = links
          block.save!
          Progress.say "imported links: #{links.inspect}"
        end
      end

    end
    Progress.step_done
  rescue Exception => e
    $stderr.puts "=================================="
    $stderr.puts "E: importing <#{username}> failed."
    $stderr.puts e
    $stderr.puts e.backtrace
    $stderr.puts "=================================="
    $stderr.puts "E: Note that ALL operations above relative to <#{username}> were CANCELLED due to these errors."
    Progress.step_failed
  end
end