#!/usr/bin/python2.4 # # Wordpress to Ikiwiki migration # vim: set foldmethod=marker: from optparse import OptionParser, Values import MySQLdb import sys import os from time import localtime, gmtime, strftime, strptime class WordPress2Ikiwiki(object): def __init__(self, options, args): self.options = options self.args = args self.tags = {} self.db_fields = ( # {{{ '%sposts.ID' % self.options.table_prefix, '%sposts.post_status' % self.options.table_prefix, '%sposts.post_title' % self.options.table_prefix, #'%sposts.post_author' % self.options.table_prefix, '%susers.display_name' % self.options.table_prefix, '%sposts.post_category' % self.options.table_prefix, '%sposts.post_content' % self.options.table_prefix, '%sposts.post_date' % self.options.table_prefix, '%sposts.post_date_gmt' % self.options.table_prefix, '%sposts.post_modified' % self.options.table_prefix, '%sposts.post_modified_gmt' % self.options.table_prefix, '%sposts.post_name' % self.options.table_prefix, # ignore these #'post_parent', #'comment_count', #'comment_status', #'guid', #'menu_order', #'ping_status', #'pinged', #'post_content_filtered', #'post_excerpt', #'post_mime_type', #'post_password', #'post_type', #'to_ping', ) # }}} self.templates = { # {{{ 'real_post' : """ [[meta title="%(post_title)s"]] [[meta author="%(post_author)s"]] [[meta date="%(post_date_gmt)s"]] %(post_content)s [[tag %(post_category)s ]] """, 'debug_post' : """ ID: %(ID)s post_status: %(post_status)s post_title: %(post_title)s post_author: %(post_author)s post_category: %(post_category)s post_content: %(post_content)s post_date: %(post_date)s post_date_gmt: %(post_date_gmt)s post_modified: %(post_modified)s post_modified_gmt: %(post_modified_gmt)s post_name: %(post_name)s -------------------------------------------------------------------------------- """, 'tag' : """ This feed contains pages with the tag "%(tag)s". [[inline pages="link(%(tag)s) and *blog/posts/* and !*/Discussion" show="10"]] """, } # }}} def makedirs(self, dirs): # {{{ """ make a dir but don't fail if it already exists """ for dir in dirs.itervalues(): try: os.makedirs(dir) except os.error, e: if e.errno == 17: print >> sys.stderr, "%s exists, not creating" % dir pass else: print >> sys.stderr, "failed to create dir %s" % dir raise # }}} def _getCategories(self, db, post_id): # {{{ """ get all catergories for a post """ q="""select wp_categories.cat_name from wp_categories LEFT join wp_post2cat on (wp_categories.cat_ID=wp_post2cat.category_id) where wp_categories.cat_ID=wp_post2cat.category_id and wp_post2cat.post_id=%d""" c=db.cursor() c.execute(q % post_id) post_tags = c.fetchall()[0] for tag in post_tags: if tag in self.tags: self.tags[tag] += 1 else: self.tags[tag] = 1 return ' '.join(post_tags) # }}} def _printTemplate(self, template, values, outfile): # {{{ self.makedirs({"temp" : os.path.dirname(outfile)}) if self.options.verbose: print "writing file %s" % outfile if self.options.debug: file = sys.stdout else: file = open(outfile, 'w') print >> file, template % values if not self.options.debug: file.close() if values.has_key('post_date') and values['post_date']: t=int(values['post_date'].strftime("%s")) os.utime(outfile, (t, t)) # }}} def convertPosts(self): # {{{ db=MySQLdb.connect( host=self.options.server, db=self.options.database, read_default_file=self.options.conf_file) c=db.cursor() c.execute("""SELECT %(fields)s FROM %(prefix)sposts LEFT JOIN %(prefix)susers on (%(prefix)sposts.post_author=%(prefix)susers.ID) """ % { 'fields' : ','.join(self.db_fields), 'prefix' : self.options.table_prefix, }) self.num_posts = c.rowcount draft_counter = 0 if self.options.debug: post_template = self.templates['debug_post'] else: post_template = self.templates['real_post'] for row in c.fetchall(): if row[1] == 'publish': out_file='%s/%s/%s.mdwn' % ( self.dirs['post'], row[6].strftime("%Y/%m/%d"), row[10]) elif row[1] == 'draft': if row[10] == "": out_file='%s/%s.mdwn' % (self.dirs['draft'], draft_counter) draft_counter += 1 else: out_file='%s/%s.mdwn' % (self.dirs['draft'], row[10]) elif row[1] == 'static': out_file='%s/%s.mdwn' % (self.dirs['static'], row[10]) else: print >> sys.stderr, "skipping post %s with unknown status %s" % ( row[0], row[9]) continue values = { 'ID' : row[0], 'post_status' : row[1], 'post_title' : row[2], 'post_author' : row[3], 'post_category' : self._getCategories(db, row[0]), 'post_content' : row[5], 'post_date' : row[6], 'post_date_gmt' : row[7], 'post_modified' : row[8], 'post_modified_gmt' : row[9], 'post_name' : row[10], } self._printTemplate(post_template, values, out_file) c.close() # }}} def convertTags(self): # {{{ for tag in self.tags.iterkeys(): tag_file = "%s/%s.mdwn" % ( self.dirs['tag'], tag.replace(' ', '_')) values = { 'tag' : tag } self._printTemplate(self.templates['tag'], values, tag_file) # }}} def main(): parser = OptionParser() parser.process_default_values = True parser.add_option("-d", "--dir", dest="outdir", default="/tmp/wp2iw", help="directory to write to, will be created if it doesn't exist", metavar="DIR") parser.add_option("-q", "--quiet", action="store_false", dest="verbose", default=True, help="don't print status messages to stdout") parser.add_option("-s", "--server", dest="server", default="localhost", help="MySQL server to query") parser.add_option("-b", "--db", dest="database", help="MySQL database to use") parser.add_option("-c", "--conf", dest="conf_file", default="~/.my.cnf", help="path to MySQL options file") parser.add_option("-t", "--table_prefix", dest="table_prefix", default="wp_", help="Wordpress table prefix") parser.add_option("--debug", dest="debug", action="store_true", default=False, help="use debug mode, write to stdout") (options, args) = parser.parse_args() if not options.database: parser.error("database not specified") migration = WordPress2Ikiwiki(options, args) migration.dirs = { 'tag' : '%s/tags' % options.outdir, 'post' : '%s/posts' % options.outdir, 'static' : '%s/static' % options.outdir, 'draft' : '%s/draft' % options.outdir, } migration.convertPosts() migration.convertTags() if options.verbose: print "Converted:" print "\t%d posts" % migration.num_posts print "\t%d tags" % len(migration.tags) if __name__ == "__main__": main()