Advanced Plugin Development for the Mura CMS: Part II: Framework (and Reset)
Mura CMS , Mura Plugins Add commentsOkay, so the more I thought about what I was trying to achieve with this tutorial, the more I realized I was committing a cardinal sin: using an overly complex example to illustrate a fairly complex problem. So, I'm going to step back a bit on the whole "learn advanced plugin development by studying a complex plugin" thing and instead build a tutorial plugin with various levels of "hello world" functionality. The previously mentioned complex plugin (Meld Gallery) will still be coming ... it's looking pretty good, by the way ... but we'll be working with something a lot simpler here.
Framework
A Mura plugin is composed of several individual components, any (or all) of which will be employed in managing and displaying plugin content. The first component, the 'administrator', is the interface available to the plugin inside the Mura administrator. A second key component is the "display object", which lets you build a direct connection between your application and a Mura page. The third key component is Mura events, which can be used to not only display content on the Mura front end, but also interact with other Mura functions as they occur. Depending upon the level of complexity, a Mura plugin will often use all three of these components to achieve its desired functionality.
To achieve this, we must have a solid starting point from which to build our plugin. I've taken the stock Mura FW/1 plugin and extended it a bit to make incorporating the desired functionality easier. I've found FW/1 ideal for developing Mura plugins because the "SubSystem" architecture allows me to create not only an overall application framework, but also use individual subsystems as the "admin" area and "display objects". This allows me to organize my code, more easily manage permissions, and build out display objects that share common functionality.
The downloadable example (bottom of post) incorporates these ideas. The FW/1 plugin is designed so that the "admin" SubSystem acts as the administrative interface for the plugin, while the "displays" SubSystem connects to the Mura front-end. You may want to install this plugin into Mura so you can follow along (make sure you are using an up-to-date version of Mura, as there are some really new features we'll be looking at in coming days).
Administrator
Lets first look at the "frameworkConfig.cfm" file, found in the root of the plugin:
The most important detail is in setting the default subsystem to "admin". The Mura Administrator will essentially act as a "wrapper" around the admin SubSystem, which will act pretty much as it would as a stand-alone application. The advantage is that by setting "admin" as the default subsystem, we won't have to manually insert this into the "action" tags. We will be manually calling the other subsystems in our display objects, so this will not affect them. NOTE: the test plugin is currently set to "reload" on every request, as per the 'reloadApplicationOnEveryRequest' setting. You will want to remove or set that value to 'false' in a production release.
If you have installed the plugin, go to "Plugins" and click on the "APD Part II Example" title. You should see a welcome screen that starts with "Welcome to the Advanced Plugin Development Part II Tutorial example". What you are looking at is the /admin/views/main/default.cfm page, which thanks to our FW/1 configuration above is the default page for our FW/1 application. In a real application we would build out this area to give Mura managers control over our plugin's functionality.
Display Objects
If you look in the example plugin, you will see a folder called "MyDisplay". This is a second FW/1 SubSystem, and will act as the front-end connector to our Plugin.
In the majority of your plugins you will at some point want to display content on a web page. Normally we can access built-in functionality from the Site Manager > Page > Content Objects tab, and it is no different with Plugins. If you open the Content Objects dropdown on the left side of that tab, you will see "Plugins" at the bottom. Select this, then (assuming you've installed the example plugin) the "APD Example II" option. You will now see any "single option" display objects, as well as a dropdown that will let you select any "dynamic" display objects (see the Configurable Display Objects post for more detail on this).
To get display objects to appear in the Content Objects dropdown, we must let Mura know they exist. We do this in the /plugin/config.xml file:
The first two examples are the long-hand, manual way of tying a FW/1 "action" to a Display Object, while the third opens up a lot of possibilites. We're going to explore the implications and advantages of dynamic Display Objects in a future post, but for now I want you to get used to how display objects work.
Our first example, "Display Object One", can be found in the "MyDisplay" SubSystem: in the folder "display" we will find the displayManager.cfc. Note that his cfc importantly extends "mura.plugin.pluginGenericEventHandler". As identified in the config.xml code, the function "displayOne" will run on any page the display object has been inserted into.
In this case, the "doEvent" function also included in this file will take the arguments $ (the Mura Scope, coming in more detail in Part III) and "main.saymyname", which is the "action" argument for the FW/1 application. Essentially we are manually calling the FW/1 SubSection "MyDisplay" with "main:saymyname" identifying which controller/view to render. The FW/1 application will run as if it had been called as a stand-alone application, returning the generated content to the Mura page that includes the Display Object. In this case, the display object will either a) state your name if you are logged in, or b) say, "sorry, you're a stranger to me".
Dynamic Display Objects
As a brief introduction to the concept of using dynamic display objects as a tool for easily incorporating individual FW/1 pages, take a look at the "displayDynamicOptionsRender" function in /MyDisplay/display/displayManager.cfc:
To enable this functionality, I've modified the stock FW/1 plugin template in several ways, most importantly by incorporating the "params" option in the dynamic Display Object to include an "action" value, which is used in the doEvent() function:
Essentially, this allows me to quickly add a series of FW/1 pages as individual Display Objects. This means that you only have to add a single reference to the Display Object cfc in your config.xml file, and yet still have any number of display objects associated to a particular SubSystem. Now, this isn't always the most efficient or effective way of adding Display Object options to your plugin, but it can be quite useful if the content is small and there isn't a great deal of interaction with them. Also, this is probably the most basic way to use dynamic display objects; we'll cover a few more advanced examples later in the series.
Events
We've already covered a lot of ground in this post, so I'll leave Mura Events for a future post ... especially since they include a huge amount of functionality on their own. Also coming up will be incorporating bean factories, a walk through the Mura Scope as it applies to plugins, and finally an install plan that includes setting up your plugin's database. Now that the FW/1 plugin and Mura plugin framework are really solid, posts in the APD series should come on a much more regular basis. Also, don't be afraid to ask for more detail on a specific subject!
One last note: the absolute best source of help in using and developing for Mura is the weekly live Mura Show. I try to never miss one, as there is almost always something new or novel introduced ... and you get to talk directly to the guys who develop Mura! You can also check out the archives, as there is a lot of good content in there as well.
Download: APD Part II Example

Aug 20, 2010 at 1:41 AM Hi Grant.
I am enjoying reading your posts on Plugins with FW/1, also watched the MuraShow regarding the Uber Gallery... looks very interesting!
Im using the example provided to update my own plugin for a Mura site to adopt the subsystem approach, as this one site will have several plugins for it. If i wanted to output an admin section, this i am assuming would be done in the admin subsystem? If you require data storage that cannot be added into the core Mura DB, how do you suggest adding tables? Do you create a separate DB for custom functionality or would it be best to add the required tables into the Mura DB?
Aug 20, 2010 at 5:23 AM Hi Grant,
Been looking through the example (again) and trying to load in a DSN into the Service in the admin subsystem. With Coldspring, im trying to add the dsn as a constructor argument on ApplicationLoad, but it never gets populated.
Is this something that is viable with the mura fw/1 coldspring setup?
How would you go about passing in the dsn into a service, if it is different from the Base Mura DSN? Really trying to get my head around it all, and it seems that your blog keeps coming up with this setup!
Thanks
Aug 20, 2010 at 8:03 AM @jbuda adding a custom datasource to your plugin is a multi-step process, from the install to the integration bits. It does involve ColdSpring (or whatever other bean factory you are using), but also a few other sections of your plugin as well. Because of this, I can't really answer the question in a simple blog post. You can look at the example from Part I of this series, which does include some of the code for accessing an external DSN, but not all.
It's actually a good next step in the series, so perhaps I'll focus on that in Part III.
Aug 20, 2010 at 8:32 AM Hi Grant. I have been looking further at the problem i was having, i noticed that i was trying to mix the 'auto' service call by FW/1 and not actually using the Bean Factory.
I have now amended my controller to ensure that it carries out the function request for the service. With that in mind, i use the bean factory to return the service in the application scope, then call the required function. I noticed that doing this, i can just pass the DSN as a constructor in Coldspring to the Service with the ${dsn} argument. So my query could just have something like #variables.dsn# when required.
The dsn can then be whatever i have used when installing the Plugin in Mura.
I noticed some commented out areas of your Application.cfc that enabled me to use this procedure, which is what im hoping you had in mind for?
Aug 20, 2010 at 9:49 AM @jbuda ... you might want to check out 'Advanced Plugin Development, Part III' for even more detail ...
Aug 24, 2010 at 9:30 AM Hi Grant, i have been trying to get the demo working that is supplied on this post.
however, im getting an error on the displayManager.cfc with this code : <cfset var params = deserializeJSON( $.event().getValue("params") ) />
The value of params is always empty and so CF errors parsing the JSON. Have i missed something with this? I was trying to output the displayOne plugin
Aug 24, 2010 at 10:34 AM The plugin was developed on my local machine, which is Railo ... ColdFusion must have an issue with trying to deserialize an empty string.
In the displayManager.cfc, change the code to the following:
<cfif isJSON( $.event().getValue("params") )>
<cfset request.context.params = deserializeJSON( $.event().getValue("params") ) />
</cfif>
Aug 25, 2010 at 8:24 AM Grant, seem to have more problems that im hoping you could help with?
When i load up my custom display object within the subsystem - plugindisplay - with the default action, all works fine. If however, i then try and get some data after the dom has loaded using ?action=plugindisplay:main.getall, it errors.
I have checked through the code and noticed that the 'before' method in the controller.cfc has nothing in the RC scope within argument when running the main.getall action. however, the RC scope is completely populated with Mura values when the default action runs.
Have you experienced this?
Aug 25, 2010 at 8:39 AM Grant, think i found the problem... maybe you could confirm. In the controller.cfc, the line : <cfset rc.userID = $.currentUser('userID')> : seems to cause the problem.
After commenting that out, it works fine. Is this line necessary?