How to Create Dynamic Content Display Objects in Mura CMS with MuraFW1 Plugins

If you've ever created a MuraFW1 plugin for Mura CMS (which this article assumes by the way!), you may have wondered how to go about creating dynamic Content Display Objects. Doing so would enable an end-user to edit a page, go to the Content Objects tab, select Plugins from the Available Content Objects dropdown, select your plugin, then select your "dynamic display object" which would then fire off and return yet form field containing the dynamic list of options available to be assigned to the desired content display region.

Still with me? Great!

Ultimately, there's three main parts to this:

1. Create the display method, i.e., myDisplay

2. Then create an "OptionsRender" display method which follows the naming convention of myDisplayOptionsRender

3. Finally, we'll add the basic display method (i.e., myDisplay) to the config.xml.cfm's list of available displayObjects

1. Create The Display Method

In the pluginEventHandler.cfc, we can add our basic display method. Ultimately, we should eventually expect a JSON formatted variable to be passed in via $.event('params'). Obviously, we'll need to deserialize the JSON, and once we do that, we can do whatever we like with it. An easy way to pass this information over to FW/1 is to stuff any desired variables into the URL scope ... and then call the typical renderApp() method and let FW/1 take over from there. Check out our simple example below which is expecting to find "whateverID" to be in the unpacked params:

<cffunction name="dspWhatever" output="false" returntype="any">
    <cfargument name="$" hint="mura scope" />
    <cfscript>
        var local = {};

        // grab everything that's been passed in from OptionsRender() and unpack the JSON option values that were saved
        local.params = DeserializeJSON($.event('params'));

        // stuff selected WhateverID into the url scope so that fw1 will pick it up
        if ( StructKeyExists(local.params, 'whateverid') ) {
            url.whateverid = local.params.whateverid;
        };

        // something went wrong ... so let's not bomb everything
        if ( not StructKeyExists(url, 'whateverid') ) {
            return '';
        } else {
            return renderApp($,'public:main.default');
        };
    
</cfscript>
</cffunction>

2. Create An "OptionsRender" Method

Now for the magical part! Whatever you named your displayMethod, you simply append "OptionsRender" to the end of it. So if you have dspWidgets, then you would have dspWidgetsOptionsRender as an available method. In the example below, I'm creating a simple recordset which we'll then loop over to output the options and dynamically generate the option value which is a tilde (~) separated list where the last item in the list is a JSON formatted list of params...yes, the very same params our display method in step one is expecting to find!

<cffunction name="dspWhateverOptionsRender" output="false" returntype="any">
    <cfargument name="$" />
    <cfscript>
        var local = {};
        
        // this recordset could come from anywhere ... doesn't even have to be a recordset, could be anything that contains data that you want to grab options from
        local.rs = QueryNew('WhateverID,WhateverTitle,WhateverField', 'VarChar,VarChar,VarChar');

        QueryAddRow(local.rs, 1);
        QuerySetCell(local.rs,'WhateverID', 'anyid-1', 1);
        QuerySetCell(local.rs,'WhateverTitle', 'My Awesome Title', 1);
        QuerySetCell(local.rs, 'WhateverField', 'Hi Ho Cherry-O', 1);

        QueryAddRow(local.rs, 1);
        QuerySetCell(local.rs, 'WhateverID', 'anyid-2', 2);
        QuerySetCell(local.rs, 'WhateverTitle', 'Another Great Title', 2);
        QuerySetCell(local.rs, 'WhateverField', 'Knick Knack Paddy Whack', 2);
    
</cfscript>
    <cfsavecontent variable="local.str">
        <cfif local.rs.recordcount>
            <select name="availableObjects" id="availableObjects" class="dropdown">
                <!--- we're going to pack everything up into a tilde (~) separated list where the last item in the is a JSON formatted list of params to be passed over to the display method --->
                <cfoutput query="local.rs">
                    <option value="plugin~#HTMLEditFormat(Replace(WhateverTitle,'~','','ALL'))#~#$.event('objectid')#~{'whateverid':'#WhateverID#','anotherfield':'#WhateverField#'}">
                        #HTMLEditFormat(WhateverTitle)#
                    </option>
                </cfoutput>
            </select>
        <cfelse>
            <p><em>Please create some Whatevers first.</em></p>
        </cfif>
    </cfsavecontent>
    <cfreturn local.str />
</cffunction>

3. Add The Rendering Method To config.xml.cfm

The final step in all this is to add your basic displayMethod to your config.xml.cfm file so that it will be registered with Mura CMS. Once it is, then we should expect to see it show up in the Content Objects tab when someone selects our plugin.

<displayobjects location="global">
    <displayobject name="Display Whatever" displaymethod="dspWhatever" component="pluginEventHandler" persist="false" />
</displayobjects>

That's it! Hopefully this helps anyone else who is creating FW/1 plugins for Mura CMS.

Cheers!

Dynamically Define a Mura CMS Theme's Display Regions

A question came up recently regarding Mura CMS and how to alter the available content object display regions. This is actually quite simple to control from within the Admin area of the site by going to Site Settings > Select the Site > Select the Display Regions Tab and then choose the Number of Display Regions, select the Primary Display Region and define the Display Region Names as a carrot "^" delimited list.

However, this particular inquisitor was actually interested in how to control these settings from within a specific theme itself. Luckily, as with most things in Mura, it really isn't all that difficult to do.

Since this is not something we would want to happen everytime a page loads, it would probably work best to use the onApplicationLoad() method in the theme's eventHandler.cfc. This way, it will only be executed whenever the entire application is loaded. The next thing we need to figure out is what methods we need to call and how to tell Mura to save our settings.

So, as I've pointed other people in the past, most all of the important methods that comprise the Mura engine can be found under the /requirements/mura/ directory. Since we're dealing with Site Settings, we'll want to review the methods available to us under the 'settings' directory. There you'll find settingsManager.cfc among other files. Within this file, you'll be able to locate a method called update() which takes an argument called data which is required to be a struct/object. The data object should have a key which contains the siteid of the site we wish to edit the settings for. Then, we can update any of the available settings we wish!

Here's some code for you to play around with:

<cffunction name="onApplicationLoad">
    <cfargument name="$" />
    <cfscript>
        var local = {};
        
        // dynamically alter the available display regions from within a theme
        local.data = {
            siteID = $.siteConfig('siteid')
            , columnCount = 5
            , primaryColumn = 3
            , columnNames = 'Test^Left Column^Main Content^Right Column^Footer'
        };
        local.siteBean = $.getBean('settingsManager');
        local.siteBean.update(local.data);
    
</cfscript>
</cffunction>

If you're wondering what settings are available to you, simply do a dump of all the existing values:

<cfdump var="#$.siteConfig().getAllValues()#" />

Hope that helps someone else trying to update their Mura Site Settings from within the theme itself.

Cheers!

How to Force Mura CMS to Use Primary Domains in SEO-Friendly Way

Mura CMS allows you to have multiple URLs assigned to each particular site. For example, you could have http://domain.com be your 'primary' domain and http://www.domain.com and http://www.anotherdomain.com set up as 'domain aliases' so that when you navigate to any one of these domains, Mura will serve up the desired site. This is pretty cool actually and there are a number of use cases out there on why you would want that to happen. However, if you want the 'primary' domain to be the one and only one used for SEO purposes and such, then you'll need to make just a couple of minor tweaks to make that happen. The best part is, I'll show you just how easy this is!

The first thing you'll need to do is copy the following method into your local contentRenderer.cfc. It's located at /{siteid}/includes/contentRenderer.cfc (or if you want this to be 'theme-specific' then edit the one located at /{siteid}/includes/themes/{themeName}/contentRenderer.cfc).

<cffunction name="doEnforceDomain" access="public" output="false" returntype="any" hint="By default, I will redirect users in an SEO-friendly way to your primary domain ... you may optionally override this by giving me an alternate 'primary' domain to enforce.">
    <cfargument name="domain" required="false" default="#$.siteConfig('domain')#" />
    <cfscript>
        var local = StructNew();
        local.doRedirect = false;
        local.servername = getPageContext().getRequest().getServerName();
        if ( local.servername neq arguments.domain ) {
            local.doRedirect = true;
            local.servername = arguments.domain;
            local.baseurl = getPageContext().getRequest().getScheme() & '://' & local.servername;
            // if not using a standard port, we should prolly include it
            local.port = getPageContext().getRequest().getServerPort();
            if ( local.port neq '80' and local.port neq '443' ) {
                local.baseurl = local.baseurl & ':' & local.port;
            };
            // uri returns '/index.cfm/path/to/page/'
            local.uri = getPageContext().getRequest().getRequestURI();
            local.urlstr = local.baseurl & local.uri;
            // append query string, if any
            if ( len(trim(getPageContext().getRequest().getQueryString())) ) {
                local.urlstr = local.urlstr & '?' & getPageContext().getRequest().getQueryString();
            };
        };
    
</cfscript>        
    <cfif local.doRedirect>
        <cflocation url="#local.urlstr#" addtoken="false" statuscode="301" />
        <cfelse>
        <cfreturn />
    </cfif>
</cffunction>

The last thing you'll need is an itsy-bitsy, teeny-weeny line of code that can actually be dropped in a number of different places. More on that in a second. However, if you want your entire site to always use the primary domain, then drop the following line of code into your local eventHandler.cfc in the onRenderStart() method. This is located in the same locations as the contentRenderer.cfc.

<cffunction name="onRenderStart">
    <cfargument name="$" />
    <cfset $.getContentRenderer().doEnforceDomain() />
</cffunction>

Remember how I mentioned you could drop this in a number of different places? Well, let me explain. Let's say you have an area of the site that uses a particular template and you want ONLY that area of the site to use a specific domain...for example 'http://docs.domain.com' for a documents area. Well instead of using the eventHandler.cfc route, you could just drop this at the top of the template and then anytime someone visits this area, they'll get re-routed properly and in a SEO-friendly manner. Isn't that sweet?

<cfscript>
    $.getContentRenderer().doEnforceDomain('docs.domain.com');
</cfscript>

As always, I hope this helps!

Cheers!

More Entries

© 2024, Stephen J. Withington, Jr.  |  Hosted by Hostek.com

Creative Commons License   |   This work is licensed under a Creative Commons Attribution 3.0 Unported License.