PHP CMSes done right: how to enable clients to edit content appropriately

In the previous post, I talked about how CMSes harm websites. I debunked the oft used selling points of faster, cheaper and client empowerment over websites and explained how CMSes butcher semantic markup, code decoupling, tasteful style, speed optimisations, maintenance ease and code freedom.

Now I want to mention a few ways how a CMS can be appropriately used to build a website. There are two scenarios I want to cover: using pre-built scripts and prioritising custom code first.

Pre-built scripts

By pre-built, I mean all you really want is an off-the-shelf setup and don’t care for customisations. So grab an out-of-the-box CMS (Joomla, Drupal, WordPress, etc), install an associated theme and several modules from the CMS’s ecosystem and glue them together. With this sort of set-up, you could have yourself a complex website system such as an e-commerce or blog site running within a day, looking good, and costing zilch if you have the know-how.

In this scenario, a CMS should be your top choice. The benefit of speed and set-up far outweighs the extremely costly alternative of custom coding such a complex system. It is for this reason that thinkMoult runs on WordPress: I just wanted a blog to run on the side with minimal fuss.

As the complexity of the system grows this benefit also grows. It would be rare to recommend to the average client to build a blog from scratch, an ecommerce system, a forum, or even ticketing system.

However once you plan on doing lots of customisations, you’re stuck.

Did that really solve anything?

Not yet, we’ve simply outlined a scenario where the cost benefit far outweighs the effort required to invest in a custom product. Unfortunately, all the issues still exist.

So how do we build a CMS for products which don’t fit those requirements – either small tailored “static poster” where first impressions are key or customised systems?

Sceptics might question why building a CMS now is any different from the CMS giants of the past. My answer is that the PHP ecosystem is maturing and the industry is standardising (see PSR-0, Composer, and latest PHP changelogs). Previously we relied mainly on CMSes as they defined a set of conventions we could live with, but now we have proper ones industry wide.

Place custom code first!

VTemplate CMS

The answer is simple. The CMS should not govern your code! Markup style generating, logic modifying systems should be at least completely decoupled if not thrown away completely. The trick to do this quickly is to isolate exactly what a CMS needs to do: and that is to allow the client to edit content.

That’s right: edit content. Not glue modules, not define routing, not to restyle the page, and never, ever, to touch anything to do with logic.

If they ever need anything more complex than editing content, make a module for it. Make that custom module on top of your existing code, and link it to a config repository – nothing else. All it should do is flick switches, not act as a heavyweight crane.

Now, for editing content – I have five strategies to fix the “butchering” aspect of CMSes:

  1. Start by ensuring your frontend code is completely decoupled from all logic. I like to do this by using Mustache as a templating language. It’s simple by design. If your frontend developers can’t break the site’s logic, your client can’t either.

  2. Write your markup and styles perfectly. Writing perfect markup and styles means your editor won’t have to care about whether that extra <div id=”does_things” class=”active wrapper_l12″> was actually vital to the page operating properly. Everything is simple and only uses standard markup tags.

  3. Use a semantic editor. A semantic editor preserves the goodness of point 2. I use WYMEditor, which has bee around for a while. Not only does it stick to the basic tags, it reads extra styles from documented CSS. This way you won’t have clients with no taste inventing new styles and layouts, but only using what you’ve provided.

  4. Beautify the code! PHP Tidy is built-in and can produce well indented, cleanly styled code markup. Don’t have faith in automatic beautifiers? With your perfect markup and complete style/markup separation in points 2 and 3, all your beautifier deals with is the most basic of markup – which probably only needs indenting before it’s called classy code (no pun intended)!

  5. Whitelist editable elements, not blacklist. The default state for whether content should be editable should be off. Don’t give them more than they ask. Because otherwise they will touch it, and inevitably break it. This means you’re custom isolating segments of editable content for the client (I move it into a Mustache partial), and testing it before handing the baton to the client. It also means you can monitor it much more easily- such as inserting an update notifier so that you can run git diff and verify they didn’t somehow still bork things over due to Murphy’s Law.

Et voila! Your client now can edit the content, not break the logic, keep it semantic, keep the code beautiful, and only touches what we wants. He also has a series of switches for the more complex areas of the site. You’re also keeping watch via that update notifier I mentioned (with a monthly fee, of course).

Back-end wise, you’ve lost nothing of the modular ecosystem that CMSes also advertise, because now we’re coding to the PSR-0 standard, and can see the various items that people offer.

What did we lose? Upfront development speed. What did we gain? Everything.

Note: the picture of the CMS is from a custom solution I developed for Omni Studios. Underneath it’s powered by Mustache, PHP Tidy, and WYMEditor, and good markup/styles, all mentioned in this post. So by custom, I mean rebranding a series of industry tools.


Content Management Systems harm websites

Yes, you read that right! Customers looking to build a web application are often wooed by the many ‘benefits’ of using a Content Management System. But before we begin: What is a content management system (abbreviated CMS)?

When a web site is built, complicated code is written to allow it to function. Some of this code builds what we see on the surface on each page. For example: the design of the site, the layout, and its content.

Content management systems harm websites

(Oh dear, we’ll explain that screenshot later!)

Web developers have built systems which now allows clients to edit the content themselves and have instantly updated content without having to go through experienced web developers. These systems are called Content Management Systems and supposedly pose these benefits:

  • Site content changes are seen instantly as the client thinks it up
  • Clients feel more ‘in control’ of the site
  • No need to pay web developers to make small and frequent edits

Sounds excellent, right? Cheaper, faster, and you’re in control. Well, unfortunately, it’s not the entire story.

What most clients don’t realise is that editing a website is not like editing a word document. CMSes create a rather similar interface which is easy to use, but causes serious side effects:

  1. The CMS editors don’t know how to cleanly separate content and style. This is the difference between what is being displayed, and how it should look like. This cruft builds up over time, making your page load slower and making it increasingly hard to make changes in the future.
  2. The CMS editors only allow you to change what things look like on the surface. Although you might not notice the difference, search engines are less likely to be able to understand your pages, and this will negatively affect your search engine rankings.
  3. They don’t discipline against bad media practice. These editors will let you upload any type of media without any considerations of how to optimise them for the web. Unoptimised images and videos mean slower website loading, more server loads (and thus server costs), and often ugly looking content.
  4. They add a lot of unnecessary code. This is another invisible side effect which leads to slower page loads and poorer search rankings.
  5. The editors don’t refer to the underlying engine when linking pages. This means that should you want to rename pages for SEO, or move site, your links are almost guaranteed to break.
  6. There is no version control. It becomes much harder to track series of changes to a single page and undo edits when problems occur.
  7. It gives you the illusion that you are an interface designer. Experienced interface designers pay attention to details such as padding, ratios, consistency, and usability that clients simply cannot match. A well designed site will slowly degrade in usability and aesthetics until it has to be redone from scratch.
  8. It lets anybody change anything. It doesn’t stop you if you’re changing a vital system page, butchering crafted copy that has undergone hours of SEO, or even edit the text of something you don’t have authority to. It becomes a human single point of failure.
  9. It exposes you to system internals. If you’re a client, all you really want to do is edit some text on your page. Modifying forms and dealing with modules is out of your league, and likely out of your contract scope. You’ll have to learn how to use a complex system just to change what is often just a simple site.
  10. You’re stuck with it. CMSes are walled gardens. They lock you into the system you’ve chosen and when you want something customised in the future, don’t be surprised when you get billed extra.

With the site almost fully in the client’s hands, clients can unknowingly break the system internals, or worse, install 3rd-party low-grade modules which can compromise the site’s security. With the power to edit now fully in the hands of clients, these system changes do not pass through the developers eyes. Over time, these accumulate and you end up with a broken site.

It isn’t all cheaper – to attempt to prevent some of these effects, developers have to spend extra time to develop custom modules for you to manage sections of the site. These costs, of course, have to be passed to you.

CMSes are also rapidly changing and constantly targeted by hackers. Not only does this mean you’re open to security breaches, the server will likely be under extra load by hackers and bots attempting to crack your site. You’re then pushed into a maintenance race to constantly update modules and your system that quickly gets forgotten: until you’re left with an outdated, unable-to-upgrade system that’s a sitting duck for hackers, even if you’ve never needed to make a single change to your content.

Did you receive training for how to use a CMS to edit your site? Bad news. You’re the only one who knows how, and probably not for long. CMSes change very rapidly – so your training will become outdated. There also isn’t much of a standard when it comes to CMSes, so you’re restricted to development firms who specialise in your CMS should you ever need professional help in the future.

Funnily enough, using a CMS is no picnic for developers, either. All CMSes cause developers to build things not the way they should be built, but the way the CMS forces them to build it. This may save time in the short-term, but often leads to costly maintenance nightmares in the long-term.

Together, using a CMS turns the craftsmanship of your site from the costly investment you poured into experienced developers into a cheap, ineffective website. You’re practically throwing away the money you spent going through detailed design revisions, search engine optimisation, training, website optimisation, responsive design, and even choosing the firm you hired to begin with. And given the accumulative nature of these adverse effects, you can be guaranteed that any changes you need done in the future will become much, much more costly.

These aren’t one-off improbable horror stories. These are things I have witnessed again and again with CMS-run sites. It is practically guaranteed to happen: the only question is when. The industry knows this, too – it’s just that CMSes are good at the short term and the prospect of self-editing content is alluring as a selling point. But it’s time to spend your money properly: get an expert craftsman to manufacture it right the first time, and keep the quality you paid for.

… coming up next: CMSes done right.

Life & much, much more

thinkMoult blog design updated.

It’s come a long way since the original concept redesign back in the July of 2009. The thinkMoult blog has been incrementally updated probably once a month with small tweaks to the layout. The blog has been stripped originally from its (relatively) featureful edition to the bare essentials – ie. a streaming wall of text with emphasis on clear headers and content areas.

To me a blog is a very much a written journal. Social and pictorial blogs aren’t "blogs" in my definition of the word. As a result I’ve decided to condense things a little, cutting out pictures which don’t complement the article, focusing on clear typographical elements (pushing the limits of the beloved Arial font!) and effective use of padding rather than borders. I’m maintaining the simplicity of the previous layout (no sidebar, no link lists or fancy plugins) and sticking to my roots.

The design itself was inspired by the Depo Skinny Theme but with obvious edits here and there on font styles. What this implies is that I’ve also involuntarily drastically improved the semantics behind the blog itself – which is a good thing of course.

Everything should’ve been ported over such as avatar support in comments, asides along with my asides category pagination hack, and the various footer tweaks. There have also been a few edits here and there which add to the overall polish of the design.

Well, I hope you like it, and let me know if there is anything which looks messed up.


Make a category not considered as a post in WordPress

In other words, how do you make posts that are in a certain category not count towards total page post count in WordPress?

A while back I set up Asides on this blog. The problem was that previously I was displaying 5 posts per page. Now with asides it still displayed 5 posts per page, but as asides are probably one sentence long at most I personally don’t consider them to be blog posts. This meant that it didn’t display 5 “real” posts per page. So, how do I fix this?

Disclaimer: I’m not experienced in the under-the-hood of WordPress and as a result some of this solution might be hackery. However it works for me, and that’s what counts.

Problem 1: displaying 5 real posts per page regardless of how many asides there are.

WordPress loops through a series of posts per page and displays them one by one. Initially I thought they would increment a counter, of which I could easily change so that if the post category was in “asides”, it will not increment the counter. However there were a couple flaws: 1) There was no counter, or I completely missed one, 2) The database queries sets the LIMITs from the administrator settings right at the very beginning, and 3) pagination will be completely messed up.

The solution was pretty simple, firstly we set the database query LIMIT to an obscenely large amount – more posts than we ever think we’ll need on a page. This can be done in the administrator panel. Change “display posts per page” to a random large value. I chose 15 because it seems pretty realistic that real posts + aside posts < 15 for 99.9% of the time.

The second step is to manually change the criteria for when the loop terminates. This way it will not actually show 15 posts, but instead up to 15 posts. What we’ll do is create a new counter, where ever time we display a post that isn’t an “aside”, we increment the counter, until it hits 5 posts (if I wanted 5 real posts per page) – at which time it’ll terminate the loop.

This can be done in the wp-includes/query.php page. To begin with we’ll need a new variable in the class for our counter. So below class WP_Query { we should add:

var $counting_up = 0;

Just to make sure that $counting_up resets itself as it should, we’ll add this to the init() function:

$this->counting_up = 0;

Now the next step is to modify the the_post() function. When the loop has started and the category is not an aside, we’ll increment our counter. In this example my aside category ID is category 429. This will be different for you, so you change it. So simply add this to the the_post() function:

if ( !in_category(429) && $this->current_post != -1 ) {

Now we’ve got our counter, we’ll set up the loop to terminate correctly. This can be done in the have_posts() function. Notice this is the have_posts() function inside the WP_Query class, not outside. We can modify our if statement to terminate when our counter hits 4 posts (as the first isn’t counted – therefore in effect we’ll display 5 real posts), and also when we don’t have a do_not_terminate variable set to the WP_Query. Why this do_not_terminate variable is important is if we ever need to override this, as well as later I’ll explain when we look at pagination issues. Here is my completed modified if statement:

if ($this->counting_up == 4 && !$this->query_vars['do_not_terminate']) {
$this->in_the_loop = false;
do_action_ref_array('loop_end', array(&$this));
return false;
} elseif ($this->current_post + 1 < $this->post_count) {
return true;
} elseif ($this->current_post + 1 == $this->post_count && $this->post_count > 0) {
do_action_ref_array('loop_end', array(&$this));

Now we’ve solved problem 1, and 5 real posts are displaying on our front page, let’s move on to problem 2.

Problem 2: previous page, or going to older posts will no longer work.

Since pages are pretty obsolete at this point, we’ll switch to using offsets. This is because each page will no longer display a fixed number of posts, each may display a variable amount of posts, minimum being 5 (that we set just now), and maximum being 15 (that we set at the very beginning). So to start we’ll hop over to our index.php in our theme file, and simply get the offset from the URL and pass it through to our post loop. Here goes:

<?php if ($_GET['offset'] && is_numeric($_GET['offset'])) {
query_posts('offset='. $_GET['offset']); $offsetting = $_GET['offset'];
} else { $offsetting = 0; } ?>

So with that code in index.php, we can now visit and offset our posts by 20. To determine how many posts to offset by in previous pages, we simply take how much we’re currently offset by, and add all the posts we’ve displayed on the page, regardless of whether or not it is an aside or a real post. To do this we need another counter. So we’ll initialise our counter, perhaps near the beginning of index.php:

<?php $on_page = 0; ?>

… then within our while (have_posts()) { loop, (or whatever equivalent loop your theme uses), we’ll just increment it:

<?php $on_page++; ?>

So then we recode our “previous posts” link to go to:

<a href="<?php echo $offsetting + $on_page; ?>" >Previous posts</a>

That was simple, eh? This brings us to problem 3.

Problem 3: newer posts don’t work, for obvious reasons.

Going forward in time is a little bit more complex. We want to calculate how much less we should offset by. To do this we’ll create a function to calculate this. The function will need to know how much we’re currently offset by. Based on that, it’ll query 15 posts into the future, then loop through those posts in reverse order. If it can’t go 15 posts into the future (eg: on the first page, and perhaps the second), it’ll go as far into the future as it can. When looping through, it’ll record the category of each of the posts. Whenever it hits a post, it’ll increment the count we want to offset less by. When we hit a post that category isn’t an aside (category 429 in my example), it’ll increment a counter that determines how many real posts we’ve hit so far. So therefore we have two counters. When the real counter hits 6 posts, it’ll terminate the offset counter. This is because I want 5 real posts per page, and based on how we coded problem 1, we know that the last post of any page must be a real post, not an aside.

We can place this function in the functions.php file of our theme. Here is the function, of which lazy people can copy and paste:

function back_to_the_future($offset = 0) {
$new_offset = $offset-15;
if ($new_offset < 0) {
$new_offset = 0;
$diff_offset = $offset - $new_offset;
$future_query = new WP_Query(array(
'showposts' => $diff_offset,
'order' => 'DESC',
'offset' => $new_offset,
'do_not_terminate' => TRUE

$post_data = array();

while ($future_query->have_posts()) {
if ($diff_offset == 0) {
} else {
$cat_id = get_the_category();
$cat_id = $cat_id[0]->cat_ID;
$post_data[] = $cat_id;

$post_data = array_reverse($post_data);
$count_posts = 0;
$count_total = 0;

foreach ($post_data as $post_cat) {
if ($post_cat != 429) {
if ($count_posts == 6) {
} else {

return $offset - $count_total;

People who looked through the code will realise that we passed the do_not_terminate variable to WP_Query that we set up when addressing Problem 1. This is required because if we didn’t, we won’t get 15 posts into the future, instead we’ll just get however many posts starting from 15 posts into the future that include 5 real posts – which is totally useless.

To finish off nicely we’ll edit our “newer posts” link to use this calculated offset in our index.php file, but only display when we have a proper offset to show and we’re not on the first page.

<?php if ($future_offset != 0 || !empty($offsetting)) { ?>
<a href="<?php echo $future_offset; ?>">Newer Posts</a>
<?php } ?>

Tada – all done! I hope that helped somebody out there, but if not at least I have it for archival purposes. If you’re using it, let me know how it goes!


Implemented a new “Asides” feature.

As you might’ve guessed, the thinkMoult blog is not the fanciest WordPress implementation in the neighbourhood. Heck, our sidebar disappeared a while back. The site currently uses three plugins: a related posts feature, one that allows you to subscribe to comments, and finally good ol’ Akismet which likes to delete any comments that mention the word viagra.

WIPUP is gaining momentum and will replace some of my blog posts as the main way I release updates on projects, and as I’ve quit Twitter I have a small hole left inside me for short updates that happen once in a while. Astute readers would’ve noticed just below this post there is a post … without a post! Yes, it’s a tiny update and I shall use it for better means in the future than greeting mother Earth.

For those living under rocks, it’s basically a short sentence update that will not necessarily follow the post-every-2-days schedule I try to stick to. The update can also optionally include a link to another website.

For those curious on how I did this hackery, I followed the instructions from this guide. Well, not fully – I made some modifications and I couldn’t be bothered to style it so I just made it a header. It’s semantically incorrect – so sue me.


Now officially vulnerable to muscle aches! Oh, and WordPress 2.7.

Oh joy! I get an excuse to talk about something else other than tech for a while. For the past few days, my skin seems to have been plagued with what resembles a nasty case of chicken pox, except that it’s restricted to certain regions, such as my hands (like I’m wearing weird lumpy gloves), my back, my feet, and occasionally popping up on my thighs. In addition to that, my lips have managed to achieve a stunning level of dryness and chap(piness?), as well as maintain a constant bleeding throughout the day. Seriously, how do you do that in a tropical country where skyjuice is plentiful? I snapped a picture of it whilst on the way to a charity fun fair this morning, of which I won’t mention details about. (oh, the picture doesn’t do its bark-like quality with oozing blood any justice. In fact, it makes it look redder than it really is – should be brown. Shame.)

It’s all a bit nasty and I wasn’t quite sure what caused it until a visit to the clinic this morning. Because it’s not exactly the smartest thing to announce “hey guys, here are my medical records, look how cool they are!” online, the most I can say is that I pretty much don’t like any sort of painkiller that exists. So any future muscle aches that occur … well, I’ll just have to sit it out and endure. Funnily enough, I actually had a dream about a week ago that I would get this sort of allergic reaction. It seems to have come pretty realistically true. Right down to the minute details of which figure would be the most swollen with lumps. On other news, I will be flying off to the U.K. on Thursday, but don’t expect that to curb the flow of creative blog posts (this one exempted), as I’ve got quite a bit to show.

On other news, I’ve just upgraded to WordPress 2.7. However, this isn’t just any upgrade, it’s an awesome upgrade. That’s because I now get a cooler backend for managing thinkMoult. The whole design looks all beefed and faster to access stuff. Take a look if you don’t believe me:


Along with the upgrade, the flash upload now works again for me (no more tedious click, upload, reload, click), as well as new thumbnail sizes for adding images … there are probably a lot of new features I haven’t noticed yet, but all the same, I’m excited. It’s a new toy, it’s a new thang, it’s awesome.

Rethinking my post, it probably wasn’t a good idea to mix sickness and software together.


WordPress 2.6.5

Well, it seems as though version 2.6.4 has – well, quite literally been scrapped and the developers have skipped right on to 2.6.5. I’m up to date, and again using it as an excuse to take up a post on my writing schedule. For want of something more interesting to talk about, apparently Mr. Stallman himself (I mean, nobody would ever do identity theft online, right?) has heard about my public accusation of his singing abilities being the cause of the slow progress on open-source, and decided that I’m correct. You can see proof of this on the post “Cause of the Open-Source Lag” itself, as well as grab a chance to listen to *choke* … a wonderful song.

Oh, and to make this post have some sort of interesting content, here’s a scrot of my new computer’s desktop, running KDE (4 series), all freshly personalised (still unfinished though). Again, click on it to get the full resolution image (Yes! No longer 1024×768, now it’s 1280×800!)


WordPress Updated

As usual, I’m about two versions behind. (2.6.1) I’ve decided to stop procrastinating and upgrade WordPress. Happy to say it was flawless and we’re now running on 2.6.3.

(Wow great excuse to use up a whole post – don’t worry I’ve got stuff in store)


Hello world!

Welcome to WordPress. This is your first post. Edit or delete it, then start blogging!

Yes, that’s right! We’ve migrated (To change location periodically, especially by moving seasonally from one region to another) to WordPress! The installation was smooth, and we’ve got a sexy theme up doing some jazz for the the thinkMoult page!