Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 230 lines (185 sloc) 6.15 KB
#!/usr/bin/perl
# trac2mw
# Converts a trac wiki to MediaWiki format
# Input:
# MySQL XML export of the Trac database
# Output:
# MediaWiki XML, suitable for feeding to Special:Import
#
# by Matthew Sachs <matthewg@zevils.com>
#
#This work is hereby released into the Public Domain. To view a copy
#of the public domain dedication, visit:
# http://creativecommons.org/licenses/publicdomain/
# Name of your MediaWiki
use constant WIKI_NAME => "MyWiki";
# Front page of your MediaWiki
use constant WIKI_BASE => "http://example.com/Wiki/Main_Page";
# To whitelist pages, list them here, one per line, e.g.
# @includePages = qw(
# FooBar
# FooBaz
# );
#
# If any pages are listed, only pages on the list will be included.
my @includePages = qw(
);
# Pages can be renamed.
# Trac page name => MediaWiki page name
my %renamePages = (WikiStart => "Main Page");
# Trac -> MediaWiki author map.
# TracUser => [MediaWiki username, MediaWiki user ID]
# Example:
# my %authors = (
# matthew => ["Matthew", 1],
# liz => ["Liz", 2]
# );
my %authors = ();
# END CONFIGURATION
use strict;
use warnings;
use XML::LibXML;
use POSIX qw(strftime);
our $VERSION = "0.1";
sub fixWikiList {
my($in) = @_;
my $ret = "";
my @rows = split(/\n/, $in);
my @inlists = ();
foreach my $row (@rows) {
$row =~ s/^ ( *)([#*])//;
my($space, $toplist) = ($1, $2);
while(length($space) < @inlists) {
pop @inlists;
}
push @inlists, $toplist;
$ret .= join("", @inlists) . $row . "\n";
}
return $ret;
}
sub fixWikiTable {
my($in) = @_;
my $ret = "{|\n";
my @rows = split(/\n/, $in);
foreach my $row (@rows) {
$ret .= "|-\n";
$row =~ s/^\|\|//;
$row =~ s/\|\|$//;
foreach my $col (split(/\|\|/, $row)) {
$ret .= "| $col\n";
}
}
$ret =~ s/\n\| -$/\n/;
$ret .= "|}\n";
return $ret;
}
sub fixWikiText {
my($text) = @_;
# [wiki:Foo] -> [Foo]
$text =~ s/\[wiki:/[/g;
# [Link] -> [[Link]]
$text =~ s/\[([a-z0-9 _-]+)\]/[[$1]]/ig;
# <sp><sp>* -> **
$text =~ s/((?:^(?: +)(?:[*#])(?:.*)$(?:\n?))+)/fixWikiList($1)/gem;
# CamelCase -> [[CamelCase]]
$text =~ s^ ([A-Z][a-z0-9]+[A-Z][a-z0-9]+[a-zA-Z0-9]*)\b^ [[$1]]^g;
# Table syntax
$text =~ s/((?:^\|\|.*\|\|$(?:\n?))+)/fixWikiTable($1)/gem;
return $text;
}
my $parser = XML::LibXML->new();
my $doc = $parser->parse_file(shift);
my $root = $doc->documentElement();
my @wikiPages = $root->findnodes("//table_data[\@name='wiki']/row");
my %pages;
foreach my $node (@wikiPages) {
my $name = $node->find("field[\@name='name']")->[0]->textContent();
next unless grep { $_ eq $name } @includePages;
$name = $renamePages{$name} if $renamePages{$name};
my $text = $node->find("field[\@name='text']")->[0]->textContent();
my $author = $node->find("field[\@name='author']")->[0]->textContent();
my $time = $node->find("field[\@name='time']")->[0]->textContent();
my $version = $node->find("field[\@name='version']")->[0]->textContent();
$version--;
$pages{$name} ||= [];
$pages{$name}->[$version] = {
text => $text,
author => $author,
time => $time,
};
}
my $outDoc = XML::LibXML::Document->new();
my $mw = XML::LibXML::Element->new("mediawiki");
$outDoc->setDocumentElement($mw);
use constant MW_NS => "http://www.mediawiki.org/xml/export-0.3/";
use constant XSI_NS => "http://www.w3.org/2001/XMLSchema-instance";
$mw->setNamespace(XSI_NS, "xsi");
$mw->setNamespace(MW_NS);
$mw->setAttributeNS(XSI_NS, "schemaLocation", "http://www.mediawiki.org/xml/export-0.3/ http://www.mediawiki.org/xml/export-0.3.xsd");
$mw->setAttributeNS(MW_NS, "version", "0.3");
$mw->setAttribute("xml:lang", "en");
my $siteinfo = XML::LibXML::Element->new("siteinfo");
$mw->addChild($siteinfo);
$siteinfo->appendTextChild("sitename", WIKI_NAME);
$siteinfo->appendTextChild("base", WIKI_BASE);
$siteinfo->appendTextChild("generator", "trac2mw $VERSION");
$siteinfo->appendTextChild("case", "first-letter");
my $namespaces = XML::LibXML::Element->new("namespaces");
$siteinfo->addChild($namespaces);
foreach my $ns ([-2, "Media"], [-1, "Special"],
[0, ""], [1, "Talk"],
[2, "User"],
[3, "User talk"],
[4, WIKI_NAME],
[5, WIKI_NAME . " talk"],
[6, "Image"],
[7, "Image talk"],
[8, "MediaWiki"],
[9, "MediaWiki talk"],
[10, "Template"],
[11, "Template talk"],
[12, "Help"],
[13, "Help talk"],
[14, "Category"],
[15, "Category talk"],) {
my $nsElement = XML::LibXML::Element->new("namespace");
$namespaces->addChild($nsElement);
$nsElement->setAttribute("key", $ns->[0]);
$nsElement->appendText($ns->[1]);
}
my $pageID = 1000;
while(my($page, $revisions) = each(%pages)) {
my $pageNode = XML::LibXML::Element->new("page");
$mw->addChild($pageNode);
$pageNode->appendTextChild("title", $page);
$pageNode->appendTextChild("id", $pageID);
my $revID = 1;
foreach my $revision(@$revisions) {
my($text, $author, $time) = ($revision->{text},
$revision->{author},
$revision->{time});
$text = fixWikiText($text);
my $revision = XML::LibXML::Element->new("revision");
$pageNode->addChild($revision);
$revision->appendTextChild("id", $pageID + $revID);
$revision->appendTextChild("timestamp",
strftime("%Y-%m-%dT%H:%M:%SZ",
gmtime($time)));
my $contributor = XML::LibXML::Element->new("contributor");
$revision->addChild($contributor);
my($user_id) = $authors{$author};
if($user_id) {
$contributor->appendTextChild("username", $user_id->[0]);
$contributor->appendTextChild("id", $user_id->[1]);
}
my $textNode = XML::LibXML::Element->new("text");
$revision->addChild($textNode);
$textNode->setAttribute("xml:space", "preserve");
$textNode->appendText($text);
} continue {
$revID++;
}
} continue {
$pageID += 1000;
}
$outDoc->toFH(\*STDOUT, 1);