Updating Instiki from v0.10 to v0.13

A company I work for uses Instiki to as a wiki-type system on the company intranet. Instiki is a simple RoR (Ruby on Rails) wiki clone written by David Heinemeier Hansson (… the creator of the Ruby on Rails framework). It’s a great app & I would recommend looking into it if you have not already.

Anyway, I found that upgrading from v0.10 to v0.13 was a little challenging. Here’s what I did to make it work the way I wanted it to:

1. Download & Install instiki v0.13. It’s damn easy. Follow onscreen directions. If you get stuck, see the Instiki website.

2. Dump data from v0.10 to a sql file.

There is a dump script for recent versions of instiki, but I did not see the v0.10 install. The dump script is called “import_storage”.

You can use the “import_storage “script that comes with v0.13 to dump the data from your v0.10 install by executing the following from within instiki v0.13 app root directory:

ruby script/import_storage -t /tmp/i2500 -i /usr/local/instiki-0.10.1/ -d mysql -o /tmp/instiki_20080408-1.sql

/usr/local/instiki-0.10.1/ is the path to the 0.10 app
/tmp/instiki_20080408-1.sql is the path of the dump file
/tmp/12500 is a temp path (may or may not be needed depending on system permissions)

From there, you should have a sql file that contains a dump of the current v0.10 database in the form of a .sql file located at: /tmp/instiki_20080408-1.sql

3. Load up 0.10 data into the 0.13 database

When you installed instiki 0.13, you created a new database. Let’s call that database “wiki”. You need to load the sql file you just created into that database now.

You can load the .sql file into your mysql ‘wiki’ database by executing:

$ mysql -u[username] -p[password] wiki < /tmp/instiki_20080408-1.sql

4. Empty the SPAM pattern file

v0.13 contains a SPAM detection feature that uses keywords. Cool idea, but the default list of patterns is kinda odd. For example, any page that contains the following words will be marked as SPAM: airplane, good job, hamburger…. and a ton of other totally random words. I would definitely recommend emptying the SPAM pattern file (and adding words as necessary if needed).

You can empty the SPAM pattern file by executing:

echo “” > [app root]/config/spam_patterns.txt

5. Allow forward-slash (/) to appear in page names.

With instiki v0.10, page names could contain forward slashes in them, for example: “CARS/TRUCKS”. When I installed v0.13, I found that links to these pages would cause a internal server error – a error screen was displayed instead of the page I wanted. Not sure if this is due to changes with rails, routing, or instiki itself, but I was able to change the routes to behave the way I wanted them to by adding 1 line to [app root]/config/routes.rb….

after the last “connect_to_web” line in routes.rb, I added:

connect_to_web map, ‘:web/show/*id’, :controller => ‘wiki’, :action => ‘show’

This should “glob” any text after :web/show to an array. Also, you will then need to edit the “load_page” method in [app root]/app/controllers/wiki_controller.rb to work with the array (it expects a string by default).

Search [app root]/app/controllers/wiki_controller.rb for “def load_page”, and replace the line:

@page_name = params[‘id’]


@page_name = params[‘id’].to_a.join(‘/’)

At that point, you should be able to use forward slashes in page names.

6. Tweak the HTML sanitization file

Lastly, I tweaked the [app root]/lib/sanitize.rb file a bit. You may need to add elements to the “acceptable_elements” array as needed (top of the file). But more importantly, I found that HTML that was entered in uppercase (for example, <BR> or <H1>) was being escaped. I know it’s old-school HTML, but that’s what the company’s wiki contained. So, rather than updating the HTML, I made the wiki accept it. It seems that because it is uppercase, it fails to match the acceptable list of tags. I changed the sanitize_html method to be:

def sanitize_html(html)
if html.index(“<“)
tokenizer = HTML::Tokenizer.new(html)
new_text = “”

while token = tokenizer.next
node = XHTML::Node.parse(nil, 0, 0, token, false)
new_text << case node.tag?
when true
if ALLOWED_ELEMENTS.include?(node.name.downcase)
if node.closing != :close
node.attributes.delete_if { |attr,v| !ALLOWED_ATTRIBUTES.include?(attr) }
ATTR_VAL_IS_URI.each do |attr|
val_unescaped = CGI.unescapeHTML(node.attributes[attr].to_s).gsub(/[00-40\177-\240]+/,”).downcase
if val_unescaped =~ /^[a-z0-9][-+.a-z0-9]*:/ and !ALLOWED_PROTOCOLS.include?(val_unescaped.split(‘:’)[0])
node.attributes.delete attr
if node.attributes[‘style’]
node.attributes[‘style’] = sanitize_css(node.attributes[‘style’])
node.to_s.gsub(/</, “<“)
node.to_s.gsub(/</, “<“)

html = new_text

… the main difference is the addition of “downcase” methods to the comparison lines.

That’s it. Works great for me. Please let me know if this was helpful, or if you have a better way to achieve these results!