oksure.org

Category: 컴퓨터

  • Migration: Zeroboard to WordPress (Using Ruby)

    오늘 하루 내가 겪은 제로보드에서 워드프레스로의 이동에 대해서 간략히 정리해 놓고자 한다.

    1. 옮길 테이블 정하기
      나의 경우에는 freeboard, letters, link 등 총 7개 게시판을 옮기기로 계획했다.
    2. DB Backup & 로컬 SQL 서버에 Restore
      기존 제로보드 홈페이지에서 DB를 백업 받는다. 나의 경우 호스팅 업체에서 sqldump로 전체 DB를 백업 받을 수 있도록 되어 있었다. 다운로드 받은 sql 파일을 로컬 서버의 mysql 콘솔을 통해서 restore한다.
    3. Ruby Gem Install
      내가 사용했던 gem들은 mysql-ruby와 libxml이었다.

      install gem mysql-ruby
      install gem libxml

      나의 경우에는 처음에 이렇게 설치하고 irb에서 require ‘mysql’ 을 했을 때 “invalid win32 application”이라고 에러가 났었는데, sql server를 64bit 버전으로 설치한 것이 문제였다. sql server를 32bit로 다시 설치했다. 각각 API Doc 주소는 다음과 같다.
      mysql: http://www.tmtm.org/en/mysql/ruby/
      libxml: http://libxml.rubyforge.org/rdoc/index.html

    4. Zeroboard의 field와 WordPress XML 포맷의 element 매칭
      제로보드는 sql server의 DB를 뒤져보면 필드가 쭉 나오는데, 문제는 워드프레스의 경우 DTD도 마땅히 찾기 힘들다는데 문제가 있었다. 이 사이트를 참고해서 WXR에 대해서도 대략 알게 되었다. 내가 정리한 board와 comment 테이블에서의 zeroboard와 wxr의 관계는 다음과 같다.

      • zetyx_[boardname] <=> WXR item
        • no <=> wp:post_id
        • memo <=> content:encoded
        • name <=> dc:creator
        • subject <=> title
        • reg_date <=> pubDate, wp:post_date, wp:post_date_gmt
      • zetyx_comment_[boardname] <=> WXR item
        • no <=> wp:comment_id
        • name <=> wp:comment_author
        • memo <=> wp:comment_content
        • ip <=> wp:comment_author_IP
        • reg_date <=> wp:comment_date, wp:comment_date_gmt
    5. Ruby 코드 짜기
      실제 루비 코드를 짜는데 있어서 크게 두 가지 정도가 방해가 되었는데, 하나는 xml encoding을 utf-8으로 기록하는 것과 CDATA 블럭을 넣는 방법을 몰랐었다. 둘 다 RDoc에 나오니까 “찾기”를 사용해서 찾으면 금방 나올 것이다. (내가 사용했던 코드는 이 글 제일 아래에 붙여 놓았다.)
    6. WordPress에서 import하기
      여기서 결정적으로 난국에 부딪혔던 것이 한글 문제였는데, 처음에 import 했을 때 한글이 다 깨져서 나오길래 왜 그런가 하고 봤더니 ruby에서 저장한 문서는 ANSI로 저장이 되어 있어서 그랬다. 편집기로 열어서 다른 이름으로 저장을 해서 UTF-8으로 저장하고 import하면 깔끔하게 된다. 이어서 또 귀찮았던 것은 import할 때 사람 이름이 user로 등록되어 있지 않으면 어떻게 처리할지 모두 다 물어보는 것이었다. 한글 아이디는 생성이 안 되기 때문에 모든 한글 이름을 그에 해당하는 영문 id를 입력해 주었다. 마지막으로 파일 크기가 너무 크면 한 번에 잘 업로드가 안 되니까 한 50개에서 100개 정도로 끊어서 import하는 것이 (최소한 내 경험 상에서는) 적절한 듯 싶다.

    다음은 내가 짰던 코드다.

    main.rb

    # To change this template, choose Tools | Templates
    # and open the template in the editor.
    
    puts "Hello World"
    
    require "mysql"
    require "xml"
    
    #####
    # Creating basic rss
    #####
    
    doc = XML::Document.new()
    doc.encoding = XML::Encoding.encoding_to_s(XML::Encoding::UTF8)
    
    doc.root = XML::Node.new('rss')
    root = doc.root
    root['version'] = '2.0'
    root['xmlns:content'] = 'http://purl.org/rss/1.0/modules/content/'
    root['xmlns:wfw'] = 'http://wellformedweb.org/CommentAPI/'
    root['xmlns:dc'] = 'http://purl.org/dc/elements/1.1/'
    root['xmlns:wp'] = 'http://wordpress.org/export/1.0/'
    
    root << channel = XML::Node.new('channel')
    channel << title = XML::Node.new('title') << 'oksure.org'
    channel << link = XML::Node.new('link') << 'http://oksure.byus.net/09012003'
    channel << description = XML::Node.new('description') << 'oksure.org'
    channel << pubDate = XML::Node.new('pubDate') << 'Wed, 10 Sep 2003 00:00:00 +0000'
    channel << generator = XML::Node.new('generator') << 'http://wordpress.org/?v=MU'
    channel << language = XML::Node.new('language') << 'ko'
    channel << wp_wxr_version = XML::Node.new('wp:wxr_version') << '1.0'
    channel << wp_category = XML::Node.new('wp:category')
    wp_category << wp_category_nickname = XML::Node.new('wp:category_nickname') << 'season1'
    wp_category << wp_category_parent = XML::Node.new('wp:category_parent')
    wp_category << wp_cat_name = XML::Node.new('wp:cat_name') << XML::Node.new_cdata('Season 1')
    wp_category << wp_category_description = XML::Node.new('wp:category_description') << XML::Node.new_cdata('Archive from oksure.org [2003-2004] ')
    ['banner', 'diary', 'freeboard', 'letter', 'link', 'profile', 'poem'].each do |category|
    	channel << wp_category = XML::Node.new('wp:category')
    	wp_category << wp_category_nickname = XML::Node.new('wp:category_nickname') << category
    	wp_category << wp_category_parent = XML::Node.new('wp:category_parent') << 'Season 1'
    	wp_category << wp_cat_name = XML::Node.new('wp:cat_name') << XML::Node.new_cdata(category.capitalize)
    end
    
    #####
    # MySQL part
    #####
    
    db = Mysql.real_connect("localhost", "root", "********", "oksurenet_backup_20081120")
    
    zb = "zetyx_"
    boards = ["freeboard", "4u_poem", "letters", "profile", "link", "diary", "main_ban"]
    
    boards.each do |board|
    	puts board + " is being processed."
    
    	category_name = board unless ["main_ban", "4u_poem", "letters"].include?(board)
    	if board == "letters"
    		category_name = "letter"
    	elsif board == "main_ban"
    		category_name = "banner"
    	elsif board == "4u_poem"
    		category_name = "poem"
    	end
    
    	result = db.query("SELECT * FROM " + zb + "board_" + board)
    	result.each_hash do |row|
    		channel << item = XML::Node.new('item')
    		item << title = XML::Node.new('title') << row["subject"]
    		item << link = XML::Node.new('link')
    		item << pubDate = XML::Node.new('pubDate') << Time.at(row["reg_date"].to_i).strftime("%a, %d %b %Y %H:%M:%S +0000")
    		item << dc_creator = XML::Node.new('dc:creator') << XML::Node.new_cdata(row["name"])
    		item << category = XML::Node.new('category') << XML::Node.new_cdata(category_name.capitalize)
    		item << category = XML::Node.new('category') << XML::Node.new_cdata(category_name.capitalize)
    		category['domain'] = 'category'
    		category['nicename'] = category_name
    		item << guid = XML::Node.new('guid')
    		guid['isPermaLink'] = 'false'
    		item << description = XML::Node.new('description')
    		item << content_encoded = XML::Node.new('content:encoded') << XML::Node.new_cdata(row["memo"])
    		item << content_excerpt = XML::Node.new('content:excerpt') << XML::Node.new_cdata('')
    		item << wp_post_id = XML::Node.new('wp:post_id') << row["no"]
    		item << wp_post_date = XML::Node.new('wp:post_date') << Time.at(row["reg_date"].to_i).strftime("%Y-%m-%d %H:%M:%S")
    		item << wp_post_date_gmt = XML::Node.new('wp:post_date_gmt') << (Time.at(row["reg_date"].to_i) - (60 * 60 * 9)).strftime("%Y-%m-%d %H:%M:%S")
    		item << wp_comment_status = XML::Node.new('wp:comment_status') << 'open'
    		item << wp_ping_status = XML::Node.new('wp:ping_status') << 'open'
    		item << wp_post_name = XML::Node.new('wp:post_name')
    		item << wp_status = XML::Node.new('wp:status') << 'publish'
    		item << wp_post_parent = XML::Node.new('wp:post_parent') << '0'
    		item << wp_menu_order = XML::Node.new('wp:menu_order') << '0'
    		item << wp_post_type = XML::Node.new('wp:post_type') << 'post'
    		item << wp_post_password = XML::Node.new('wp:post_password')
    
    		result_comment = db.query("SELECT * FROM " + zb + "board_comment_" + board + " where parent = " + row["no"])
    		result_comment.each_hash do |row_comment|
    			item << wp_comment = XML::Node.new('wp:comment')
    			wp_comment << wp_comment_id = XML::Node.new('wp:comment_id') << row_comment["no"]
    			wp_comment << wp_comment_author = XML::Node.new('wp:comment_author') << XML::Node.new_cdata(row_comment["name"])
    			wp_comment << wp_comment_author_email = XML::Node.new('wp:comment_author_email')
    			wp_comment << wp_comment_url = XML::Node.new('wp:comment_url')
    			wp_comment << wp_comment_IP = XML::Node.new('wp:comment_IP') << row_comment["ip"]
    			wp_comment << wp_comment_date = XML::Node.new('wp:comment_date') << Time.at(row_comment["reg_date"].to_i).strftime("%Y-%m-%d %H:%M:%S")
    			wp_comment << wp_comment_date_gmt = XML::Node.new('wp:comment_date_gmt') << (Time.at(row_comment["reg_date"].to_i) - (60 * 60 * 9)).strftime("%Y-%m-%d %H:%M:%S")
    			wp_comment << wp_comment_content = XML::Node.new('wp:comment_content') << XML::Node.new_cdata(row_comment["memo"])
    			wp_comment << wp_comment_approved = XML::Node.new('wp:comment_approved') << '1'
    			wp_comment << wp_comment_type = XML::Node.new('wp:comment_type')
    			wp_comment << wp_comment_parent = XML::Node.new('wp:comment_parent') << '0'
    			wp_comment << wp_comment_user_id = XML::Node.new('wp:comment_user_id') << '0'
    		end
    	end
    	puts "Number of rows returned: #{result.num_rows}"
    
    end
    
    puts "===== Created WXR (WordPress eXtended Rss) file ====="
    
    db.close
    
    #####
    # Writing XML
    #####
    
    format = true
    doc.save('C:\libxml.xml', format)