Tutorials

Pm Wiki Blog Administration

Since I started internetting in around 1995, I've wanted a web presence, but have been unsatisfied by some part of the process. I've used a ton of blogging sites and content management systems, including (but not limited to) Diaryland (which amazingly still exists), Livejournal, Wordpress, MediaWiki, DokuWiki, and Joomla!, but they all fell short in some way.

PmWiki, along with the BlogIt cookbook (cookbooks are analogous to plugins or addons) has met all of the criteria that I've been looking for in a web presence.

PmWiki + BlogIt advantages (for me)

  • Extremely easy to extend and customize

I was never able to wrap my head around creating templates and plugins for Wordpress and other systems, but I can see myself writing something for PmWiki. It forces you to learn a bit up-front (through copy-pasting simple code rather than clicking checkboxes on an admin page) but after that the general structure of the code and how to extend it becomes obvious.

  • Content is stored as flat files

This makes everything easy to back up, and suitable for archival. I find myself annoyed by the size and disorganization in my "documents" folder, so the plan is to move everything there to this site eventually (albeit not necessarily publicly). In addition, NearlyFreeSpeech.net has you pay for the storage, bandwidth and other services that you actually use, and by not using a database I save $0.02 per day (later, I'll give some more tips on how to keep hosting costs down).

  • Looks pretty nice

I mean, just look around. Nice.

  • Keeps revisions

Since PmWiki is a wiki, the complete edit history of a page is maintained; that means that if I happen to make some errant changes to a page (even a blog post) I can see exactly what I've changed and when. While this is not necessarily that important for a one-user blog, it can provide an interesting picture of the life of a document, especially a large one that changes over time. I'm not sure if I'll write my thesis on bekolay.org necessarily, but if I do, having the list of changes could prove quite useful (or at least interesting).

  • Amazing support

I tend to be the type of person that doesn't give a shit about support, but there were a few instances in the past few weeks that have given me pause. While there is unfortunately no support forum, the PmWiki page itself picks up the slack. I've added a question to some of the wiki pages there and gotten the exact response I was looking for every time. I've also had a question answered on the pmwiki-users mailing list, though I am not really a big fan of the mailing list system (I much prefer forums).

Possible disadvantages

Some considerations that you might want to keep in mind if you're thinking of going this route yourself:

  • Steep learning curve

Being not-quite-as-popular as similar software packages like MediaWiki, the installation process and administration are not quite as refined as, say, the one-step process of Wordpress. Of course, if you're administering a website in general, this probably shouldn't deter you. If the phrase "copy-paste these lines to your local/config.php" doesn't confuse or frighten you, then you're fine.

  • Centralized support

Like I mentioned above, the support I've received has been absolutely great, primarily because it's come from the developers themselves. However, this means that if those developers move on to other projects, support might not be so readily available. In most large projects, users support each other, and while this certainly does happen with PmWiki, the install-base is undoubtedly smaller than other similar software packages.


Setting up PmWiki for blogging

The following steps are how I went about setting up PmWiki and BlogIt. Most steps are, I think, suitable for everyone, but there are many possible deviations from this formula.

Step 0: Acquire web hosting

If you're thinking of setting up your own site and don't already have one, then you're going to have to secure web hosting (unless you want to set up your own server -- an issue that you'll have to deal with yourself.

Even if you already have a web host, I really suggest taking a look at NearlyFreeSpeech. Chances are the blog you're planning (much like mine) will see very little traffic over its lifetime, so why pay for more storage and bandwidth than you're ever going to use? With NearlyFreeSpeech, you pay for what you use. To date, I think my hosting at NearlyFreeSpeech has cost me about $5, if that. Cheapness aside, the experience has also been the best I've had so far; the web interface is very nicely set up and non-bloated, and you have FTP and SSH access to your sites.

Step 1: Install PmWiki

The details for installation can be found here.

Don't neglect to go through the list of initial setup tasks after installation.

Step 2: Install BlogIt

The details for installation can be found here. The process involves first installing PmForm then BlogIt, but the instructions are very straightforward.

Step 3 (Optional): Install a skin

This step is not necessary, as BlogIt will work with a stock PmWiki installation, but to make full use of BlogIt's features, install a skin designed for it. Those skins are:

These skins are all maintained by David Gilbert, creator of BlogIt.

Step 4: Get RSS feeds working

I spend most of my time online in my RSS reader, and expect that the poor fools reading my site do the same, so having a nice RSS feed is very important. In particular, I have a distaste for RSS feeds that show only snippets of the article and force you to click to read more.

By default, PmWiki keeps a record of all recent changes in Site.AllRecentChanges. When you create or update a page, that change (along with the change summary) is recorded there, as well as in the RecentChanges page for the corresponding group. I.e., if you create a page at Blog.MyDay, it will be recorded at Blog.RecentChanges. These RecentChanges pages can be made into RSS feeds automatically by appending ?action=rss to the end of the URL for a RecentChanges page (e.g. Projects.RecentChanges and Projects.RecentChanges?action=rss).

The following steps will show you how to set a separate page to list all major changes (e.g. new blog posts) and how to have the corresponding RSS feed show the full post.

1. Set an appropriate $RecentChangesFmt

The default format for recent changes gives a bit too much detail, in my opinion. Add the following code to your local/config.php to use my recent changes fromat (see Site.AllRecentChanges):

$RecentChangesFmt['Site.MajorChanges'] = '* [[$Group.$Name|$Group / $Titlespaced]] (:comment ' . microtime() . ' :)  $CurrentTime';

Of particular importance is that there are two spaces before $CurrentTime; without those two spaces, the changes will not be ordered correctly.

After this step, you should be able to get a list of all changes by navigating to your_url/Site/MajorChanges. Note that only new changes will be shown, so if you want old posts to show up, you'll have to edit and re-save them.

2. Filter Site.MajorChanges

Most likely, you don't want every change made to your site to show up in your RSS feed. Here are some pieces of code that you can add to local/config.php to filter the pages that get added to Site.MajorChanges, and therefore your RSS feed.

Don't show minor changes:

if (@$_POST['diffclass'] == 'minor') {
  unset($RecentChangesFmt['Site.MajorChanges']);
}

Don't show BlogIt comments or drafts:

if (@$_POST['target']=='blogit-comments' || $_POST['ptv_entrystatus']=='draft') {
  unset ($RecentChangesFmt['Site.MajorChanges']);
}

Only show changes to a certain group (whitelist):

list($Group, $Name) = explode('.', ResolvePageName($pagename));
if ($Group != 'Blog') {
  unset($RecentChangesFmt['Site.MajorChanges']);
}

Don't show changes to a certain group (blacklist):

list($Group, $Name) = explode('.', ResolvePageName($pagename));
if ($Group == 'Site' || $Group == 'SiteAdmin') {
  unset($RecentChangesFmt['Site.MajorChanges']);
}

3. Set up $FeedFmt

Copy the following to local/config.php to have your feed show full posts.

if ($action == 'rss') {
  # Set the feed options
  $FeedFmt['rss']['feed']['title'] = $WikiTitle;
  $FeedFmt['rss']['feed']['description'] = $WikiTag;
  # Set each item's options
  $FeedFmt['rss']['item']['author'] = '$LastModifiedBy';
  $FeedFmt['rss']['item']['link'] = '{$PageUrl}?when=$ItemISOTime';
  $FeedFmt['rss']['item']['title'] = '{$Group} / {$Title}';
  $FeedFmt['rss']['item']['description'] = 'FeedText';

  function FeedText($pagename, &$page, $tag) {
    $p = "(:include $pagename#blogit_entrybody :)";
    $content = MarkupToHTML($pagename, $p);
    return "<$tag><![CDATA[$content]]></$tag>";
  }

  include_once("scripts/feeds.php");
}

Note that some feed readers will not show updated posts unless you include the ?when=$ItemISOTime part in the link (however, if that's desired, then you can remove it).

4. Set up feed discovery

If you're using Firefox (or other browsers now, I think) you can tell if a website has an RSS feed by the trademark orange-wavey-icon that shows up in the address bar to the left of the favorites-star. The following code, added to local/config.php, will let browsers know where your RSS feed is so that it can show the RSS feed icon.

$HTMLHeaderFmt['feedlinks'] = '<link rel="alternate" type="application/rss+xml"
 title="$WikiTitle" href="$ScriptUrl?n=Site.MajorChanges&amp;action=rss" />'
;

After these four steps, you should have a chronological list of changed pages at Site.MajorChanges, and an RSS feed with full posts at Site.MajorChanges?action=rss.

Note that if you don't care about the default AllRecentChanges page, you can overwrite it with what you want to show up in your RSS feed. Below is the RSS feed section of my local/config.php, copied verbatim, to show how this is done.

# Set the cool recent changes format
$RecentChangesFmt = array(
  '$SiteGroup.AllRecentChanges' => '* [[$Group.$Name|$Group / $Titlespaced]] (:comment ' . microtime() . ' :)  $CurrentTime',
  '$Group.RecentChanges' => '* [[$Group.$Name|$Titlespaced]] (:comment ' . microtime()
  . ' :)  $CurrentTime');

# Don't list minor changes
if (@$_POST['diffclass'] == 'minor') {
  unset($RecentChangesFmt['$SiteGroup.AllRecentChanges']);
  unset($RecentChangesFmt['$Group.RecentChanges']);
}

# Don't list comments or drafts
if (@$_POST['target']=='blogit-comments' || $_POST['ptv_entrystatus']=='draft') {
  unset($RecentChangesFmt['$SiteGroup.AllRecentChanges']);
}

# Don't list changes to a bunch of groups
list($Group, $Name) = explode('.', ResolvePageName($pagename));
if ($Group == 'Site' || $Group == 'SiteAdmin' || $Group == 'EditTemplates'
    || $Group == 'Main' || $Group == 'Private' || $Group == 'Tags') {
  unset ($RecentChangesFmt['$SiteGroup.AllRecentChanges']);
}

# Feed discovery for browsers
$HTMLHeaderFmt['feedlinks'] = '<link rel="alternate" type="application/rss+xml"
  title="$WikiTitle" href="$ScriptUrl?n=Site.AllRecentChanges&amp;action=rss" />'
;

if ($action == 'rss') {
  # Set the feed options
  $FeedFmt['rss']['feed']['title'] = $WikiTitle;
  $FeedFmt['rss']['feed']['description'] = $WikiTag;
  # Set each item's options
  $FeedFmt['rss']['item']['author'] = '$LastModifiedBy';
  $FeedFmt['rss']['item']['link'] = '{$PageUrl}?when=$ItemISOTime';
  $FeedFmt['rss']['item']['title'] = '{$Group} / {$Title}';
  $FeedFmt['rss']['item']['description'] = 'FeedText';

  function FeedText($pagename, &$page, $tag) {
    $p = "(:include $pagename#blogit_entrybody :)";
    $content = MarkupToHTML($pagename, $p);
    return "<$tag><![CDATA[$content]]></$tag>";
  }

  include_once("scripts/feeds.php");
}

Tips & Tricks

  • Manually edit your Site.MajorChanges for full RSS feed control

Site.MajorChanges (or whatever page your use to generate your RSS feed), unlike other pages that use PageLists to dynamically generate lists, is updated whenever a page is changed. The actual content of the page is static, so you can manually change it as you see fit.

For example, if you have a page that you don't want to show up in your RSS feed, or in your list of recent changes, you can simply edit the page and delete it. If you edited a page twice, you can delete it in the recent changes page to ensure that it only shows up in your RSS feed once.


If there's anything unclear in this tutorial, please leave a comment at this blog post.

Thanks once again to Patrick Michaud, creator of PmWiki, and David Gilbert, creator of BlogIt, for their amazing work and help.

Update: Also a big thanks to commenter Shawn, who let me know about a better way to do the FeedText function, which results in only outputting the entry body. Thanks Shawn!