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.
0 Comments: