Saturday, 19 November 2011

RedDot SEO (Part 1)

SEO is important for commercial websites and that's a given. As for local government (where I work) it has never been seen as quite so relevant. The rather blunt reasoning being that the public don't get a choice in which council they use, it's a captive audience so why bother? One thing is for certain, a users first view of your site (whatever its intended audience) is often not your homepage (therefore ignoring the exceptional navigation you have implemented), it's generally via search engine results.
This is where SEO can help us all; pointing the user in the right direction before they even step foot on your site. Anyway, enough of the waffle as I'm no doubt preaching to the converted here.

SEO and RedDot

I decided to revisit an area I expect every RedDotter has looked into at least once, that of enforcing unique & meaningful urls. The usual first part of this is easy enough - manually define publication packages with sensible directory names; all well and good, and usually done by some kind of RedDot admin so no worries about users getting in a pickle.
The second part is a bit more tricky, making sure published filenames are SEO friendly. There are already a couple of plugins available on the Solutions Exchange that will set page filenames to mirror the page headline but RedDot does not currently enable enforcement of unique filenames within a publication directory. Not an ideal situation and if a duplication occurs files are overwritten.

What process would we like RedDot to follow? Probably something like the following:

  • When a page is created or renamed, set the filename to a parsed version of it (no nasty characters etc.).
  • If a user manually sets a filename don't overwrite it with the page headline (unless the headline subsequently changes).
  • If there is a duplciate filename in existence; prompt the user for an alternative or suggest they change the headline to something more unique.
  • Don't let the user release the page if it contains a duplicate filename (ALA mandatory field warning).
  • Provide a SmartTree accessible toggle to disable the functionality for individual pages.



This is all possible with javascript and RQL and it's something I've been toying with the last couple of days. My main priorities for the solution:
  • Automated as much as possible; I don't want to bother the user unless really necessary.
  • As few RQL calls as possible within SmartEdit (Pure JS calls for any mandatory calls), I don't want the editing process slowed down.
So far I have been concentrating on embedding the processing in SmartEdit page-open.
I have the RQL down to one single mandatory call (I need the current filename and this is only available via RQL).


Impact on users

I am however, having some second thoughts on this approach. Maybe it should be done as part of the workflow process?

So what to do:
  • Stick with SmartEdit page processing which slows the editing process a little.
  • Let workflow do the file naming duplication checks and reject the page to an admin for fixing if required (but they won't thank me for the extra work).
  • As above but reject the page back to the user (unfortunately users hate workflow rejections and won't thank RedDot for not prompting them in SmartEdit before the released the page).
I think I'm going to stick with the SmartEdit option and finish off the implementation next week; I can always change tact at a later date if I think it's not really working.

Once completed, I will explain the RQL / JS in the second part of this post and (time allowing) post a newer version of the RustyLogic RedDotNet library with the backend code you will need if you want to follow the same approach.
Any thoughts on the subject more than welcome :)


Sunday, 13 November 2011

RedDot: Advanced Page Defintions

Page Definitions are great but what if we could expand them to allow the use of page references and even inheritance? Here's one approach to the challenge.
I love users but sometimes its best to keep things simple for them. Options are the enemy especially if they really don't use the CMS very often. Too many options can lead to mistakes and a general mess which requires someone to mop up and the users confidence is dented.

A good way to keep things simple is by using template pre-assignments and page definitions. I have seen many projects use this approach successfully, but, it can lead to multiple (but very similar) base templates which can become a chore to maintain with endless duplicate page definitions cascading off each.

With these thoughts in mind, I started putting a new site together based upon the wonderfully simple 960grid css framework. Determined not to have multiple base templates but to still keep things simple for the user I experimented with page definitions a little deeper. The main problem I ran into was that you cannot assign references to page definitions.

I'm not going to ramble on about why you would want or need to do this (it would take too long and I'm sure you will have your own opinions about it) we'll just move on to the how part...
With some basic jquery and knowledge of RQL you can achieve simple page referencing and much more...

Firstly you'll need an initialisation template for each reference (you assign this to the page definition in the place you want the actual reference to appear), here's one of mine:

<script type="text/javascript">
    if (!rqlCommands)
        var rqlCommands = [];

    head(function() {
    var params = {  "loginGuid": "<%infLoginGuid%>", 
                    "sessionKey": "<%infSessionKey%>", 
                    "pageGuid": "<%!! Context:Pages.GetPage(Guid:<%infPageGuid%>).GetElementByName(conLeftNav).Value[Int32:0].Id  !!%>",
                    "linkGuid": "<%!! Context:Pages.GetPage(Guid:<%infPageGuid%>).MainLink.Id !!%>"
        var rqlCommand = { "SetReference": params };

        params = {  "loginGuid": "<%infLoginGuid%>", 
                        "sessionKey": "<%infSessionKey%>", 
                        "pageGuid": "<%infPageGuid%>"
        rqlCommand = { "Delete": params };

So what's it doing?
Two commands are being formed here and added to an array (There maybe several of these initialisation classes in one page all adding commands to the array).
The first is defining parameters to be used in our RQL to replace the instance of our initialisation page with a reference of the actual page we want.
The second is to delete our initialisation instance so it doesn't hang around in unconnected pages.
Don't worry about the head(function()); it's there because I'm using head.js, if you're using vanilla jquery it would be $document.ready() or similar.

At the bottom of our base template we include a javascript file to process the queue of commands. Here I'm using an AJAX (or SJAX to be more precise) call to a custom web service which in turn calls my RustyLogic RedDotNet library, you could of course call the RQL functions directly in javascript or any other language you choose.

Here is my version:

<script type="text/javascript">

    head(function () {
    if (!(typeof rqlCommands === "undefined")) {
            while (rqlCommands.length > 0) {
                $('#loading').replaceWith('<div id="loading" class="loading" style="width: 100%; height: 100%; top: 100px; display: inline-block; position:absolute; text-align: center; z-index:10000;"><p><img src="/cms/WebClient/App_Themes/Standard/Images/spinner.gif" />Please wait while page is initialised...' + rqlCommands.length + '</p></div>');
                var rqlCommand = rqlCommands.pop();

function ExecuteRql(rqlCommand)
            $.each(rqlCommand, function (commandName, commandParameters) {

                        type: "POST",
                        async: false,
                        contentType: "application/json; charset=utf-8",
                        data: JSON.stringify(commandParameters),
                        url: "/services/RustyLogic.asmx/" + commandName,
                        dataType: "json",
                        success: function (response) 
                        error: function (response) 
                        complete: function (response) 

When a new page is created in SmartEdit the queue is built and executed followed by a page refresh to show the changes to the user.

It's a simple example of what can be achieved but opens up many possibilities. It is easily expanded to allow inheritance from parent pages (I am using it to inherit top navigation when a child page is created).
This has resulted in a simple base class which can be used throughout the site and automation of page initialisation which means less for the user to worry about.

I hope you have enjoyed my first blog post,