Skip to content


Migration: Zeroboard to WordPress (Using Ruby)

  • Twitter
  • email
  • PDF
  • Facebook
  • Google Bookmarks
  • del.icio.us

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

  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 &lt;&lt; channel = XML::Node.new('channel')
channel &lt;&lt; title = XML::Node.new('title') &lt;&lt; 'oksure.org'
channel &lt;&lt; link = XML::Node.new('link') &lt;&lt; 'http://oksure.byus.net/09012003'
channel &lt;&lt; description = XML::Node.new('description') &lt;&lt; 'oksure.org'
channel &lt;&lt; pubDate = XML::Node.new('pubDate') &lt;&lt; 'Wed, 10 Sep 2003 00:00:00 +0000'
channel &lt;&lt; generator = XML::Node.new('generator') &lt;&lt; 'http://wordpress.org/?v=MU'
channel &lt;&lt; language = XML::Node.new('language') &lt;&lt; 'ko'
channel &lt;&lt; wp_wxr_version = XML::Node.new('wp:wxr_version') &lt;&lt; '1.0'
channel &lt;&lt; wp_category = XML::Node.new('wp:category')
wp_category &lt;&lt; wp_category_nickname = XML::Node.new('wp:category_nickname') &lt;&lt; 'season1'
wp_category &lt;&lt; wp_category_parent = XML::Node.new('wp:category_parent')
wp_category &lt;&lt; wp_cat_name = XML::Node.new('wp:cat_name') &lt;&lt; XML::Node.new_cdata('Season 1')
wp_category &lt;&lt; wp_category_description = XML::Node.new('wp:category_description') &lt;&lt; XML::Node.new_cdata('Archive from oksure.org [2003-2004] ')
['banner', 'diary', 'freeboard', 'letter', 'link', 'profile', 'poem'].each do |category|
	channel &lt;&lt; wp_category = XML::Node.new('wp:category')
	wp_category &lt;&lt; wp_category_nickname = XML::Node.new('wp:category_nickname') &lt;&lt; category
	wp_category &lt;&lt; wp_category_parent = XML::Node.new('wp:category_parent') &lt;&lt; 'Season 1'
	wp_category &lt;&lt; wp_cat_name = XML::Node.new('wp:cat_name') &lt;&lt; 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 &lt;&lt; item = XML::Node.new('item')
		item &lt;&lt; title = XML::Node.new('title') &lt;&lt; row["subject"]
		item &lt;&lt; link = XML::Node.new('link')
		item &lt;&lt; pubDate = XML::Node.new('pubDate') &lt;&lt; Time.at(row["reg_date"].to_i).strftime("%a, %d %b %Y %H:%M:%S +0000")
		item &lt;&lt; dc_creator = XML::Node.new('dc:creator') &lt;&lt; XML::Node.new_cdata(row["name"])
		item &lt;&lt; category = XML::Node.new('category') &lt;&lt; XML::Node.new_cdata(category_name.capitalize)
		item &lt;&lt; category = XML::Node.new('category') &lt;&lt; XML::Node.new_cdata(category_name.capitalize)
		category['domain'] = 'category'
		category['nicename'] = category_name
		item &lt;&lt; guid = XML::Node.new('guid')
		guid['isPermaLink'] = 'false'
		item &lt;&lt; description = XML::Node.new('description')
		item &lt;&lt; content_encoded = XML::Node.new('content:encoded') &lt;&lt; XML::Node.new_cdata(row["memo"])
		item &lt;&lt; content_excerpt = XML::Node.new('content:excerpt') &lt;&lt; XML::Node.new_cdata('')
		item &lt;&lt; wp_post_id = XML::Node.new('wp:post_id') &lt;&lt; row["no"]
		item &lt;&lt; wp_post_date = XML::Node.new('wp:post_date') &lt;&lt; Time.at(row["reg_date"].to_i).strftime("%Y-%m-%d %H:%M:%S")
		item &lt;&lt; wp_post_date_gmt = XML::Node.new('wp:post_date_gmt') &lt;&lt; (Time.at(row["reg_date"].to_i) - (60 * 60 * 9)).strftime("%Y-%m-%d %H:%M:%S")
		item &lt;&lt; wp_comment_status = XML::Node.new('wp:comment_status') &lt;&lt; 'open'
		item &lt;&lt; wp_ping_status = XML::Node.new('wp:ping_status') &lt;&lt; 'open'
		item &lt;&lt; wp_post_name = XML::Node.new('wp:post_name')
		item &lt;&lt; wp_status = XML::Node.new('wp:status') &lt;&lt; 'publish'
		item &lt;&lt; wp_post_parent = XML::Node.new('wp:post_parent') &lt;&lt; '0'
		item &lt;&lt; wp_menu_order = XML::Node.new('wp:menu_order') &lt;&lt; '0'
		item &lt;&lt; wp_post_type = XML::Node.new('wp:post_type') &lt;&lt; 'post'
		item &lt;&lt; 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 &lt;&lt; wp_comment = XML::Node.new('wp:comment')
			wp_comment &lt;&lt; wp_comment_id = XML::Node.new('wp:comment_id') &lt;&lt; row_comment["no"]
			wp_comment &lt;&lt; wp_comment_author = XML::Node.new('wp:comment_author') &lt;&lt; XML::Node.new_cdata(row_comment["name"])
			wp_comment &lt;&lt; wp_comment_author_email = XML::Node.new('wp:comment_author_email')
			wp_comment &lt;&lt; wp_comment_url = XML::Node.new('wp:comment_url')
			wp_comment &lt;&lt; wp_comment_IP = XML::Node.new('wp:comment_IP') &lt;&lt; row_comment["ip"]
			wp_comment &lt;&lt; wp_comment_date = XML::Node.new('wp:comment_date') &lt;&lt; Time.at(row_comment["reg_date"].to_i).strftime("%Y-%m-%d %H:%M:%S")
			wp_comment &lt;&lt; wp_comment_date_gmt = XML::Node.new('wp:comment_date_gmt') &lt;&lt; (Time.at(row_comment["reg_date"].to_i) - (60 * 60 * 9)).strftime("%Y-%m-%d %H:%M:%S")
			wp_comment &lt;&lt; wp_comment_content = XML::Node.new('wp:comment_content') &lt;&lt; XML::Node.new_cdata(row_comment["memo"])
			wp_comment &lt;&lt; wp_comment_approved = XML::Node.new('wp:comment_approved') &lt;&lt; '1'
			wp_comment &lt;&lt; wp_comment_type = XML::Node.new('wp:comment_type')
			wp_comment &lt;&lt; wp_comment_parent = XML::Node.new('wp:comment_parent') &lt;&lt; '0'
			wp_comment &lt;&lt; wp_comment_user_id = XML::Node.new('wp:comment_user_id') &lt;&lt; '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)

관련 글 (링크에 마우스를 올리면 요약 내용이 보입니다.)

  1. 워드프레스 호스팅 업체 옮기기 (연관성: 5.71109점)
  2. 오~ (연관성: 4.8548점)
  3. 다우버(Dauver) – 아고라에 썼던 글 (1) (연관성: 4.31668점)
  4. Delicious+Chrome: 자바스크립트 즐겨찾기 버튼 (연관성: 4.28995점)
  5. 파이썬 네트워크 시각화 소스 코드 (연관성: 4.13422점)

Posted in 컴퓨터. Tagged with , , , , .

0 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

Some HTML is OK

(never shared)

or, reply to this post via trackback.