오늘 하루 내가 겪은 제로보드에서 워드프레스로의 이동에 대해서 간략히 정리해 놓고자 한다.
- 옮길 테이블 정하기
나의 경우에는 freeboard, letters, link 등 총 7개 게시판을 옮기기로 계획했다. - DB Backup & 로컬 SQL 서버에 Restore
기존 제로보드 홈페이지에서 DB를 백업 받는다. 나의 경우 호스팅 업체에서 sqldump로 전체 DB를 백업 받을 수 있도록 되어 있었다. 다운로드 받은 sql 파일을 로컬 서버의 mysql 콘솔을 통해서 restore한다. - 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 - 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
- zetyx_[boardname] <=> WXR item
- Ruby 코드 짜기
실제 루비 코드를 짜는데 있어서 크게 두 가지 정도가 방해가 되었는데, 하나는 xml encoding을 utf-8으로 기록하는 것과 CDATA 블럭을 넣는 방법을 몰랐었다. 둘 다 RDoc에 나오니까 “찾기”를 사용해서 찾으면 금방 나올 것이다. (내가 사용했던 코드는 이 글 제일 아래에 붙여 놓았다.) - 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)