CGI::Kwiki
This article is about a new Perl module called CGI::Kwiki. With this module you can create a Wiki Web site in less than a minute. Now that’s quick. Or more appropriately, ``That’s Kwik!”
If you’ve not heard of a Wiki, it’s a Web site that allows you to add and edit pages directly from your browser. Generally, every page on the site has a link or button that will let you edit the page that you are reading. When you edit a page, the raw contents of that page come up in a text edit area in your browser. You can make any changes you want. When you hit the SAVE button, the changes become live.
To create a new page, you just create a link on the current page to a page that didn’t exist before. Then when you follow the new link, you are allowed to edit the new page.
Knowledge of HTML is not a prerequisite for using a Wiki. It’s not even a requisite, because the raw Wiki contents that you edit are not presented as HTML. Wikis use a much more natural markup, that resembles the messages posted in Usenet news groups. An example can speak for itself:
== A Page Header for a Sample Wiki Page ==
Here's list of some WikiFormattingCodes:
* Lines that begin '* ' form a bulleted list
* Asterisks might be used to mean *bold* text
* Links like http://www.perl.com work automatically
The only markup that should require further explanation is the text WikiFormattingCodes
. Capitalized words that are mushed together form a link to another page on the Wiki site.
A Wiki is simply a Web site that is easy for ordinary people to edit. So where did the Wiki idea come from and why is it important?
Ward’s Wiki Wisdom
I’ve only been dabbling in the world of Wiki for less than a year. Rather than answer that question myself, I decided to ask the inventor of the Wiki. Now by pure coincidence, Ward Cunningham lives but a few miles from my house and well with in my telephone area code. I decided to drop him a line, and find out his innermost feelings on his creation:
Brian: Yes, hello. May I speak to Mr. Ward Cunningham?
Ward: Who is this?
Brian: This is Brian Ingerson from Perl.com. I have a few questions.
Ward: Perl?! That’s not me! Wall is to blame. Call him.
Brian: No. Wait. It’s about the Wiki.
Ward: Ah, yes. The Wiki. Well let’s get to business.
Brian: Why did you invent the Wiki?
Ward: Wiki had a predecessor that was a hyper-card stack. I wrote it to explore hypertext. I wanted to try recording something that was ragged, something that wouldn’t fit into columns. I had this pet theory that programming ideas were spread by people working together. I set out to chart the flow of ideas through my company (then Tektronix). This turned out to be more fun than I ever would have imagined.
When we were really trying to capture a programmer’s experience in software patterns, I remembered that stack and set out to do it over with the technology of the moment, the World Wide Web. This was 1994. I wrote Wiki to support and enlarge the community writing software patterns.
Brian: What do you see as Wiki’s most-positive contribution to the world?
Ward: Back in 1994, the Web was a pretty wonderful place, with lots of people putting up stuff just because they thought someone else would find it interesting or useful. Wiki preserves that feeling in a place that has become too much of a shopping mall. It reminds people that sometimes to work together you have to trust each other more than you have any reason to.
Brian: Are you concerned that there are so many different Wiki implementations?
Ward: I was concerned once. I wish everyone used my markup instead of inventing their own. But that didn’t happen. Now I realize that the implementations have done more to spread the idea than I ever could with my one version. That is the way it is with really simple things.
Brian: What programming language is your Wiki written in?
Ward: Um, … Perl.
Brian: Tell me about that.
click
Wikis Wikis Everywhere
Just in case you didn’t visualize the tongue entering and exiting my cheek, Ward does not have anything against Perl. To the contrary, he does almost all his open-source development with it, including his Wiki software. Try visiting his Wiki site, http://c2.com/cgi/wiki, for an excellent introduction to Wiki.
As was pointed out, there are many many implementations that have sprung forth since the Wiki was invented, and many of those were written in Perl. That’s because a Wiki is fairly easy to implement and everyone seems to want to do it slightly differently.
Most of these implementations are just simple CGI scripts at heart. Even though they may have gathered dozens of special features over the years, they are really just ad hoc programs that are not particularly modularized or designed for extensibility.
One notable exception is the CGI::Wiki module by Kate ``Kake” Pugh. This relatively new CPAN distribution is designed to be a Wiki framework. The various bits of functionality are encapsulated into class modules that can be extended by end users. As far as I know, this project is the first attempt in Perl to modularize the Wiki concept. It’s about time!
The second attempt is a completely different module called CGI::Kwiki; the subject of this article. When I evaluated CGI::Wiki, I found it a little too heavy for my needs. It had about a dozen prerequisite modules and required an SQL database. CGI::Kwiki by comparison requires no extra modules besides those that come with Perl, and stores its Web pages as plain text files.
I find this preferable, because I can install a new Kwiki in seconds (literally) and I have the full arsenal of Unix commands at my disposal for manipulating the content. In fact, the default search facility for CGI::Kwiki is just a method call that invokes the Unix command grep
.
Another compelling aspect of CGI::Kwiki is that every last bit of it is extensible, and extending it is trivial. About the only thing you can’t easily change is the fact that it is written in Perl.
Because of this, I have probably set up more than a dozen Kwiki sites in the past month, and customized each one according to my needs. In this article, I’ll show you how to do the same thing.
The Kwikest Way to Start
So just how easy is it to install a Kwiki? Well, that depends on how many of the basics you already have in place. You need a Web server and Perl, of course. You also need to have the CGI::Kwiki module installed from CPAN. That’s about it.
For the sake of a specific example, let’s say that you are running the Apache Web server (version 1.3.x) and that /home/johnny/public_html/cgi-bin/
is a CGI-enabled directory. With that setup in place, you can issue the following commands to create a new Kwiki:
cd /home/johnny/public_html/cgi-bin/
mkdir my-kwiki
cd my-kwiki
kwiki-install
Done! Your Kwiki is installed and ready for action. You should be able to point your Web browser at:
http://your-domain/~johnny/cgi-bin/my-kwiki/index.cgi
and begin your wiki adventure.
At this point, if you do an ls
command inside the my-kwiki
directory, then you should see two files (index.cgi
and config.yaml
). index.cgi
is just a point of execution for the CGI::Kwiki class modules, and config.yaml is little more than a list of which class modules are being used. You should also see a directory called database
, where all your Kwiki pages are stored as individual plain text files.
These files will become important later as we explore how to customize Kwiki to your personal needs or whims.
If you are having trouble configuring Apache for CGI, then here is the basic httpd.conf
section that I use for my personal Kwikis:
Alias /kwiki/ /home/ingy/kwiki/
<Directory /home/ingy/kwiki/>
Order allow,deny
Allow from all
Options ExecCGI FollowSymLinks Indexes
AddHandler cgi-script .cgi
DirectoryIndex index.cgi
</Directory>
This allows me to connect with this URL:
http://localhost/kwiki/
Using Your Kwiki
When you first visit your newly installed Kwiki, you’ll notice that there are a number of default pages already installed. Most notably is the one called HomePage, because that’s the one you’ll see first. This page requests that you change it as soon as possible. Go ahead and give it a try. Click the EDIT
button.
You should see the text of HomePage laid out in Kwiki format inside an editable text area. Make some changes and click the SAVE
button. The first thing you’ll probably want to know is exactly how all the little Kwiki markup characters work.
KwikiFormattingRules
CGI::Kwiki has a set of default formatting rules that reflect my favorites from other Wikis. Some are from WardsWiki, some from MoinMoin, some from UseMod. All of them are customizable. More on that shortly. For now, let’s go over the basics.
The first thing to learn is how to create a link. A link to another page on the site is made by squishing two or more words together in CamelCase. If the page doesn’t exist yet, then that’s OK. Clicking on it will allow you to create the new page from scratch. This is how Wikis grow.
You can also create an external link by simply starting some text with http:
. Like http://c2.com/cgi/wiki, the original Wiki Web site. Sometimes you want an internal link that isn’t CamelCase. Just put the link text inside square brackets. If you want the link to be external, then add the http:
component inside the brackets:
[check_this_out]
[check this out http://checked.out]
The second most-common formatting rule I use is preformatted text. This is used for things like Perl code examples. Text that is preformatted is automatically immune to futher Wiki processing. To mark text as preformatted you just indent it. This is similar to the approach that POD takes:
sub backwards_string {
return join '', reverse split '', shift;
}
One of the FormattingRules that I personally like is the ability to create HTML tables. You do it like this (if you’re a bowler):
| Player | 1 | 2 | 3 |
| Marv | 8-1 | X | 9-/ |
| Sally | X | X | 8-1 |
| Ingy | 5-2 | 6-0 | 7-0 |
| Big Al | 0-1 | 5-\ | X |
(The people I bowl with usually get tired after three frames)
Tables are made by separating cells with vertical bar (or pipe) characters. Many times I need to put multiline text inside the cells. Kwiki accomplishes this by allowing a Here-Document style syntax:
| yaml | perl | python |
| <<end_yaml | <<end_perl | {'foo':'bar','bar':[42]} |
---
foo: bar
bar:
- 42
end_yaml
{
foo => 'bar',
bar =>
[ 42 ]
}
end_perl
Kwiki has a fairly rich set of default formatting rules. You’ll find an exhaustive list of all the rules right inside your new Kwiki. The page is called KwikiFormattingRules. To find this page (and every other page on your Kwiki) click the RecentChanges link at the top of the current page.
KustomizingKwiki
To those of you familiar with the Wiki world, this has all been fairly pedestrian stuff so far. Here’s where I think that things get interesting. As I stated before, every last part of the Kwiki software is changable, customizable and extensible. Best of all, it’s easy to do.
CGI::Kwiki is made up of more than a dozen class modules. Each class is responsible for a specific piece of the overall Kwiki behavior. To change something about a particular class, you just subclass it with a module of your own.
Some of the more important CGI::Kwiki classes are:
CGI::Kwiki::Formatter
This is the most-common class to extend. It’s where all the formatting rules are codified.
CGI::Kwiki::Database
This class loads and stores the Kwiki pages.
CGI::Kwiki::Template
Defines the HTML that gives the look and feel to your Kwiki.
CGI::Kwiki::Driver
Controls the entire CGI process.
CGI::Kwiki::Config
An abstraction for reading the config file parameters.
Kwiki knows what classes to use by looking in it’s config file. So if you want to subclass something, then the first thing you would do is change the config.yaml
entry to point to your new class. Let’s start with a easy one.
A Kwik and Dirty Tweak
Kwiki will turn a word or phrase inside *
asterisks*
to bold text. This is similar to the way you might do it in text e-mail. But WardsWiki uses '''
triple quotes'''
for bolding. Let’s change your Kwiki to do it like Ward does.
First, create a file called MyFormatter.pm
. You can put it right inside your Kwiki installation directory, and Kwiki will find it. The contents of the file should look like this:
package MyFormatter;
use base 'CGI::Kwiki::Formatter';
sub bold {
my ($self, $text) = @_;
$text =~ s#'''(.*?)'''#<b>$1</b>#g;
return $text;
}
1;
Now, change the config.yaml
file to use this line:
formatter_class: MyFormatter
The Kwiki formatting engine will now call your subroutine with pieces of text that are eligible to contain bold formatting. Sections of text that are already preformatted code will not be passed to your bold()
method. And as you can see, MyFormatter
is a subclass of CGI::Kwiki::Formatter
so all the other formatting behaviors remain intact.
Kwiki’s Formatting Engine
Let’s look under the hood at CGI::Kwiki’s hotrod formatting engine. You’ll need to be familiar with it to do any serious formatting changes. Conceptually, it’s rather simple. It works like this.
- The text starts out as one big string.
- There is a list of formatting routines that are applied in a certain order.
- The string is passed to the first formatting routine. This routine may change the original text. It may also break the text into a number of substrings. It then return the strings it has created and manipulated.
- Each of the substrings is run through the next formatting routine in line.
- Sometimes, a formatting routine will want to make sure that no further routines touch a particular substring. It can do this by returning a hard reference to that string.
- After all the substrings have been passed through every routine, they are joined back together to form one long string.
The specific routines and their order of execution is determined by another method called process_order()
. The process_order
method just returns a list of method names in the order they should be called. The default process_order
method is:
sub process_order {
return qw(
function
table code header_1 header_2 header_3
escape_html
lists comment horizontal_line
paragraph
named_http_link no_http_link http_link
no_wiki_link wiki_link force_wiki_link
bold italic underscore
);
}
The best way to get a good feel for how to do things is to look over the CGI::Kwiki::Formatter module itself.
KontentKontrol
The biggest fear that many people have of setting up a Wiki site is that someone will come along and destroy all their pages. This happens from time to time, but in general people just don’t do it. It’s really not that cool of a trick to pull off. Someone could even write a program to destroy a Wiki, but if they were that smart, hopefully they’d be mature enough not to do it.
As of this writing, CGI::Kwiki doesn’t do anything to protect your data. But remember, it’s just code. Let’s now extend your code to do a simple backup, everytime a page is written.
Possibly the simplest way to back up files on Unix is to use RCS. Let’s make the Kwiki perform an RCS checkin every time it saves a page.
This time we need to extend the database class. Change the config file like so:
database_class: MyDatabase
Then write a file called MyDatabase.pm that looks like:
package MyDatabase;
use base 'CGI::Kwiki::Database';
sub store {
my $self = shift;
my ($file) = @_;
$self->SUPER::store(@_);
system(qq{ci -q -l -m"saved" database/$file backup/$file,v});
}
1;
Note: Be sure to add a backup directory that the CGI program can write to:
mkdir backup
chmod 777 backup
In this case the store
method calls its parent method to handle this actual database store. But then it invokes an extra rcs command to backup the changes to the file.
Hopefully these examples will give you an idea of how to go about making other types of modifications to CGI::Kwiki. If you make a whole set of cohesive and generally useful extensions, then please consider putting them on CPAN as module distribution.
A Kwiki in Every Pot
The classic use for a Wiki site is to provide a multi-user forum for some topic of interest. In this context, Wiki is a great collaboration tool. People can add new ideas, and revise old ones. The Wiki serves as both an archive and a news site. Most Wikis provide a search mechanism and a RecentChanges facility.
But I think this only scratches the surface of Wiki usage possibilities. Since a Kwiki is so easy to create, I now find myself doing it all the time. It’s almost like I’m creating a new wiki for every little thing I set out to do. Here’s a few examples:
Personal Planning
I have a personal wiki for keeping track of my projects. I keep it on my laptop.
Module Development
Every Perl module I write these days has its own Kwiki in the directory. I use them mainly for creating Test::FIT testing tables. (See Test::FIT on CPAN). But I can also use it for project notes and documentation. Since I can extend the Kwiki, I can make it export the pages to POD if I want.
Autobiowiki
I am seriously considering writing the stories of my life in a Wiki. If I can get others to to the same, then the Wikis could be linked using Ward’s SisterSite mechanism. This would create one big story. (See http://c2.com/cgi/wiki)
Project Collaboration
For my bigger projects I like to create a user community based around a Wiki. Using Test::FIT I can actually get my users to write failing tests for my projects. And they can help write documentation, report bugs, share recipes, etc. (See http://fit.freepan.org and http://yaml.freepan.org)
Conclusion
One final point of interest; this entire article was written in a Wiki format. I needed to submit it to my editor in POD format, which he in turn formatted into the HTML you are reading now. I accomplished this by simply using an extension of CGI::Kwiki::Formatter that produces POD instead of HTML!
NOTE: The raw content of this article along with the formatter program can be found at http://www.freepan.org/ingy/articles/kwiki/
Editor’s note: http://www.kwiki.org has been created as the official kwiki home page.
Brian Ingerson has been programming for more than 20 years, and hacking Perl for five of those. He is dedicated to improving the overall quality of scripting languages including Perl, Python and Ruby. He currently hails from Portland, Ore.; the very location of this year’s O’Reilly Open Source Convention. How convenient!
Tags
Feedback
Something wrong with this article? Help us out by opening an issue or pull request on GitHub