Templates & Layout Customization

With the addition of BlogBaker to the collection of amazing Sparklit products, came a few neat changes to how content is displayed.

Traditionally, a website URL contains a number of parameters, which are evaluated by the webserver for interpretation. The inputs are sent to a black-box (from a user perspective) and out comes a dynamically generated website.

BlogBaker is the first of our products to take the black-box portion of this process, and open it up to give the users significantly more fine tune control over how their pages are displayed. Some of the other hosted blogging software on the market also allowed you to do this, but not nearly as well as we do.

Templates in BlogBaker

We created a pseudo-HTML template parsing back end, which accepts HTML templates that contain non-standard HTML formatted tags and turn them into the pages of your blog.

There are a number of custom tags, including the following:

  • bif, belseif, belse -- conditional logic
  • battr -- accessing information from objects
  • bdata -- accessing stored data from the application
  • bcontainer, bsection -- container elements for rendering
    • bcontainers require that all the tags inside of them are either bsections or bwidgets
    • bsections allow any custom tags, or HTML to be added
  • bwidget -- configuration for a widget

At the moment, we're in the process of writing documentation to support the templating system, so that users can easily look up what these tags do, and read up on some practical examples. Here's an example of a tag in use:

<bwidget id="category0" type="category" title="Categories" sorting="alphabetical" flat="false" layout="dropdown" counts="true"></bwidget>

The category widget has several attributes which you can use to configure how it is displayed. Note that the "type" and "id" attributes are required, and that the "id" attribute must be unique, and the "type" attribute must be one of the allowed widget types.

  • title -- the title you would like displayed (optional)
  • sorting -- the order you would like have your categories displayed in, possible options are:
    • alphabetical, reversealpha (reverse alphabetical), frequency (display based on popular usage)
  • layout -- the type of category list you want to show, possible options are:
    • dropdown (a dropdown select box), hierarchy (shows up in tree format)
  • flat -- set this to "true" if you want to flatten the displayed list of categories
  • counts -- set this to true if you would like to see the number of times each category has been assigned

Note that the "type" and "id" attributes are required, and that the "id" attribute must be unique, and the "type" attribute must be one of the allowed widget types. (I won't list all the possibilities here though.)

Templates in ActiveBoard

A project that I have been working on for the last week or two is to implement the templating system I created for BlogBaker in ActiveBoard. A forum is actually quite similar to a blog in the general scheme of things, and both products benefit greatly from the additional customization that templating provides. It also means that when a client comes to me saying "Can I make my ActiveBoard do this?" I will be able to reply jovially "SIR, YES SIR!" ...or something like that anyway.

In the coming weeks I am looking to launch this for practical testing and usage online. Several of our more prominent boards, such as MediaZoneJA, are looking forward to this, and have kindly offered to help test it out.

Here's a quick example of a template in ActiveBoard:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<ul>
<loop var="categories" item="category" index="i">
    <li>
        <div><attr name="title" item="category"></attr></div>
        <if var="nsubforums" compare="greater" value="0">
            <ul>
            <loop var="subforums" item="subforum" index="j">
                <li>
                    <attr name="title" item="subforum"></attr>
                </li>
            </loop>
            </ul>
        </if>
        <else>
            <li>
                No forums have been created in this category yet.
            </li>
        </else>
        </ul>
    </li>
</loop>
</ul>

A "nice" implementation...

I also wanted to briefly discuss a bit of technical fun I've had during the creation of the template parser, and subsequent updates and improvements. PERFORMANCE! It's important, very important. So much so, that I've already gone through 5 iterations of the template parser.

The first method I came up with, was integration with phpQuery, a popular, jQuery modeled DOM traversal and manipulation project. phpQuery is easy to use, relatively bug free (though I found a couple during my time working with it), and uses PHP's built in DOM functionality.

phpQuery comes with a price though, in that it has a very high usage overhead. Importing DOM text strings has a hefty overhead (partially PHP DOM's fault), which means that if you have to create multiple documents your minimum execution time is going to be pretty high. Here's an example of what I mean.

Imagine you have a some simple template HTML like this...

<loop var="numbers" item="number" index="i">
<div>at index <attr name="i"></attr></div>
<loop>

...and there are "numbers" is a list of 10 numbers, from 0 to 9 inclusive. To evalulate this in phpQuery, once the <loop> is detected, you have to repeat the evaluation of the contents of the loop 10 times. Unfortunately, to do this, you cannot simply replace the <attr> tag 10 times, because after the first replacement you have actually removed it from the template.

Ultimately there are a number of possibilities, but I'll outline only two: 1) duplicating the contents of the <loop> 9 more times, and 2) copy the contents of the loop, save it and evaluate it separately 10 times.

1) This requires DOM manipulation (Ugh DOM manipulations.) 9 times, and then an additional 10 replacements as you traverse and update the newly created nodes.

2) This requires fewer DOM manipulations, but requires you to create 10 additional DOM documents (Ugh, overhead!), one for each iteration of the loop.

I'd also like to point out, that when I think of something as a "long time", I mean that it's taking several hundred milliseconds. So compared to a lot of things... it's fast but... on a massive scale, it's not fast enough.

Last but not least, a better implementation

It turns out that you can accomplish the same things that phpQuery can do with its object oriented DOM document approach, by doing a little string manipulation and a state-based system.

A very simple regular expression can split up an entire template on the opening and closing tags of special elements, and give you a flat array of all the important parts of a template broken into three categories: opening tag, closing tag, raw HTML.

After that you can traverse the array asking yourself simple questions: am I in a condition? am I in a loop? am I raw HTML?

At present, the template parser algorithm is roughly 18% slower than dynamically rendering a page with logic PHP, and by the end of next week I hope to have better than 5% slower parsing.

GeSHi and line height fun

Being a developer, one of the major features I (and my colleagues) wanted to see in BlogBaker was syntax highlighting for code blocks. In the future, many posts will include snippets of code used to help describe problems that we are working on and how we are working to accomplish them. Additionally, many of us will be creating personal blogs outside of work, and will want to be able to insert code snippets.

It's fairly straight forward to turn some code into a snippet, and is entirely based on custom <pre> tags.

For example, if I wanted to paste the contents of a simple Hello World PHP script, I would edit the HTML view of my post, and enter <pre lang="php">, paste my PHP script, and close </pre>. See below example:

1
2
3
4
5
6
7
8
9
10
class foobar
{
    public function greetings()
    {
        echo "Hello World!";
    }
}
 
$foo = new foober;
$foo->greetings(); // prints "Hello World!"

Tada!

During the addition of this feature, however, I ran into an unexpected hiccup regarding line heights. I had seen GeSHi used before on a friends blog, and noticed that the height of the line numbers did not match up with the height of the code. See below screenshot:

Line Height Example 1

Very odd... and to top it off, I noticed the same issue on my development blog and during testing. At first, I thought that I had conflicting CSS changing the line-heights, height of the parent element, or something of that nature. Other alternatively possibilities included that the error might be a browser rendering bug.

A breakthrough came when I had one of my coworkers (Brendan) test out what I was working on in his browsers. Brendan noted that it was correct in one of his browsers, but out of alignment in another browser. Further investigation revealed that no explicit monospace font family style was being applied to the

<pre&gtl tags surrounding the code, and as a result the browser used whatever default had been selected. In most browsers this appears to be Courier New, which appears render differently in different situations.

Before concluding anything about Courier New itself, I checked and doublechecked that there were no styles affecting elements inside the columns which might change their height (ie. 1px padding or margin on elements), but I was unable to find anything. To matters more interesting, the columns were not even out of alignment by a uniform amount. For example, 150 lines of code were maybe 19 px off by the end -- VERY odd, and suggests that there are likely not any errant styles.

After that, I tried changing the font family of the monospace tags, forcing it to Consolas and Courier in CSS. After that, all my tests started showing up in perfect alignment across all browsers.

Line Height Example 2

So, a word to those using GeSHi: if you run into line-height issues, check what font family is being used to render the preformatted tags!

Theme: Fresh Print by BlogBaker. Powered by BlogBaker.