blogarchive

Extend Mura's display functionality with custom methods which can be used directly in the admin content editor or wired into the logic of the theme's display templates. 

  • Aug 13, 2015
  • Ronnie Duke
  • Developers

Mura gives you the ability to create your own custom functionality, directly from the theme level. This article will show you how you can enhance your themes by creating reusable custom methods that can be used in both layout templates and the admin editor.

Recap

When developing themes, you may have a series of 'objects' that you would like to display over and over again, such as slideshows, detail blocks, etc.

In the Class Extensions tutorial, we went over how to create custom attributes for a Book, and display them onto the page using a component. Here is the final component we created:

<h3>Details</h3>
 
<p>
    <strong>Author:</strong> [ m ]$.content('bookAuthor')[ /m ]<br />
    <strong>Release Date:</strong>&nbsp;[ m ]dateFormat($.content('bookReleaseDate'),'mm/dd/yy')[ /m ]<br />
    <strong>ISBN:</strong>&nbsp;[ m ]$.content('bookISBN')[ /m ]<br />
    <strong>Condition:</strong>&nbsp;[ m ]$.content('bookNewUsed')[ /m ]<br />
    <strong>Price:</strong>&nbsp;$[ m ]$.content('bookPrice')[ /m ]   
</p>

This worked well for our example, but there are a few drawbacks:

  1. The markup is open to admin editors who could potentially alter the markup, affecting its display.
  2. There is no logic around each item; if ISBN doesn't exist, for example, the label would still show with a blank value next to it. This would also happen if the user added this component to a page that isn't even a Book, in which case none of the extended attributes would show up.

One way we can solve these issues is to create a custom method in our theme that will display the book details whenever it's used. 

Creating a Custom Display Method

Display methods in Mura are located in a file called contentRenderer.cfc. Now, this file exists in two locations within your site:

  1. Site Level: {siteID}/includes/contentRenderer.cfc
  2. Theme Level: {siteID}includes/themes/{ThemeName}/contentRenderer.cfc

It's important to understand that both files will render methods in your site, however, theme level contentRenderer.cfc will append any methods that don't exist at the site level, and override any methods that already exist at the site level.

Another important thing to consider is that when doing theme development, any work done above the theme directory will not be distributed with your theme. If you're planning on creating themes for distribution, I recommend keeping all of your custom code at the theme level.

Adding to Your Theme's contentRenderer.cfc

In your theme directory, open the contentRenderer.cfc file

Note: if you are building your theme from scratch and do not have the file already, go ahead and add the contentRenderer.cfc from the MuraBootstrap3 theme attached to this article. 

In this file, we're going to create a new function called dspBookDetails

<cffunction name="dspBookDetails" output="yes">
    <!--- Logic goes here --->
?</cffunction>

Now, whenever we call this function, Mura will execute anything inside of it. At the moment, there is nothing for Mura to do or display, until we specify that we want the function to return something. Let's add a variable to store our content for this function to return:

<cffunction name="dspBookDetails" output="yes">
    <!--- Create the variable to store our content --->
    <cfsavecontent variable="ret">
        <cfoutput>
            <!--- Content here --->
        </cfoutput>
    </cfsavecontent>
     <!--- Return the variable containing the content --->
    <cfreturn ret>
</cffunction>

You can see now that we are creating a variable to store all of our content and logic (<cfsavecontent>), then telling the function to return that variable to the page (<cfreturn>).

Now we are ready to add the actual book content, similar to how we had in the component:

<cffunction name="dspBookDetails" output="yes">
        <cfsavecontent variable="ret">
            <cfoutput>
                <h3>Details</h3>
                <p>
                    <strong>Author:</strong>&nbsp;#$.content('bookAuthor')#<br />
                    <strong>Release Date:</strong>&nbsp;#dateFormat($.content('bookReleaseDate'),'mm/dd/yy')#<br />
                    <strong>ISBN:</strong>&nbsp;#$.content('bookISBN')#<br />
                    <strong>Condition:</strong>&nbsp;#$.content('bookNewUsed')#<br />
                    <strong>Price:</strong>&nbsp;$#$.content('bookPrice')#   
                </p>
            </cfoutput>
        </cfsavecontent>
        <cfreturn ret>
    </cffunction>

Note: we replaced the [ m ] tags with hashtags (#) because we're in a code file.[ m ] tags are only able to be used in the admin area editors. 
(Also, for the purposes of this article, we have added spaces inside the [ brackets ] which should be removed in actual use.)

Calling the Function From a Component

Now that we have the function created in contentRenderer.cfc, we can call it using the Mura Scope. Go back to the component and replace the contents with the following line:

<div>[ m ]$.dspBookDetails()[ /m ]</div>

Note: We are wrapping the function in a <div> to prevent CKEditor from adding additional <p> tags around the output. Also, don't forget the parenthesis after calling the function ()

When you publish the component and reload the book page, you should see the book details identical to how you had it before:

Calling the Function From a Layout Template

In addition to calling the function from a component, you can also use your custom methods directly within your layout templates. For example, you can duplicate thetwoCol_SR.cfm template, call it book.cfm, and add this to the right sidebar:

<aside class="col-lg-3 col-md-3 col-sm-4 col-xs-12 sidebar">
    <!--- Display the book details --->
    #$.dspBookDetails()#
    <!--- Display any additional display objects addeed to this page --->
    #$.dspObjects(3)#
</aside>

Adding Some Dynamic Logic

Now that we have the book details contained in a custom method, we can add some additional logic to make our output a bit more robust. First, let's wrap the entire contents of the function to detect whether or not the page in question is actually a book. If not, then it will return nothing.

<cffunction name="dspBookDetails" output="yes">
    <!--- If the page is a 'Book' then run the function --->
    <cfif $.content('subType') eq 'Book'>
        <cfsavecontent variable="ret">
            <cfoutput>
                <h3>Details</h3>
                <p>
                    <strong>Author:</strong> #$.content('bookAuthor')#<br />
                    <strong>Release Date:</strong>&nbsp;#dateFormat($.content('bookReleaseDate'),'mm/dd/yy')#<br />
                    <strong>ISBN:</strong>&nbsp;#$.content('bookISBN')#<br />
                    <strong>Condition:</strong>&nbsp;#$.content('bookNewUsed')#<br />
                    <strong>Price:</strong>&nbsp;$#$.content('bookPrice')#   
                </p>
            </cfoutput>
        </cfsavecontent>  
        <cfreturn ret>
    </cfif>
</cffunction>

The next thing we want to do is detect whether a value actually exists for each item we're displaying (Author, ISBN, etc). If the value does not exist, we want to skip the item and move on to the next line.

We can accomplish this by wrapping an if statement around each line, seeing if there is a length of the value greater than 0:

<!--- If the book author value has a length greater than 0 --->
<cfif len( $.content('bookAuthor') )>
    <strong>Author:</strong> #$.content('bookAuthor')#<br />
</cfif>

Wrap each book detail item in the code above, replacing the $.content() values with the appropriate values for each item:

<cffunction name="dspBookDetails" output="yes">
    <!--- If the page is a 'Book' then run the function --->
    <cfif $.content('subType') eq 'Book'>
        <cfsavecontent variable="ret">
            <cfoutput>
                <h3>Details</h3>         
                <p>
                    <!--- Only output if the Author is defined --->
                    <cfif len($.content('bookAuthor'))>
                        <strong>Author:</strong> #$.content('bookAuthor')#<br />
                    </cfif>
                    <cfif len($.content('bookReleaseDate'))>
                        <strong>Release Date:</strong>&nbsp;#dateFormat($.content('bookReleaseDate'),'mm/dd/yy')#<br />
                    </cfif>
                    <cfif len($.content('bookISBN'))>
                        <strong>ISBN:</strong>&nbsp;#$.content('bookISBN')#<br />
                    </cfif>
                    <cfif len($.content('bookNewUsed'))>
                        <strong>Condition:</strong>&nbsp;#$.content('bookNewUsed')#<br />
                    </cfif>
                    <cfif len($.content('bookPrice'))>
                        <strong>Price:</strong>&nbsp;$#$.content('bookPrice')#
                    </cfif>  
                </p>
            </cfoutput>
        </cfsavecontent>         
        <cfreturn ret>
    </cfif>
</cffunction>

Conclusion

Creating custom methods is a great way to add versatility to your themes while maintaining the integrity of your code.

Also see Mura CMS Documentation: Functions & Methods

Developers

Blog Post
By Grant Shepert
Blog Post
By Grant Shepert
Blog Post
By Grant Shepert
Blog Post
By Grant Shepert
Blog Post
By Grant Shepert
Blog Post
By Grant Shepert
Blog Post
By Grant Shepert
Blog Post
By Michael Evangelista
Blog Post
By Grant Shepert
Blog Post
By Grant Shepert
Blog Post
By Michael Evangelista
Blog Post
By Michael Evangelista
Blog Post
By Grant Shepert
Blog Post
By Matt Levine
Blog Post
By The Mura Team
Blog Post
By Pat Santora
Blog Post
By Pat Santora
Blog Post
By Matt Levine
Blog Post
By Matt Levine
Blog Post
By Matt Levine
Blog Post
By Eddie Ballisty
Blog Post
By Sean Schroeder
Blog Post
By Grant Shepert
Blog Post
By Grant Shepert
Blog Post
By Grant Shepert
Blog Post
By Grant Shepert

Marketers

Blog Post
By Andrew Medal
Blog Post
Blog Post
By Ronnie Duke
Blog Post
By Sean Schroeder