I've noticed what I think are becoming clearer segregators between the Drupal, Habari, and WordPress platforms; some details that differentiate the products more in my mind than they had before.

Drupal is constructed to let users build sites entirely from its UI. Much effort is made to make it unnecessary even to write any HTML code, yet still allow users to configure the type and quantity of data fields assigned to their content. Users can set options to determine the layout and formatting of output.

As a result of Drupal's user-configurability though, any little things that fall outside of what the system can do for you are often crazily more complex to apply than other systems. For example, if you want to apply any special formatting to a field of data that is entered on a node, you must write theme functions or alter a template's output of that field. This is often a more complex task than what would be required to output the same data in other systems, which is explicitly what Drupal is trying to avoid in the first place.

WordPress is constructed to have a fairly fixed set of data, in comparison to Drupal's field flexibility. By doing so, it can confine the user to a very strict set of data to output via a finite set of specific PHP functions - aka "Template Tags". This is a benefit to people producing WordPress themes because it gives them the flexibility to design HTML and CSS code, and then insert the functions for data output into predefined spaces in their templates.

WordPress is different from Drupal in that it assumes you will want to edit the template code to change the output, rather than configure the organization and display of data from inside the software. As a result, WordPress' code must stay simple to appeal to users who wouldn't otherwise grasp database design, and so would likely never have the level of data configurability that Drupal offers.

Habari is constructed to have a reasonably fixed set of data, more like WordPress than Drupal. A key difference though, is that Habari doesn't confine itself to simple tags for the benefit of non-coders. By leveraging an assumption of some coding ability, or at worst, the ability to cut and paste existing working code snippets that produce effective output, more dynamic data can be extruded and manipulated from the initial basic offerings.

Unlike Drupal, Habari is not designed to allow users to change the layout or presentation of the data on the page. Like WordPress, Habari assumes that a theme designer will want more control over the layout of output than could be provided by a generic UI. Still, in many cases, Habari requires that theme developers have a more developed coding skill to make use of the tools that it provides.

There's been a good amount of action in Habari commits lately concerning permissions. The system is finally, after much too long a delay, working well enough that it's inevitable that 0.6 will appear shortly. The permission system in Habari is steeped in a great amount of unwarranted mystery, and is pretty straightforward if you clear your mind of preconceptions and start over with some basic concepts.

First, you should be familiar with the vocabulary of permissions. Habari has the obvious user which represents a user who has successfully logged in using a username and password. There is also an anonymous user that is used to represent any user that has not logged in. Users can be members of one or more groups. Typically, permission to resources is extended by Habari to groups, which make them easier to maintain, but Habari also supports assigning permissions to users. These concepts are pretty easy, but some others might sound new.

Habari uses a thing called a token in regard to permissions. Groups and users can be given different combinations of access to any token. Access is a combination of the standard CRUD permission set: create, read, update, and delete. (Actually, in Habari currently these accesses are named create, read, edit, and delete.) To put these terms together in a sentence, a group (or user) can be assigned read and update permissions to a token. Habari also allows you to assign deny access to a token. Deny access overrides any granted CRUD permissions to a token you might have.

What are these tokens good for? Good question.

One or more tokens can be assigned to any post or action. So if you have a post that is assigned the token "artichoke", and your user (or a group your user is a member of) has "read" access to the "artichoke" token, then you are able to read that post. Likewise, if you have "edit" permission on the "artichoke" token, you may edit that post.

There are some tokens that are applied to posts in general. For example, the "post_entry" token is implicit on any post of type "entry". If your user has "read" access to "post_entry", then you can read entry-type posts. In fact, the Anonymous group, of which the anonymous user is a member, has this permission, which is why anonymous visitors to Habari sites can see the posts. If this permission is revoked, then anonymous users see nothing, and you must be logged in as a user with appropriate permissions to read any posts.

Token can also be applied to actions within the system. So a token "manage_comments" might control your user's ability to manage comments. The CRUD access rights don't apply to these tokens. Any access right on the "manage_comments" token would allow you to manage comments. Only if you have no access or have been explicitly denied access to the "manage_comments" token would you be unable to manage posts. These types of tokens are called "binary tokens" versus the "CRUD tokens" that apply to posts. Some upcoming schema changes in the Habari database will delineate these more clearly for the purpose of displaying them more effectively in the admin.

The beauty of the system is that permissions are queried with every request for posts by default, and no additional permissions checking or filtering is required. A single query returns the posts that are available for a specified user, allowing the system to operate both efficiently and effectively.

This system may seem more complex than what one would expect, and under the hood it's not trivial, but it provides an unprecedented level of permission configuration that is managed by the core software, and an API that is easy for plugins to manipulate. With this permission system it is possible to create simple private post functionality, a multi-tier subscription model, and even custom permissions to existing or brand new features within the admin.

For people that run single-user blogs, the permission system stays nicely tucked out of the way until such time as the site needs it. Guest posting anyone?

Today, I wrote three new Habari plugins. They're all fairly simple, but they each use some techniques that plugin developers might like to know. So I'm going to take a few posts here in a series to outline how I go through the construction of these plugins.

The first plugin I'll start with is one that will be of particular use here on RedAlt, a code highlighter.

With this plugin, I'd simply like to take a chunk of code that's output inside of "code" tags and print it nicely formatted and colorized so that it's easy to read. With the prettify project on Google Code, neatly licensed under the ASL just like Habari and eventually my plugin, I should be able to accomplish this fairly easily.

Basic Framework

So, let's start out with the basic plugin framework:

'Prettify', 'url' => 'http://habariproject.org/', 'author' => 'Owen Winkler', 'authorurl' => 'http://asymptomatic.net/', 'version' => '1.0', 'description' => 'Displays pretty source code in the site output', 'license' => 'Apache License 2.0', ); } ?>

The above code essentially tells Habari all about this plugin. Without this function, the plugin won't run. I create a directory in user/plugins named "prettify" and save this file as "prettify.plugin.php" for a starting point. The ".plugin." part of the file is what keys Habari that this file is a plugin and is a candidate for activation.

In future releases of Habari, the primary source of the information provided by the info() method will be instead via an xml file that is packaged with the plugin.

Add Dependent Files

Next, we need the plugin to actually do something. I know I'm going to use the CSS and JavaScript from the Prettify project on Google Code, so I know I need to at least add those files to the page output.

How do I add those files to the output? I use another plugin hook:

public function action_template_header() { Stack::add('template_stylesheet', array($this->get_url(true) . 'prettify.css', 'screen')); Stack::add('template_header_javascript', $this->get_url(true) . 'prettify.js', 'prettify', 'jquery'); }

This chunk of code does two very basic things, but may seem a little complicated.

First, the function action_template_header() connects the plugin to the "template_header" hook. This hook executes in the theme when the template executes $theme->header();

The two lines in the function add things to a Habari Stack. A Stack is just a term for a list of things that you are usually going to output. Stacks have a couple of nice features that I'm using in addition to naming the Stack itself. They let you name the item that you're inserting as the third parameter, and they let you name dependencies of that thing in the fourth parameter.

In the case of the css file, I'm just adding the file to the stack and don't care about the name or the order. In the case of the Prettify javascript library file, I'm naming the stack item "prettify" and I'm making sure that it is loaded after any item in the stack named "jquery", which we'll be using later.

When the rest of the theme class executes, the stacks will be appropriately output according to the formatting specified in the theme class.

One handy thing here that's easy to overlook is the $this->get_url() method. In this case, $this is an instance of the plugin, and so the get_url() method is in the base Plugin class. The function returns the URL of the plugin directory, which is really useful to know if you need to link to a file that is distributed with the plugin.

With just this code, the plugin outputs the required javascript and css to support the Prettify Google project.

Implement Highlighting

With the dependent code being output properly, I need to add code to make use of it. I'll have to change the action_template_header() function slightly to make the requisite calls to the prettify javascript library.

I want to make sure that I don't call the prettify code before the whole page loads. I can do this easiest with jquery, which is often used for other features in the theme. I'll add that to the dependencies in the javascript stack, and add the code to make the prettify library colorize my code:

public function action_template_header() { Stack::add('template_stylesheet', array($this->get_url(true) . 'prettify.css', 'screen')); Stack::add('template_header_javascript', Site::get_url('scripts', true) . 'jquery.js', 'jquery'); Stack::add('template_header_javascript', $this->get_url(true) . 'prettify.js', 'prettify', 'jquery'); Stack::add('template_header_javascript', '$(function(){ PR_TAB_WIDTH = 2;prettyPrint()});', 'prettify_inline', array('jquery', 'prettify')); }

My new code chunk is "prettify_inline", which is dependent on both jquery and our previously added "prettify" stack item. Note that the value added to the stack is raw javascript, not a URL to a javascript file like the other values. Habari sorts this out automatically and uses the appropriate tag formatting on output.

The inline code here sets the tab width to 2 (the default is 8) and executes the prettyPrint() command, which finds all of the code blocks using the class "prettyprint" and colorizes them. This is done inside of a jQuery function that executes after the DOM for the page loads.

Tricky Line Numbers and Entities

This might be good as-is, but I would really like to add line-numbers to the output. There are a number of ways to do line numbers. Some people use the ordered list HTML tag, some people use tables. There are drawbacks to both of those. The method I'm currently preferring is using a floated list of line numbers that sits to the left of the lines.

So what I want to do is create a second code element before the code that is being highlighted, and then put the same number of numbers in it as lines in the highlighted code block. I'd prefer not to do this in the raw HTML output.

To do this, I'm going to filter the content of the post, and wherever there is a line break, I'm going to replace that with a br tag with a specific class name. This will let me insert real HTML breaks and count the exact number of breaks in the code with javascript so I can insert line numbers.

Also, when I add content to a post that I intend to syntax-highlight, I want Habari to automatically turn any HTML code inside that block into entities. This will make it easier to paste code that contains HTML tags, since I won't have to convert them into entities myself.

To do this, I will add to the filter some code that turns any code within the prettyprint code tags into its HTML entity counterpart.

Here's the code that does it:

public function filter_post_content_out($content) { $content = preg_replace_callback('%(<\s*code [^="">]*class\s*=\s*(["\'])[^\'"]*prettyprint[^\'"]*\2[^>]*>)(.*?)()%si', array($this, 'content_callback'), $content); return $content; } public function content_callback($matches) { $code = trim($matches[3], "\r\n"); $code = str_replace( "\r\n", "\n", $code); $code = htmlentities($code); $code = str_replace( "\n", "
", $code); return $matches[1] . $code . $matches[4]; }

The filter_post_content_out() method attaches to a plugin hook that executes when the post content is output, typically via echo $post->content_out; using the content_callback() method as the replacement function for preg_replace_callback().

The only really tricky bit here is the regular expression that captures the code blocks, and only if they have the "prettyprint" class assigned to them.

I also need to add the javascript code to count the breaks and output the line numbers, which results in this final version of action_template_header():

public function action_template_header() { Stack::add('template_stylesheet', array($this->get_url(true) . 'prettify.css', 'screen')); Stack::add('template_header_javascript', Site::get_url('scripts', true) . 'jquery.js', 'jquery'); Stack::add('template_header_javascript', $this->get_url(true) . 'prettify.js', 'prettify', 'jquery'); Stack::add('template_header_javascript', '$(function(){ $("code.prettyprint").each(function(){ l=$("br.pretty", this).length+1;oz="";for(z=1;z<=l;z++) oz+="z+" "; $(this).wrap("

").before(""+oz+""); }); PR_TAB_WIDTH = 2;prettyPrint()});', 'prettify_inline', array('jquery', 'prettify')); }

The Final Plugin

So all of that gets basically what I want. I made a few tweaks to the prettify CSS so that it looks nice within the site. It's the CSS that does the expanding of the code on hover in Firefox. Apart from that the two extra files come straight from the Prettify Project of Google, and the last file, the prettify.plugin.php is this one here:

'Prettify', 'url' => 'http://habariproject.org/', 'author' => 'Owen Winkler', 'authorurl' => 'http://asymptomatic.net/', 'version' => '1.0', 'description' => 'Displays pretty source code in the site output', 'license' => 'Apache License 2.0', ); } public function filter_post_content_out($content) { $content = preg_replace_callback('%(<\s*code [^="">]*class\s*=\s*(["\'])[^\'"]*prettyprint[^\'"]*\2[^>]*>)(.*?)()%si', array($this, 'content_callback'), $content); return $content; } public function content_callback($matches) { $code = trim($matches[3]); $code = str_replace( "\r\n", "\n", $code); $code = htmlentities($code); $code = str_replace( "\n", "
", $code); return $matches[1] . $code . $matches[4]; } public function action_template_header() { Stack::add('template_stylesheet', array($this->get_url(true) . 'prettify.css', 'screen')); Stack::add('template_header_javascript', Site::get_url('scripts', true) . 'jquery.js', 'jquery'); Stack::add('template_header_javascript', $this->get_url(true) . 'prettify.js', 'prettify', 'jquery'); Stack::add('template_header_javascript', '$(function(){ $("code.prettyprint").each(function(){ l=$("br.pretty", this).length+1;oz="";for(z=1;z<=l;z++) oz+="z+" "; $(this).wrap("

").before(""+oz+""); }); PR_TAB_WIDTH = 2;prettyPrint()});', 'prettify_inline', array('jquery', 'prettify')); } } ?>

Future Improvements

I'd potentially like to add a couple of things to this plugin, both relating to downloads. I'd like to be able to link a code block to a download, so if you click a link near the code, it would download that code.

It might also be useful to have a button or link that automatically copies a code block to the clipboard. For right now, that would include some Flash (because you can't access the clipboard from javascript), and I'm not really too interested in that right now.

Having the code output expand and contract or enable vertical scrolling on user interaction would probably be a useful addition.

It might also be nice to have some settings that allow the code to link to cross-references. For example, it would be useful if PHP functions and keywords linked to their documentation on the php.net site. It would be even more nice if the code could link to a documentation center of my choosing, although I don't know of a documentation standard or API that would make that easy enough to enable as a web service. Still, it might be worth thinking about.