Okay, after many iterations and changes, I've finally packaged up the Meld URL Interceptor for the Mura CMS into a functional plugin. This is mainly a development tool, to be sure, but I'm sure the creative out there will find many uses for it. Perhaps best of all, it's free (GPL 2 licensed)!
Meld URL Interceptor offers a few very significant benefits. First, they make URLs "friendly" to both human eyes and search engines. Second, they hide your variable architecture from unfriendly eyes, making it harder to crack your site. Third, they incorporate validation at a very early stage (again, helping to foil the ne'er-do-wells). Finally, they are a very efficient method of packaging variables for your Mura-integrated applications.
The Meld URL Interceptor plugin basically intercepts the URL path for a Mura page request and looks for a matching "key". For instance, if you had a URL like:
http://www.someimaginarysite.com/go/store/product/widgets/the-super-widget/
... Mura would expect that in the Site Manager you would have pages called, in descending order, "store", "product", "widgets" and "the super widget". However, with Meld URL Interceptor, you could have "store" as the only real page, and the rest passed to that page in a "URLInterceptReponse" bean via Mura's event model. That might sound scary and complicated to the uninitiated, but we'll walk through it a bit to make things clearer.
So, let's assume we have as a page in a Mura site the following:
http://www.someimaginarysite.com/store/
...and on this page we have inserted some custom code, a plugin, etc. that will display a product so long as it gets passed some request information. Normally you would have to do this using a query string like:
http://www.someimaginarysite.com/store/?cat=widgets&name=the-super-widget
...but that's just plain ugly, not to mention decidedly less than SEO-friendly. Meld URL Interceptor to the rescue!
Our first step (after installing the plugin) is to create an active intercept. There are several methods of doing this, including:
- creating a static XML file that defines the entire intercept
- using a blank XML template and then creating a series of them dynamically
- creating them directly via the API and some code
I'll cover the first method, creating a static XML intercept. The second method is achieved merely by reading in a blank template and setting the relevant bits (intercept, site id, etc.). The third is too technically complicated to cover in this blog post (and, since I never use this method because of its complexity vs. using the templates, I'll leave for those ambitious enough to figure it out on their own).
Manually-Created Intercepts
To create an intercept, we first make a copy of the blank template, appropriately entitled "BlankIntercept.xml.cfm", which can be found in the plugin's "intercepts" directory. This is a vanilla XML file with the ".cfm" extension to prevent casual viewers from seeing the contents and decrypting your architecture (as long as the "youshallnotpass" application.cfm file remains in the directory as well!). For our example lets name this "ProductIntercept.xml.cfm".
There are three basic levels in the intercept xml file. The first defines the basic settings for our intercept:
These settings are:
- name: a "friendly" name for your eyes
- package: a unique variable-friendly name for your intercept (no spaces, etc.)
- isactive: when yes, the intercept is active
- intercept: the part of the URL we are looking for as the "root" of our intercept, i.e. everything afterward is data.
- pathprefix: is just an extra "assurance" layer for
controlling intercepts. For instance, you could have
"/store/shopping/store/product/widgets/..." and set a path prefix of
"shopping" to ensure only the second "store" URL segment was matched.
- matchfromroot: this indicates that the intercept, above,
must begin at the root. For instance if we set this to true, the URL
"/ourstore/store/product/" would not match because "store" is not in
he root of the Mura URL. If set to false, we could have
"/stores/beverlyhills/store/" or
"shoppingmall/the-litte-bakers-kiosk/store/" and the intercept would
still trigger.
- strict: means that the intercept will trigger (be created) only if there is something following the intercept. For instance "/store/cart/" would create a response in strict mode but "/store/" would not
- recurse: basically tells Meld URL Interceptor to look at URL segments below a matching intercept for matching keys (keys are discussed below).
- keepkey: this retains the keyset id (again, below) as part of the Mura URL. When set to yes, the URL "/store/product/widgets/the-super-widget/" would expect that "/store/product/" is a real Mura page as opposed to just "/store/".
- siteid: identifies which site the intercept is valid for
- keysets: an array to hold our variable definitions (you can have as many keysets as you want for your intercept)
There are some basic rules with the actual "intercept" value. First, they must match the entire URL segment, i.e. "store" would not match "/stores/" or "/ourstore/". Also, intercepts can contain slashes, i.e. "shopnow/store", to more precisely match your URL segments. Finally, they should not start or end with a slash ("store" is a valid intercept value, while "/store" is not).
The next part of the intercept is the keysets. Keysets are basically the tools used to define your variable sets. As an example:
Each keyset defines the following:
- id: is the URL "trigger" that tells the Meld URL Interceptor which key to use. For example, this keyset would be triggered when we had a URL with "/stores/product/", while "/stores/checkout/" would expect to find an id of "checkout"
- eventkey: is a useful variable for event-driven programming, such as FuseBox or ColdBox. You can use this variable to define your event keys separate from the variables it contains.
- keys: is an array of variable definitions
The final section of the intercepts are the variable "keys":
Keys are basically variable definitions, or more precisely every key represents a variable that might be passed in the URL. For example, in "/store/product/widgets/my-super-widget/12345/" the intercept is "store", the keyset is "product", and each URL segment afterward ("widgets", etc.) would be defined by a key.
- name: is the variable name for my key (this must be a variable friendly value, i.e. no spaces, begins with a letter, etc.)
- validate: uses the basic (unless you know regex) integrated validation. Valid values are "string, number/numeric, regex and boolean
- regex: is only used if the validate value is set to "regex". For validation to pass, this value must regex-match the submitted value (the above example uses a UUID-regex pattern to look for ColdFusion-type uuids)
- maxlength: will limit responses to this length (leave blank if you do not want this validation)
- eventkey: a string value that can be useful in event-driven applications
So, all together we have a full intercept template:
If we had the above intercept active in Mura, and had called it with a URL like below:
http://www.someimaginarysite.com/default/index.cfm/store/product/widgets/my-super-widget/123456/
... we would get get the following "URLInterceptResponse" bean returned (this is a cfdump of URLInterceptResponse.getMemento() method):

The URLInterceptResponse bean can be found in the Mura "event", which is generated with every Mura page request. For example, you could use #event.getValue('URLInterceptResponse').getData()# to work with the collected data. You could even use Mura tags, such as:
[mura]event.getValue('URLInterceptResponse').getIsValid()[/mura]
... but I really wouldn't recommend doing that with data passed in through the URL (i.e. via getData()), since it could invite code injection and general naughtiness. Another point is that you might want to create a general, "overloaded"-type intercept that doesn't defined the specific variables. If there are more URL segments then there are defined keys, Meld URL Interceptor will put the values into the "extra" array.
The key functions to use on URLInterceptResponse are:
- getData()
- getExtra()
- getEventKey()
- getFailed()
- getKeysetID()
- getIsValid()
- getValidated()
- getMemento() (which returns all values)
By the way, you'll notice that validation for this example failed because "123456" did not match the validation requirement defined in the intercept. Meld URL Interceptor doesn't actually stop processing or throw an error, but instead merely flags the offending values in the "failed" array. It is up to your application to decide how to deal with failed validation.
Intercept Matching
When it comes to matching, only full URL "segments" will be considered when compared to your intercept values. In other words, if an intercept doesn't exactly match one of the words found between two slashes "/" in a URL, then no intercept has been found. For example:
http://www.someimaginarysite.com/default/index.cfm/shopping/store/product/
- "stores" will not match
- "products" will not match
- "shop" will not match
- "shopping/st" will not match
- "default" will not match
- "shopping" will match
- "store" will match
- "product" will match
There are some other general rules that will help you work with Meld URL Interceptor:
- When there is something in the URL beyond the matching intercept,
both the intercept and keyset id have to match for the
InterceptResponse to exist. This is expected behavior, and exists so
that both "real" Mura pages can exist below a matching intercept and to
allow invalid requests to return a 404/not found error.
- Keysets aren't required. If you simply have <keysets /> in
your xml intercept file, then any match for the intercept will create
the URLInterceptResponse and everything will go in the "Extra" array. Important: this effectively blocks any "real" Mura pages from ever being found below an intercepted segment, so use this wisely.
- Keys aren't required. When there are no keys for a keyset, everything is pushed into the "Extra" array.
Other Stuff
The plugin includes a debugging object called "Meld URL Interceptor - Dump InterceptResponse". You can insert this via Content Objects > Plugins into a Mura page to see the results of your intercept matches. The above dump was generated from this display object.
Also, you will have to reload your Mura application (Reload Application, bottom-left of the Mura CMS admin menu) whenever you create (or update) an intercept template. This is because the intercepts are loaded and stored in Mura's application scope. New intercept files will not be recognized until the application is reloaded.
In Conclusion
Meld URL Interceptor for the Mura CMS is a clean, efficient and secure method of passing variables to your Mura applications and plugins. You can even integrate the library directly into your own plugin, which allows you to control aspects like dynamic creation or extend the validation model to somthing more robust. For instance, we use the library cfcs in our Meld Forums plugin to control the flow of the application (event keys and FuseBox) and to add an extra layer of security.
Hopefully you can find a good use for them too!
Download Meld URL Interceptor.

Nov 23, 2009 at 1:52 PM Nice! I'll very likely make use of this, soon. Thanks!
Mar 1, 2010 at 12:17 PM Just beginning to get to grips with Mura - this plugin looks useful for integrating some of my old web application features into my new Mura site...
Looking at the instructions above, it seems that an intercepted URL must have at least three parts to it - the intercept, the keyset and one or more values.
How could I use the plugin to handle a URL such as:
www.mydomain.com/products/my-great-product/
...where I want "my-great-product" to be passed to the "products" page?
Mar 1, 2010 at 12:30 PM @Seb: there are a couple of ways you can do this.
For instance "products" could be your root application (i.e. the Mura page you insert the Intercept content object into), and you would not have any specific keys attached to the intercept. The intercept response would contain a data array that contains all the proceeding url segments (i.e. array[1] = 'my-great-product'). This is fine if you plan to do all of your application flow control via the returned array values.
A much more powerful way to do this is to again have "products" as your root application, but then use "/products/product/my-great-product" or something similar as "product" can then become your application's action. This way you can have /products/list or products/checkout and use the second element to control your application flow. The second url segment becomes the "id" in your intercept.xml file, and everything after that can be data passed to the specific part of your application you've identified.
For simple apps, the former is just fine (I use that all the time). For something more complex, you'll definitely want to explore what the keys/keysets can do for you.
Mar 1, 2010 at 12:45 PM Thanks Grant.
I've got it working with method 1 - the only disadvantage is that you can't use patterns to check the validity of the value, but rather need to do that within the page. No great problem, though.
The actual case in hand is a sports team website, with, for example,
/results/2009/
I suppose I could re-work the structure so I have a whole top-level directory for stats, so "stats" would then be the intercept.
It might be nice, though, to be able to have the intercept and the keyset as the same section of the original URL - thus opening up the power of the keysets to all URLs.
Mar 1, 2010 at 12:53 PM Actually, that might be possible if you add the intercept to the root of your site. Then "results" is the keyset id and "2009" is a key. I've never done this, so I'm not sure what if anything Intercepts will make of this ... off of the root page with no path at all it might trigger and it might not.
I might have to play with this in the future, so any input is welcome.
Mar 1, 2010 at 1:02 PM No, doesn't seem to work... (my URLs don't include the site name, so that might be the problem).
Apr 9, 2010 at 6:09 AM All working nicely now...
If I set up an intercept with a keyset and key values within that, are all the keys I define required? For instance, I might have a keyset with 3 keys, but the URL may include 1, 2 3 or none of them...