Mura X: Extending Mura

Mura Beans & Objects

The term "bean" is borrowed from the Java programming language. According to Wikipedia, JavaBeans are reusable software components. They are used to encapsulate many objects into a single object (the bean), so that they can be passed around as a single bean object instead of multiple individual objects.

In CFML, a bean can be a ColdFusion Component (CFC). However, there are a few characteristics the CFC must have to differentiate itself from other types of CFCs:

  1. Serializable
  2. A no-argument constructor
  3. Allows access to properties using getter and setter methods

In short, beans hold information about the object or entity they represent, and allow developers the ability to quickly and easily access and manipulate some of its attributes. For example, a bean could represent a person, and its attributes might contain data such as the person's name, age, height, weight, birthdate, etc.

Mura Bean Objects

In Mura, developers can use its software components without necessarily having to understand their inner workings. Mura bean objects provide developers the ability to directly access and manipulate their associated records in the database. This is most easily accomplished using the bean's associated helper methods. As you'll see in the Mura ORM section, developers may also create their own bean objects too.

Syntax

Here we cover the basic syntax that applies to all Mura bean objects. When you want to work with a specific bean object, you will first want to load it by a specific attribute. However, as each bean object is slightly different, the attributes you may load by will vary.

Loading Bean Objects

As illustrated in the examples below, you should use the special Mura scope (m) method labeled getBean, when you wish to work with a Mura bean object.

someBean = m.getBean('someBean')
            .loadBy(someAttribute='someAttributeValue');

You may optionally include a siteid parameter to load a bean from another Mura site, managed under the same installation.

someBean = m.getBean('someBean')
            .loadBy(someAttribute='someAttributeValue', siteid='SomeSiteID');

Getting an Attribute of a Bean Object

The "getter" examples below illustrate how to "get" a specific attribute of a bean object.

someBean.get('attributeName');
someBean.get{AttributeName}();
someBean.getValue('attributeName');

Setting an Attribute of a Bean Object

The "setter" examples below illustrate how to "set" a specific attribute of a bean object.

someBean.set('attributeName', 'Some Value');
someBean.set{AttributeName}('Some Value');
someBean.setValue('attributeName', 'Some Value');

Verify Existence of a Bean Object

Most bean objects have a couple of helper methods to verify whether the bean object exists, or if it's new. Whichever method you choose, is entirely personal preference.

// returns `true` if bean exists, or `false` if it does not
someBean.exists();

// returns `true` if bean is new, or `false` if bean already exists
someBean.getIsNew();

Deleting Bean Objects

The basics for deleting a bean object is to simply call the delete helper method. The following example assume you have a properly loaded bean to delete.

someBean.delete();

Creating, Updating, & Saving Bean Objects

The basics for saving a bean object is to simply call the save helper method. The following example assumes you have a properly loaded bean to save, and may have made some changes to one or more attributes using one of the setter methods above.

someBean.save();

Error Handling

When saving an entity, if an error occurs, you'll need to figure out how you want to deal with the error(s). Keep in mind, if an error is returned, the save did not occur!

Errors are returned as a structure, where each key in the struct represents the object's attribute "name", and the value contains the error message itself.

someBean.save();

// If the bean has errors, then it did not save...
if ( someBean.hasErrors() ) {

  // errors are returned as a struct
  errors = someBean.getErrors();

  WriteOutput('<h3>Please review the following errors:</h3><ul>');

  // Loop over the errors
  for ( e in errors ) {
    WriteOutput('<li>Attribute: #e# <br> Message: #errors[e]#</li>');
  }

  WriteOutput('</ul>');
} else {
  WriteOutput('<h2>Success :: No Errors!</h2>');
}

Common Bean Objects

The following table contains information about Mura's most commonly used bean objects.

Bean Load By Description
configBean n/a Includes instance-wide configuration data, such as data collected in the settings.ini.cfm file.
site siteid Includes site configuration data collected via the Site Settings section in the administration area, as well as other useful site-specific information.
content contentid,
contenthistid,
filename,
remoteid,
title,
urltitle
Includes data about a Mura content item, such as the title, summary, body, etc. In addition, there are several content-specific helper methods included. For example, you could get an iterator of any children of the content item, what specific categories the content item has been assigned to, etc.
user userid,
username,
remoteid
Includes user-specific data, and includes a number of helper methods. For example, you can obtain an iterator of the groups the user belongs to, etc.
group groupid,
groupname,
remoteid
Includes group-specific data, and includes a number of helper methods. For example, you can obtain an iterator of the users who belong to the group, etc.
comment commentid,
remoteid
Includes comment-specific data. In addition, there are some comment-specific helper methods included such as whether the comment is approved, deleted, etc.
category categoryid,
name,
remoteid,
filename,
urltitle
Includes category-specific data. This bean contains information about a category itself, and does not describe any relationships to content items themselves.
feed feedid,
name,
remoteid
Allows developers to programmatically query Mura for other beans/objects (such as content items, users, and/or custom objects), based on desired filters or search criteria.

Inspecting Available Attributes

For each of the above beans/objects, using CFML's <cfdump> tag, you can easily inspect the available attributes and their values using the code examples below.

<!--- Loading a bean/object --->
<cfset someBean = m.getBean('{someBean}').loadBy({someAttribute}={someAttributeValue})>

<!--- Assumes you have a properly loaded bean --->
<cfdump var="#someBean.getAllValues()#">

Config Bean

The configBean object includes instance-wide configuration data, primarily the data collected in the settings.ini.cfm file.

See the Mura Scope's globalConfig subscope to access the same information.

Syntax

The following examples illustrate how to "get" and/or "set" various attributes of the configBean. Use whichever syntax feels best to you.

Loading the Bean

The configBean is the only bean/object that doesn't require a call to loadBy to get a populated bean, since it's loaded when the application is initialized.

<cfset configBean = m.getBean('configBean')>

Once you have a variable to to reference the desired bean/object, you can use the example Getters and Setters below.

Getters

configBean.get('attributeName')
configBean.get{AttributeName}()
configBean.getValue('attributeName')

Setters

configBean.set('attributeName', 'someValue')
configBean.set{AttributeName}('someValue')
configBean.setValue('attributeName', 'someValue')

Note: Setting any values of the configBean are only temporary, and last for the duration of the request. In other words, calling the save method on configBean will not save any values.

Usage

Use the configBean bean/object to access global configuration attributes.

Attributes

The table below lists available attributes. For the vast majority of the attributes listed below, you may reference the settings.ini.cfm section for details.

Attribute
admindir
admindomain
adminemail
adminssl
allowautoupdates
allowedindexfiles
allowlocalfiles
allowquerycaching
allowsimplehtmlforms
allowunicodeinfilenames
applydbupdates
appreloadkey
assetdir
assetpath
autodiscoverplugins
autoresetpasswords
bcryptpasswords
broadcastappreloads
broadcastcachepurges
broadcastwithproxy
cffpconfigfilename
cfstaticjavaloaderscope
clearsessionhistory
clusteriplist
compiler
confirmsaveasdraft
contact
context
cookiedomain
cookiepath
createrequireddirectories
customurlvardelimiters
dashboard
dashboardcomments
datacollection
datasource
dbcasesensitive
dbpassword
dbschema
dbtablespace
dbtype
dbusername
debuggingenabled
defaultflatviewrange
defaultflatviewtable
duplicatetransients
emailbroadcaster
enablemuratag
encryptionkey
encryptpasswords
extensionmanager
filedelim
filedir
filestore
filestoreaccessinfo
filestoreendpoint
fmallowedextensions
fmpublicallowedextensions
forceadminssl
hashurls
hasrazuna
hstsmaxage
imageinterpolation
imagequality
indexfile
indexfileinurls
javaenabled
loadcontentby
locale
lockablenodes
logevents
loginstrikes
mailserverip
mailserverpassword
mailserverpopport
mailserversmtpport
mailserverssl
mailservertls
mailserverusername
mailserverusernameemail
managelinks
mapdir
maxarchivedversions
maxsourceimagewidth
mfaenabled
mfaperdeviceenabled
mfasendauthcode
mode
mysqlengine
notifywithversionlink
plugindir
pluginspath
postbundles
productionassetdir
productionassetpath
productiondatasource
productionfiledir
productionpushmode
productionwebroot
proxypassword
proxyport
proxyserver
proxyuser
purgecomments
purgedrafts
readonlydatasource
readonlydbpassword
readonlydbusername
requirementspath
saveemptyextendedvalues
scriptprotect
scriptprotectexceptions
securecookies
sendfrommailserverusername
serverport
sessioncookiesexpires
sessionhistory
sessiontimeout
sharableremotesessions
showadminloginhelp
sitedir
siteidinurls
skipcleanfilecache
sortpermission
strictfactory
strongpasswordregex
tempdir
title
tooltips
urltitledelim
usedefaultsmtpserver
version
webroot
webrootmap
advancedcaching
alwaysuselocalrenderer
autoupdatemode
clientmanagement
clientstorage
customtagpaths
defaultfilemode
donottrackagents
editablecomments
errortemplate
fmshowapplicationroot
fmshowsitefiles
haslockablenodes
javasettingsloadcoldfusionclasspath
javasettingsreloadonchage
javasettingswatchextensions
javasettingswatchinterval
maxportalitems
mfa
mfaperdevice
ormautomanagesession
ormcfclocation
ormdatasource
ormdbcreate
ormenabled
ormeventhandling
ormflushatrequestend
ormlogsql
ormsavemapping
ormskipcfcwitherror
ormusedbformapping
ping
recaptchalanguage
recaptchasecret
recaptchasitekey
rendermuraalerts
requesttimeout
sameformfieldsasarray
strongpasswords
testbox
usefilemode
windowdocumentdomain

Site Bean

The site bean/object includes site configuration data collected via the Site Settings section in the administration area, as well as other site-specific information.

See the Mura Scope's siteConfig subscope to access the same information when working with the site being used in the current request.

Syntax

The following examples illustrate how to "get" and/or "set" various attributes of the site bean/object. Use whichever syntax feels best to you.

Loading the Bean

You should load a site bean/object by a SiteID. You can use any SiteID you wish, or simply use the session.siteid when you wish to work with the site associated with the existing request.

<cfset siteBean = m.getBean('site').loadBy(siteid=session.siteid)>

Once you have a variable to reference the desired bean/object, you can use the example Getters and Setters below.

Getters

siteBean.get('attributeName')
siteBean.get{AttributeName}()
siteBean.getValue('attributeName')

Setters

siteBean.set('attributeName', 'someValue')
siteBean.set{AttributeName}('someValue')
siteBean.setValue('attributeName', 'someValue')

Usage

Use the site bean/object to access and/or manipulate "site" configuration attributes.

Attributes

The table below lists available attributes.

Attribute
accountactivationscript
addobjects
admanager
advertiseruserpoolid
baseid
cache
cachecapacity
cachefactories
cachefreememorythreshold
categorypoolid
columncount
columnnames
commentapprovaldefault
contact
contactaddress
contactcity
contactemail
contactname
contactphone
contactstate
contactzip
contentapprovalscript
contentcanceledscript
contentpendingscript
contentpoolid
contentrejectionscript
contentrenderer
contenttypefilepathlookup
contenttypeloopuparray
customtaggroups
datacollection
deploy
displayobjectfilepathlookup
displayobjectlookup
displayobjectloopuparray
displaypoolid
domain
domainalias
editprofileurl
emailbroadcaster
emailbroadcasterlimit
enablelockdown
enforcechangesets
enforceprimarydomain
errors
exportlocation
extendautocomplete
extenddata
extenddatatable
extendsetid
extranet
extranetpublicreg
extranetpublicregnotify
extranetssl
feedmanager
filepoolid
frommuracache
googleapikey
haschangesets
hascomments
haslockablenodes
hassharedfilepool
instanceid
isnew
isremote
javalocale
jsdatekey
jsdatekeyobjinc
jsonapi
largeimageheight
largeimagewidth
lastdeployment
locking
loginurl
mailinglistconfirmscript
mailserverip
mailserverpassword
mailserverpopport
mailserversmtpport
mailserverssl
mailservertls
mailserverusername
mailserverusernameemail
mediumimageheight
mediumimagewidth
nextn
orderno
pagelimit
placeholderimgext
placeholderimgid
primarycolumn
privateuserpoolid
publicsubmission
publicsubmissionapprovalscript
publicuserpoolid
rbfactory
recaptchalanguage
recaptchasecret
recaptchasitekey
reminderscript
remotecontext
remoteport
resourcedomain
resourcessl
sendauthcodescript
sendloginscript
showdashboard
site
siteid
sitelocale
smallimageheight
smallimagewidth
sourceiterator
subtype
tagline
theme
themelookup
themerenderer
type
usedefaultsmtpserver
usessl
viewdepth

Content Bean

The content bean/object includes data about a Mura content item, such as the title, summary, body, etc. In addition, there are several content-specific helper methods included. For example, you could get an iterator of any children of the content item, what specific categories the content item has been assigned to, etc.

See the Mura Scope's content subscope to access the same information when working with a content item being used in the current request.

Syntax

The following examples illustrate how to "get" and/or "set" various attributes of the content bean/object. Use whichever syntax feels best to you.

Loading the Bean

You can load a content bean/object by any of the following attributes:

  • contentid
  • contenthistid
  • filename
  • remoteid
  • title
  • urltitle
<cfset contentBean = m.getBean('content').loadBy(title='Home')>

Once you have a variable to reference the desired bean/object, you can use the example Getters and Setters below.

Getters

contentBean.get('attributeName')
contentBean.get{AttributeName}()
contentBean.getValue('attributeName')

Setters

contentBean.set('attributeName', 'someValue')
contentBean.set{AttributeName}('someValue')
contentBean.setValue('attributeName', 'someValue')

Usage

Use the content bean/object to access and/or manipulate a content item.

Attributes

The table below lists available attributes.

Attribute Description
active Whether the content item is active.
approvalchainoverride Whether to ignore approval chain, if applied.
approvalgroupid The current approval chain GroupID the content item is assigned to.
approvalstatus The approval status of the content item.
approved Whether the content item is approved.
assocfilename The name of the file of the primary associated image/file.
body The main content/body of the content item.
categoryid A comma-delimited list of CategoryIDs associated with the content item.
changesetid The ChangeSetID the specific content item version is associated with, if any.
childtemplate Default template to be applied to children, if any.
contenthistid A unique identifier for the specific version of the content item.
contentid A unique identifier for the content item.
created Date/time the content was originally created.
credits The credits field of the content item. Similar to a byline.
depth A numeric representation of number of levels deep the content item resides in relation to the "Home" page.
display Whether to display the content item.
displayinterval If "display" is set to "Per Schedule", a JSON-encoded string representing the scheduled display interval.
displaystart If "display" is set to "Per Schedule", the date/time to begin displaying the content item.
displaystop If "display" is set to "Per Schedule", the date/time to stop displaying the content item.
expires The content expiration date field of the content item.
featurestart If the content item is featured, the date/time to begin featuring it.
featurestop If the content item is featured, the date/time to stop being featured.
fileext The file extension of the primary associated image/file.
fileid The FileID of the primary associated image/file.
filename The URL to the content item, excluding the domain name.
filesize The file size of the primary associated image/file.
htmltitle The HTML Title field of the content item.
imageheight If the content item is a Folder, and imagesize is "custom" the height in pixels to use for images.
imagesize If the content item is a Folder, the pre-defined size to use for images (e.g., small, medium, large, custom, etc.)
imagewidth If the content item is a Folder, and imagesize is "custom", the width in pixels to use for images.
inheritobjects Whether the content item is inheriting display objects from an ancestor.
isfeature Whether the content item is featured in the section.
ishome Whether the content item is the "Home" page.
islocked Whether the content item is locked, if type is a file.
isnav Whether the content item should be shown in navigational objects generated by Mura.
isnew Whether the content item is new.
lastupdate The date/time the content item was last updated.
lastupdateby The name of the user who last updated the content item.
lastupdatebyid The UserID of the user who last updated the content item.
majorversion The major version number of the content item, if type is a file.
menutitle The Menu Title field of the content item.
metadesc The Meta Description of the content item.
metakeywords The Meta Keywords of the content item.
minorversion The Minor Version of the content item, if the type is a file.
mobileexclude Whether to exclude the content item from navigation objects on mobile devices.
moduleassign If content type is a Form or Component, a comma-delimited list of ModuleIDs of modules to allow the Form/Component to be used in. Essentially, the answer to the "Where would you like to use this Component?" question.
moduleid ModuleID of the content item.
newfile This is the field you set new primary associated files, when creating new content programatically.
nextn If content type is a Folder, the number of records to display on each page.
notes The Notes field of the content item.
objectparams The objectparams of the content item.
orderno The Order Number of the content item, if the parent is using "Manual" sort.
parentid The ContentID of the direct parent of the content item.
path A comma-delimited list of ContentIDs representing the ancestry of the content item.
releasedate The Official Release Date field of the content item.
remoteid A special field for developer's to use. May load the content item by this field. It does not have to be unique. Typically, used to store a unique identifier to associate the content item to an external data store.
remotepubdate A special field for developer's to use when importing or associating content to an external data store.
remotesource A special field for developer's to use when importing or associating content to an external data store. Typically, the name of the external data.
remotesourceurl A special field for developer's to use when importing or associating content to an external data store. Typically, the domain of the external data.
remoteurl A special field for developer's to use when importing or associating content to an external data store. Typically, the full URL to the external content.
restricted Whether the content item is restricted.
restrictgroups A comma-delimited list of GroupIDs to restrict access to.
searchexclude Whether to exclude the content item from Mura's search results.
siteid The SiteID the content item is associated with.
sortby If content type is Folder, which field to sort by.
sortdirection If content type is Folder, which direction to sort by.
status The Status of the content item (e.g., Published, Draft, etc.).
statusid The StatusID of the content item.
subtype Subtype of the content item.
summary The Summary field of the content item.
tags A comma-delimited list of default tags.
target Whether to open in a new browser window.
template The layout template applied directly to the content item. If empty, Mura will traverse up the tree until it locates an applied template, and if none are found or the applied template is not found, will use the default.cfm layout template.
title The main Title field of the content item.
type The type of the content item (e.g., Page, Folder, Calendar, Link, File, etc.)
urltitle The URL Title field of the content item.

Helper Methods

When working with content, the following content bean/object's helper methods may be quite useful.

getCategoriesIterator()

Returns an iterator of categories the content item has been associated with.

<cfset it = contentBean.getCategoriesIterator()>
<!--- Loop over the iterator --->
<cfif it.hasNext()>
  <ul>
    <cfloop condition="it.hasNext()">
      <cfset item = it.next()>
      <li>
        #esapiEncode('html', item.get('name'))#
      </li>
    </cfloop>
  </ul>
<cfelse>
  <!--- Content item has not been categorized --->
</cfif>

getKidsCategoryIterator()

Returns an iterator of categories that children of the content item have been associated with.

<cfset it = contentBean.getKidsCategoryIterator()>
<!--- Loop over the iterator --->
<cfif it.hasNext()>
  <ul>
    <cfloop condition="it.hasNext()">
      <cfset item = it.next()>
      <li>
        #esapiEncode('html', item.get('name'))#
      </li>
    </cfloop>
  </ul>
<cfelse>
  <!--- No child categories exist --->
</cfif>

getRelatedContentIterator()

Returns an iterator of related content items.

<cfset it = contentBean.getRelatedContentIterator()>
<!--- Loop over the iterator --->
<cfif it.hasNext()>
  <ul>
    <cfloop condition="it.hasNext()">
      <cfset item = it.next()>
      <li>
        <a href="#item.get('url')#">
          #esapiEncode('html', item.get('title'))#
        </a>
      </li>
    </cfloop>
  </ul>
<cfelse>
  <!--- No related content items exist --->
</cfif>

You can also optionally pass in a specific Related Content Set name.

<cfset it = contentBean.getRelatedContentIterator(name='Related Videos')>

getCommentsIterator()

Returns an iterator of comments associated with the content item.

<cfset it = contentBean.getCommentsIterator()>
<!--- Loop over the iterator --->
<cfif it.hasNext()>
  <ul>
    <cfloop condition="it.hasNext()">
      <cfset item = it.next()>
      <li>
        <div class="comments-datetime">
          #LSDateFormat(item.get('entered'))# 
          #LSTimeFormat(item.get('entered'))#
        </div>
        <div class="comments-text">
          #esapiEncode('html', item.get('comments'))#
        </div>
        <div class="comments-author">
          #esapiEncode('html', item.get('name'))#
        </div>
      </li>
    </cfloop>
  </ul>
<cfelse>
  <!--- No comments exist --->
</cfif>

User Bean

The user bean/object includes user-specific data, and includes a number of helper methods. For example, you can obtain an iterator of the groups the user belongs to, etc.

Note: The group bean/object is essentially a user bean/object. The primary difference is that the user bean/object contains only user-specific information, and user-specific helper methods. Also, the type attribute will be 1 if a group, and 2 if a user.

Syntax

The following examples illustrate how to "get" and/or "set" various attributes of the user bean/object. Use whichever syntax feels best to you.

Loading the Bean

You can load a user bean/object by any of the following attributes:

  • userid
  • username
  • remoteid
<cfset userBean = m.getBean('user').loadBy(username='steve')>

The user bean/object's loadBy method allows for an optional second argument, labeled siteid. This is necessary when working with a user assigned to a different siteid than the site you are currently working with. For example, if you are working on "Site B" and your user was created under the "default" site, then you would have to load the user similar to the example below.

<cfset userBean = m.getBean('user').loadBy(username='steve', siteid='default')>

Once you have a variable to reference the desired bean/object, you can use the example Getters and Setters below.

Getters

userBean.get('attributeName')
userBean.get{AttributeName}()
userBean.getValue('attributeName')

Setters

userBean.set('attributeName', 'someValue')
userBean.set{AttributeName}('someValue')
userBean.setValue('attributeName', 'someValue')

Usage

Use the user bean/object to access and/or manipulate a Mura user.

Attributes

The table below lists available attributes.

Attribute Description
addresses A recordset of the user's addresses.
categoryid A comma-delimited list of CategoryIDs the user has selected as interests.
company The user's company.
created Date/time the user was created.
email The user's email address.
fname The user's first name.
inactive Whether the user/group is inactive.
interests A comma-delimited list of CategoryIDs the user has selected as interests.
isnew Whether the user bean is new.
ispublic Whether the user is a Site Member (1) or System User (0).
jobtitle The user's job title.
lastlogin Date/time the user last logged in.
lastupdate Date/time the user was last updated.
lastupdateby Name of the user who last updated the user.
lastupdatebyid UserID of the user who last updated the user.
lname The user's last name.
mobilephone The user's mobile phone number.
notes The user's notes.
password The user's encrypted password.
passwordcreated Date/time the user's password was last updated.
photofileext The file extension of the user's photo.
photofileid A unique identifier for the user's photo.
primaryaddressid The unique identifier for the user's primary address.
remoteid A special field for developer's to use. May load the user by this field. It does not have to be unique. Typically, used to store a unique identifier to associate the user to an external data store.
s2 Whether the user is a "super admin".
siteid SiteID the user belongs to.
subtype The user's subtype.
type Whether the user is a group (1) or user (2).
userid A unique identifier for the user.
username The user's username.
website The user's website address.

Helper Methods

When working with users, the following user bean/object's helper methods may be quite useful.

isInGroup('{groupName}')

Checks to see if the user is a member of a specific group.

<cfif userBean.isInGroup('admin')>
  <!--- User is a member of the 'admin' group --->
<cfelse>
  <!--- User is NOT a member of the 'admin' group --->
</cfif>

getFullName()

Concatenates the user's first name and last name.

<cfoutput>
  #esapiEncode('html', userBean.getFullName())#
</cfoutput>

getMembershipsIterator()

Returns an iterator of the groups the user belongs to.

<cfset it = userBean.getMembershipsIterator()>

<h3>User Memberships</h3>
<!--- Loop over the iterator --->
<cfif it.hasNext()>
  <ul>
    <cfloop condition="it.hasNext()">
      <cfset item = it.next()>
      <li>
        #esapiEncode('html', item.get('groupname'))#
      </li>
    </cfloop>
  </ul>
<cfelse>
  <p>User does not belong to any groups.</p>
</cfif>

getMembershipsQuery()

Returns a recordset of the groups the user belongs to.

<cfset rsMemberships = userBean.getMembershipsQuery()>

getInterestGroupsIterator()

Returns an iterator of interest groups the user has been assigned to.

<cfset it = userBean.getInterestGroupsIterator()>

<h3>User Interests</h3>
<!--- Loop over the iterator --->
<cfif it.hasNext()>
  <ul>
    <cfloop condition="it.hasNext()">
      <cfset item = it.next()>
      <li>
        #esapiEncode('html', item.get('name'))#
      </li>
    </cfloop>
  </ul>
<cfelse>
  <p>User does not have any interests.</p>
</cfif>

getInterestGroupsQuery()

Returns a recordset of the groups the user belongs to.

<cfset rsInterestGroups = userBean.getInterestGroupsQuery()>

getAddressesIterator()

Returns an iterator of the user's addresses.

<cfset it = userBean.getAddressesIterator()>

<h3>User Addresses</h3>
<!--- Loop over the iterator --->
<cfif it.hasNext()>
  <ul>
    <cfloop condition="it.hasNext()">
      <cfset item = it.next()>
      <li>
        #esapiEncode('html', item.get('addressname'))#
      </li>
    </cfloop>
  </ul>
<cfelse>
  <p>User does not have any addresses.</p>
</cfif>

getAddressesQuery()

Returns a recordset of the user's addresses.

<cfset rsUserAddresses = userBean.getAddressesQuery()>

checkEmail('{emailAddress}')

Returns true if the email address being passed in is safe to use, meaning that it doesn't match any other user's email address. Otherwise, returns false, if the email address matches another Mura user account's email address.

<cfif userBean.checkEmail('steve@blueriver.com')>
  <!--- Safe to use --->
  <cfset userBean.set('email', 'steve@blueriver.com').save()>
<cfelse>
  <!--- Email address matches another user's email address --->
</cfif>

checkUsername('{username}')

Returns true if the username being passed in is safe to use, meaning that it doesn't match any other user's username. Otherwise, returns false, if the username matches another Mura user account's username.

<cfif userBean.checkUsername('steve')>
  <!--- Safe to use --->
  <cfset userBean.set('username', 'steve').save()>
<cfelse>
  <!--- Username matches another user's username --->
</cfif>

Group Bean

The group bean/object includes group-specific data, and includes a number of helper methods. For example, you can obtain an iterator of the users who belong to the group, etc.

Note: The group bean/object merely uses the user bean/object. However, it contains only group-specific information, and group-specific helper methods.

Syntax

The following examples illustrate how to "get" and/or "set" various attributes of the group bean/object. Use whichever syntax feels best to you.

Loading the Bean

You can load a group bean/object by any of the following attributes:

  • groupid
  • groupname
  • remoteid
<cfset groupBean = m.getBean('group').loadBy(groupname='admin')>

Note: As mentioned in the previous note above, the group bean/object leverages the user bean/object. The type attribute will be 1 if a group, and 2 if a user.

The group bean/object's loadBy method allows for an optional second argument, labeled siteid. This is necessary when working with a group assigned to a different siteid than the site you are currently working with. For example, if you are working on "Site B" and your group was created under the "default" site, then you would have to load the user similar to the example below.

<cfset groupBean = m.getBean('group').loadBy(groupname='admin', siteid='default')>

Once you have a variable to reference the desired bean/object, you can use the example Getters and Setters below.

Getters

groupBean.get('attributeName')
groupBean.get{AttributeName}()
groupBean.getValue('attributeName')

Setters

groupBean.set('attributeName', 'someValue')
groupBean.set{AttributeName}('someValue')
groupBean.setValue('attributeName', 'someValue')

Usage

Use the group bean/object to access and/or manipulate a Mura group.

Attributes

The table below lists available attributes.

Attribute Description
created Date/time the user was created.
email The groups's email address.
groupid The GroupID is a unique identifier for the group.
groupname The name of the group.
inactive Whether the group is inactive.
isnew Whether the group bean is new.
ispublic Whether the group is a Member Group (1) or System Group (0).
lastupdate Date/time the group was last updated.
lastupdateby Name of the user who last updated the user.
lastupdatebyid UserID of the user who last updated the user.
remoteid A special field for developer's to use. May load the group by this field. It does not have to be unique. Typically, used to store a unique identifier to associate the group to an external data store.
siteid SiteID the group belongs to.
subtype The group's subtype.
tablist A comma-delimited list of content editing tabs the group has access to.
type Whether the user is a group (1) or user (2).

Helper Methods

When working with groups, the following group bean/object's helper methods may be quite useful.

getMembersIterator()

Returns an iterator of the users who belong to the group.

<cfset it = groupBean.getMembersIterator()>

<h3>Group Members</h3>
<!--- Loop over the iterator --->
<cfif it.hasNext()>
  <ul>
    <cfloop condition="it.hasNext()">
      <cfset user = it.next()>
      <li>
        #esapiEncode('html', user.getFullName())#
      </li>
    </cfloop>
  </ul>
<cfelse>
  <p>Group does not have any members.</p>
</cfif>

getMembersQuery()

Returns a recordset of the users who belong to the group.

<cfset rsMembers = groupBean.getMembersQuery()>

Comment Bean

The comment bean/object includes comment-specific data. In addition, there are some comment-specific helper methods included such as whether the comment is approved, deleted, etc.

Syntax

The following examples illustrate how to "get" and/or "set" various attributes of the comment bean/object. Use whichever syntax feels best to you.

Loading the Bean

Load a comment bean/object by any of the following attributes:

  • commentid
  • remoteid
<cfset commentBean = m.getBean('comment').loadBy(commentid='{UUID}')>

Once you have a variable to reference the desired bean/object, you can use the example Getters and Setters below.

Getters

commentBean.get('attributeName')
commentBean.get{AttributeName}()
commentBean.getValue('attributeName')

Setters

commentBean.set('attributeName', 'someValue')
commentBean.set{AttributeName}('someValue')
commentBean.setValue('attributeName', 'someValue')

Usage

Use the comment bean/object to access and/or manipulate a comment.

Attributes

The table below lists available attributes.

Attribute Description
commentid A unique identifier for the comment.
comments The text of the comments.
contentid ContentID that the comment is associated with.
email Email address of the commenter.
entered Date/time of the comment.
flagcount Number of times the comment has been flagged as spam.
ip Internet Protocol (IP) address of the commenter.
isapproved Whether the comment is approved.
isdeleted Whether the comment is deleted.
isnew Whether the comment is new.
isspam Whether the comment is marked as spam.
kids Number of child comments.
name Name of the commenter.
parentid If the comment is a child, the ContentID of the parent comment.
path A comma-delimited path of ContentIDs to identify the ancestry of the comment.
siteid SiteID the comment belongs to.
subscribe Whether the commenter subscribed to future comments.
url URL the commenter entered.
userid UserID of the user who entered the comment.

Category Bean

The category bean/object includes category-specific data. This bean/object contains information about a category itself, and does not describe any relationships to content items themselves.

Syntax

The following examples illustrate how to "get" and/or "set" various attributes of the category bean/object. Use whichever syntax feels best to you.

Loading the Bean

Load a category bean/object by any of the following attributes:

  • categoryid
  • name
  • remoteid
  • filename
  • urltitle
<cfset categoryBean = m.getBean('category').loadBy(name='Mura CMS')>

Once you have a variable to reference the desired bean/object, you can use the example Getters and Setters below.

Getters

categoryBean.get('attributeName')
categoryBean.get{AttributeName}()
categoryBean.getValue('attributeName')

Setters

categoryBean.set('attributeName', 'someValue')
categoryBean.set{AttributeName}('someValue')
categoryBean.setValue('attributeName', 'someValue')

Usage

Use the category bean/object to access and/or manipulate a category.

Attributes

The table below lists available attributes.

Attribute Description
categoryid A unique identifier for the category.
datecreated Date/time the category was created.
filename URL-friendly path of the category.
isactive Whether the category is active.
isfeatureable Whether the category is able to be featured.
isinterestgroup Whether the category is an interest group.
isnew Whether the category is new.
isopen Whether the category allows content assignments.
lastupdate Date/time the category was last updated.
lastupdateby Name of the user who last updated the category.
name Name/title of the category.
notes Notes for the category.
parentid If category is a child, the CategoryID of the parent category.
path A comma-delimited path of CategoryIDs to identify the ancestry of the category.
restrictgroups A comma-delimited list of groups allowed to assign content to the category.
siteid SiteID of the category.
urltitle URL-friendly title/name of the category.

Feed Bean

The feed bean/object allows developers to programmatically query Mura for other beans/objects. Initially, many developers query for content beans/objects. In essence, when you search for content items using this method, you're creating a Collection dynamically. However, you may also query for user beans/objects, and as you'll see in the Custom Objects section, any other kind of Mura object or custom object you wish.

In short, the feed bean/object to obtains a collection of beans/objects, based on desired filters/criteria.

Note: Once the feed bean/object has been populated, most developers will call either the getIterator or getQuery methods to work with the collection of beans/objects. See the Key Methods area below for details. Please visit the Mura Iterators section, for more information on iterators.

Syntax

There are a couple of ways to obtain a feed bean/object, and each of those are described below.

loadBy

The first way for obtaining a feed bean/object is by using the common loadBy method. When loading it this way, keep in mind you will only be working with content beans/objects.

If you wish to load an existing Collection created via Mura's Admin UI, you can load a feed bean/object by any of the following attributes:

  • feedid
  • name
  • remoteid
<cfset feedBean = m.getBean('feed').loadBy(name='{Collection Name Goes Here}')>

getFeed

The primary method for obtaining a feed bean/object, is to use your desired bean's/object's getFeed method. You can either call getFeed on your desired bean/object itself, or pass in the name of the bean as an argument to the getFeed method, as shown in the examples below.

Content Feed
feedBean = m.getBean('content').getFeed();
// or
feedBean = m.getFeed('content');
User Feed
feedBean = m.getBean('user').getFeed();
// or
feedBean = m.getFeed('user');
Custom Object
feedBean = m.getBean('yourCustomObject').getFeed();
// or
feedBean = m.getFeed('yourCustomObject');

Usage

Use the feed bean/object to create, access, and/or manipulate a collection of beans/objects.

Key Methods

Since a feed bean/object is essentially an easy way to query Mura for objects, the key methods for working with feeds share a similarity with conventional SQL syntax, but in a way that's more comfortable for developers. Also, as shown in the examples below, Mura allows for method chaining. The methods below also apply to Mura ORM Feeds, and are also exposed via the Mura.js API.

Method Parameter(s) Description
where property Analogous to SQL "WHERE" clause. If property parameter is empty, the method is used for readability purposes only. You may optionally provide a property argument, in which case Mura will treat it as andProp.
prop property Analogous to SQL "AND" clause. The property argument is the attribute to use as the left operand value of a SQL WHERE condition.
andProp property Analogous to SQL "AND" clause. The property argument is the attribute to use as the left operand value of a SQL WHERE condition.
orProp property Analogous to SQL "OR" clause. The property argument is the attribute to use as the left operand value of a SQL WHERE condition.
null n/a Analogous to SQL "IS NULL" operator. Checks if the preceding property value is null.
isEQ criteria Analogous to SQL "=" comparison operator. Checks if the preceding property value is "equal to" the criteria argument.
isNEQ criteria Analogous to SQL "<>" or "!=" comparison operator. Checks if the preceding property value is "not equal to" the criteria argument.
isLT criteria Analogous to SQL "<" comparison operator. Checks if the preceding property value is "less than" the criteria argument.
isLTE criteria Analogous to SQL "<=" comparison operator. Checks if the preceding property value is "less than or equal to" the criteria argument.
isGT criteria Analogous to SQL ">" comparison operator. Checks if the preceding property value is "greater than" the criteria argument.
isGTE criteria Analogous to SQL ">=" comparison operator. Checks if the preceding property value is "greater than or equal to" the criteria argument.
isIn criteria Analogous to SQL "WHERE IN" clause. Checks if the preceding property value is in the list of literal values that have been specified as the criteria argument. Shorthand for multiple OR conditions.
isNotIn criteria Similar to the isIn method. Checks if the preceding property value is NOT in the list of literal values that have been specified as the criteria argument. Shorthand for multiple AND x NEQ conditions.
containsValue criteria Analogous to SQL "CONTAINS" comparison operator. Checks if the preceding property value "contains" the criteria argument.
beginsWith criteria Analogous to SQL "LIKE" comparison operator. Checks if the preceding property value "begins with" the criteria argument. May use wildcards (e.g., % or _, etc.)
endsWith criteria Analogous to SQL "LIKE" operator. Checks if the preceding property value "ends with" the criteria argument. May use wildcards (e.g., % or _, etc.)
openGrouping n/a Analogous to SQL "AND (" logical operator. Creates an "AND" with a parenthesis to form complex expressions. See grouping example below.
andOpenGrouping n/a Analogous to SQL "AND (" logical operator. Creates an "AND" with a parenthesis to form complex expressions. See grouping example below.
orOpenGrouping n/a Analogous to SQL "OR (" logical operator. Creates an "OR" with a parenthesis to form complex expressions. See grouping example below.
closeGrouping n/a Analogous to SQL ")" logical operator. Creates a closing parenthesis to preceding "opening" statements for complex expressions. See grouping example below.
sort property, direction Analogous to SQL "ORDER BY" clause. Allows sorting by one or more properties, and may be returned in ascending (asc) or descending (desc) order.
itemsPerPage itemsPerPage Expects a number to set the NextN attribute of the recordset. Set to zero (0) to include all items on one page.
maxItems maxItems Analogous to SQL "TOP" or "LIMIT" clause. Used to limit the number of records to retrieve. Set to zero (0) to retrieve all records. The default setting is twenty (20) for content beans/objects.
includeHomePage boolean *Only applies to Mura content.
Set to true to include the Home Page.
showNavOnly boolean *Only applies to Mura content.
Set to true to include only content items flagged as "Include in Site Navigation".
showExcludeSearch boolean *Only applies to Mura content.
Set to true to include content items flagged as "Exclude from Site Search".
isFeaturesOnly boolean *Only applies to Mura content.
Set to true to only search for content items flagged as "Feature in this section?".
contentPools criteria *Only applies to Mura content.
A comma-delimited list of SiteIDs to search for content items.
getIterator   Returns an iterator object. Please visit the Mura Iterators section, for more information on iterators.
Note: This method is not exposed or available via the Mura.js API. Use getQuery instead.
getQuery countOnly Returns a query object. Optionally pass in the countOnly argument, to obtain only a count of the records that match your filter criteria.
aggregate type, property

Use this method to aggregate queries/recordsets. This method helps by eliminating the need to write custom helper methods with queries against Mura ORM generated schema. See examples below.

Valid type options:

  • count
  • sum
  • min
  • max
  • avg
  • groupby

 

Examples

Note: When using m.getFeed('content'), unless you specify otherwise, Mura will only search for content items that are active, approved, set to appear in navigation, not excluded from search results, currently on display, exclude the home page, and limited to the top 20 records.

Also, when using m.getFeed('user'), by default, Mura will only search for Site Members (isPublic=1). To search for System Users, add .setIsPublic(0) to your method chain.

prop & andProp

This example demonstrates the use of the prop and andProp methods.

feedBean = m.getFeed('content')
            .where()
            .prop('title')
            .isEQ('About Us')
            .andProp('siteid')
            .isEQ('default');

// Analogous SQL
SELECT *
FROM tcontent
WHERE title = 'About Us'
  AND siteid = 'default';

orProp

This example demonstrates the use of the prop method.

feedBean = m.getFeed('content')
            .where()
            .prop('title')
            .isEQ('About Us')
            .orProp('title')
            .isEQ('News');

// Analogous SQL
SELECT *
FROM tcontent
WHERE title = 'About Us'
   OR title = 'News';

Sort

This example demonstrates how to obtain a feed bean/object of Mura content items. It is searching for any content where the "title" attribute is equal to "News" or "Blog" and will sort the results by title, descending.

contentFeed = m.getFeed('content')
               .where()
               .prop('title')
               .isEQ('News')
               .orProp('title')
               .isEQ('Blog')
               .sort('title', 'desc');

// Analogous SQL
SELECT *
FROM tcontent
WHERE title = 'News'
   OR title = 'Blog'
ORDER BY title DESC;

getIterator

Returns an iterator object.

<cfset it = feedBean.getIterator()>
<!--- Loop over the iterator --->
<cfif it.hasNext()>
  <ul>
    <cfloop condition="it.hasNext()">
      <cfset item = it.next()>
      <li>
        <cfdump var="#item.getAllValues()#">
      </li>
    </cfloop>
  </ul>
<cfelse>
  <!--- Feed has no items --->
</cfif>

getQuery

Returns a query object.

rsObjects = feedBean.getQuery();

contentPools

This example demonstrates how to search more than one site for content items, under the same instance of Mura.

feedBean = m.getFeed('content')
            .where()
            .prop('credits')
            .containsValue('Steve')
            .contentPools('site1,site2,site3');

// Analogous SQL
SELECT *
FROM tcontent
WHERE credits LIKE '%Steve%'
  AND siteid IN ('site1','site2','site3');

Grouping Example

This example demonstrates how to use the openGrouping, orOpenGrouping, and closeGrouping methods.

feedBean = m.getFeed('content')
            .where()
            .prop('credits')
            .containsValue('Steve')
            .openGrouping()
              .prop('title')
              .beginsWith('Why')
            .closeGrouping()
            .orOpenGrouping()
              .prop('title')
              .isEQ('News')
            .closeGrouping();

// Analogous SQL
SELECT *
FROM tcontent
WHERE credits LIKE '%Steve%'
  AND (
    title LIKE 'Why%'
  ) OR (
    title = 'News'
  );

Searching for Super Users

This example demonstrates how to search for "super" users. Remember, Mura only searches Site Members by default, so we have to use .setIsPublic(0) to search for System Users.

feedBean = m.getFeed('user')
            .where()
            .prop('s2')  // if s2=1, then they're a super user
            .isEQ(1)
            .setIsPublic(0);

// Analogous SQL
SELECT *
FROM tusers
WHERE s2 = 1
  AND ispublic = 0;

Searching for Custom Objects

This example demonstrates how to search for a custom object. See the Custom Objects section for more information.

feedBean = m.getFeed('yourCustomObject')
            .where()
            .prop('someProperty')
            .isEQ('someValue');

// Analogous SQL
SELECT *
FROM yourCustomObjectTable
WHERE someProperty = 'someValue';

Aggregate Examples

This example illustrates how to aggregate a fictitious "price" property of a "widget" object

feedBean = m.getFeed('widget')
            .aggregate('count', '*')
            .aggregate('sum', 'price')
            .aggregate('min', 'price')
            .aggregate('max', 'price')
            .aggregate('avg', 'price');

// get the query/recordset result
rsAggregatedWidgets = feedBean.getQuery();

// get an iterator of aggregated data
// NOTE: the beans in this iterator will be generic, and not actual widget beans
itAggregatedWidgets = feedBean.getIterator();

Mura Scope Objects

As noted in the Mura Scope section, in addition to the CFML scopes, Mura has its own scope called the Mura Scope. The Mura Scope also has a number of subscope objects, that are essentially wrappers of some of Mura's most common bean objects. In short, they are pre-populated beans, with their own special helper methods. The table below contains details on each of the Mura Scope's subscope objects.

Object Analogous Bean Description
m.globalConfig() configBean Wraps the configBean of the Mura instance in the current request. Includes instance-wide configuration data, such as data collected in the settings.ini.cfm file.
m.siteConfig() site Wraps the site bean of the site in the current request. Includes site configuration data collected via the Site Settings section in the administration area, as well as other useful site-specific information.
m.content() content Wraps the content bean of the content item in the current request. Includes data about a Mura content item, such as the title, summary, body, etc. In addition, there are several content-specific helper methods included. For example, you could get an iterator of any children of the content item, what specific categories the content item has been assigned to, etc.
m.currentUser() user* Wraps the sessionUserFacade for the user initiating the request. The object also has access to the user bean attributes, as well as some special session-specific data, such as whether the current user is logged in, whether the current user is a super user or admin user, etc.
m.event() n/a See the Mura "event" Scope section for details.

Inspecting Available Attributes

For each of the above subscope objects, using CFML's <cfdump> tag, you can easily inspect the available attributes and their values using the code examples below.

<cfdump var="#m.{subscope}().getAllValues()#">

<!--- For example --->
<cfdump var="#m.globalConfig().getAllValues()#">
<cfdump var="#m.siteConfig().getAllValues()#">
<cfdump var="#m.content().getAllValues()#">
<cfdump var="#m.currentUser().getAllValues()#">
<cfdump var="#m.event().getAllValues()#">
<!--- etc. --->

globalConfig

The globalConfig is a wrapper of Mura's configBean object, and is a subscope object of the Mura Scope. It includes instance-wide configuration data, such as data collected in the settings.ini.cfm file.

Syntax

The following examples illustrate how to "get" and/or "set" various attributes of the globalConfig. Use whichever syntax feels best to you.

Getters

m.globalConfig('attributeName')
m.globalConfig().get('attributeName')
m.globalConfig().get{AttributeName}()
m.globalConfig().getValue('attributeName')

Setters

m.globalConfig('attributeName', 'someValue')
m.globalConfig().set('attributeName', 'someValue')
m.globalConfig().set{AttributeName}('someValue')
m.globalConfig().setValue('attributeName', 'someValue')

Note: Setting any values with globalConfig are only temporary, and last for the duration of the request. In other words, calling the save method on globalConfig will not save any values.

Returns

A populated configBean object.

Usage

Use this scope to access global configuration attributes.

Attributes

See the Config Bean section for details on available attributes.

siteConfig

The siteConfig is a wrapper of Mura's site bean object, and is a subscope object of the Mura Scope. It includes site configuration data collected via the Site Settings section in the administration area, as well as other site-specific information.

Syntax

The following examples illustrate how to "get" and/or "set" various attributes of the siteConfig. Use whichever syntax feels best to you.

Getters

m.siteConfig('attributeName')
m.siteConfig().get('attributeName')
m.siteConfig().get{AttributeName}()
m.siteConfig().getValue('attributeName')

Setters

m.siteConfig('attributeName', 'someValue')
m.siteConfig().set('attributeName', 'someValue')
m.siteConfig().set{AttributeName}('someValue')
m.siteConfig().setValue('attributeName', 'someValue')

Returns

A populated site bean/object.

Usage

Use this scope to access "site" configuration attributes.

Attributes

See the Site Bean section for details on available attributes.

content

The content object is a wrapper of Mura's content bean object, and is a subscope object of the Mura Scope. It wraps the content bean of the content item being served in the current request. It includes data about a Mura content item, such as the title, summary, body, etc. In addition, there are several content-specific helper methods included. For example, you could get an iterator of direct children of the content item, what specific categories the content item has been assigned to, etc.

Syntax

The following examples illustrate how to "get" and/or "set" various attributes of the content object. Use whichever syntax feels best to you.

Getters

m.content('attributeName')
m.content().get('attributeName')
m.content().get{AttributeName}()
m.content().getValue('attributeName')

Setters

m.content('attributeName', 'someValue')
m.content().set('attributeName', 'someValue')
m.content().set{AttributeName}('someValue')
m.content().setValue('attributeName', 'someValue')

Returns

A populated content bean/object.

Usage

Use this scope to access information about the content item being served in the current request.

Attributes

See the Content Bean section for details on available attributes.

Helper Methods

See the Content Bean section for details on available helper methods..

currentUser

The currentUser object is a wrapper of the sessionUserFacade, and is a subscope object of the Mura Scope. Under the hood, Mura allows for access to the user bean/object attributes, as well as some special session-specific data, such as whether the current user is logged in, whether the current user is a super user or admin user, etc.

Syntax

The following examples illustrate how to "get" and/or "set" various attributes of the sessionUserFacade bean/object. Use whichever syntax feels best to you.

Getters

m.currentUser('attributeName')
m.currentUser().get('attributeName')
m.currentUser().get{AttributeName}()
m.currentUser().getValue('attributeName')

Setters

m.currentUser('attributeName', 'someValue')
m.currentUser().set('attributeName', 'someValue')
m.currentUser().set{AttributeName}('someValue')
m.currentUser().setValue('attributeName', 'someValue')

Returns

A populated sessionUserFacade bean/object.

Usage

Use this scope to access information about the user of the site in the current request.

Attributes

See the User Bean section for details on available attributes.

Helper Methods

In addition to the user bean/object's helper methods, the currentUser object has some special helper methods of its own.

isLoggedIn()

Returns true if the user is logged in. Otherwise, returns false if the user is logged out.

<cfif m.currentUser().isLoggedIn()>
  <!--- User IS logged in --->
  <h3>Hello, #esapiEncode('html', m.currentUser('fname'))#!</h3>
<cfelse>
  <!--- Use is NOT logged in --->
  <h3>Who are you?</h3>
</cfif>

isSuperUser()

Returns true if the user is a "super" user. Otherwise, returns false.

<cfif m.currentUser().isSuperUser()>
  <!--- User IS a 'super' user --->
<cfelse>
  <!--- Use is NOT a 'super' user --->
</cfif>

isAdminUser()

Returns true fi the user is an "admin" user. Otherwise, returns false.

<cfif m.currentUser().isAdminUser()>
  <!--- User IS an 'admin' user --->
<cfelse>
  <!--- Use is NOT an 'admin' user --->
</cfif>

Mura ORM

Before you proceed through this section, it's important to have at least a basic understanding of object-oriented programming (OOP) and its principles. It would be wise to take advantage of the plethora of resources, available both online and offline, before you continue in this section. Additionally, having a solid understanding of relational database management systems (RDBMSs) is extremely helpful when working through this section.

Object-Relational Mapping (ORM)

Since relational database management systems (RDBMSs) don't store objects directly, object-relational mapping (ORM), attempts to bridge the world to object-oriented programming (OOP). According to Wikipedia, ORM addresses the main issue for OOP developers who work with RDBMSs, by "translating the logical representation of objects into an atomized form capable of being stored in a database, while preserving the properties of objects and their relationships so they can be reloaded as objects when needed." In other words, ORM essentially creates a "virtual object database" that can be used by programmers.

What is Mura ORM?

Mura ORM is essentially a "virtual object database" that can be used by Mura developers. It allows developers the ability to create their own objects, and work with them in the same ways Mura developers can work with Mura's bean objects. This means developers don't have to worry about what kind of database Mura is running on, or write a ton of SQL to perform many of the most mundane tasks such as creating, reading, updating, or deleting objects and their associated database records.

While comparable in nature to ColdFusion ORM, Mura ORM is not exactly the same. However, if you've worked with ColdFusion ORM before, you will definitely find many similarities with Mura ORM.

Mura ORM Configuration

Mura employs a "convention over configuration" design paradigm for registering ORM objects/entities. So, instead of creating configuration files to describe the details of where your objects/entities reside, and how to instantiate them, Mura uses a convention-based approach. In other words, by placing your objects/entities in specifically named directories, you don't have to manually or explicitly register your entities.

Model Directory

Under the hood, Mura simply leverages DI/1, a simple convention-based dependency injection (inversion of control) framework. As such, any .CFC discovered under the "../model" directories identified below will automatically get registered with DI/1.

  • {context}/themes/{ThemeName}/model/
  • {context}/themes/{ThemeName}/content_types/{Type}/model/
  • {context}/themes/{ThemeName}/content_types/{Type}_{Subtype}/model/
  • {context}/themes/{ThemeName}/modules/{Module}/model/
  • {context}/sites/{SiteID}/model/
  • {context}/sites/{SiteID}/themes/{ThemeName}/model/
  • {context}/sites/{SiteID}/themes/{ThemeName}/content_types/{Type}/model/
  • {context}/sites/{SiteID}/themes/{ThemeName}/content_types/{Type}_{Subtype}/model/
  • {context}/sites/{SiteID}/themes/{ThemeName}/modules/{Module}/model/

You may also register any directory you want by using m.globalConfig().registerModelDir({dir}). The "dir" parameter is a logical path to a CFML directory.

This is typically done in the onApplicationLoad event. This is useful for plugins, since Mura does not automatically scan for a "model" directory in plugins, since some plugin developers may be using their own bean factory, and do not need Mura to auto-register them.

public any function onApplicationLoad(m) {
  // This is how you could register a 'model' directory in a plugin
  arguments.m.globalConfig().registerModelDir('/pluginName/path/to/your/model/');
}

All registered objects/entities are essentially "global" in the sense that they'll all use the same underlying database tables. So, you'll want to avoid describing the same entities in multiple sites. We'll cover more about entities in the Mura ORM Entity section.

The "beans" Directory

Any .CFC's discovered under "../model/beans/" will be registered as "transient" objects, not as "singleton" objects.

  • Transient objects exist only for a request and then are discarded. In other words, you et a new copy every time.
  • Singleton objects are shared among all threads and requests. In other words, there's only one of them, and you're not getting a new copy every time. These are great for "service" type objects.

The examples below show how you could register multiple transient objects.

  • ../model/beans/person.cfc
  • ../model/beans/personaddress.cfc
  • ../model/beans/personphonenumber.cfc

The "handlers" Directory

Any .CFC's discovered under a "../handlers/" directory within a "model" directory, will be registered as custom event handlers. The examples below show how you could register multiple custom event handlers, in various locations.

  • ../model/handlers/myhandler.cfc
  • ../model/services/handlers/myhandler.cfc

Registration

In order for Mura to discover these directories, and any subsequent changes you make to the directories or files contained within them, you'll need to reload Mura.

Also, the first time you reload, and anytime you add new properties to your entities, you'll need to append "&applydbupdates" to the URL in order for Mura to parse your objects/entities and make the appropriate changes to the database schema.

Mura ORM Entity

In a nutshell, a Mura ORM entity is described by creating a ColdFusion component (.CFC). Mura leverages some of the standard attributes of a Mura ORM component, and also utilizes some custom attributes, as outlined in the table below.

Attribute Req/Opt Default Description
bundleable Optional true If false, entities will not be included in any bundles.
cachename Optional You may optionally supply a custom cache name to use when being stored in Mura's cache factory.
datasource Optional m.globalConfig('datasource') If you wish to use a datasource that's different from the one Mura uses by default, you may supply one here.
dbtype Optional

If specifying a datasource attribute, specify the database type. Valid options include:

  • mysql
  • mssql
  • oracle
  • postgres
discriminatorcolumn Optional empty string You may specify a property to filter on by default.
discriminatorvalue Optional empty string If using a discriminatorcolumn, you may specify the property's criteria to filter on by default.
extends Required mura.bean.beanORM This attribute should always be present, and should always extend mura.bean.beanORM or mura.bean.beanORMHistorical
entityname Required This is the name of the object/entity.
manageschema Optional true If false, Mura will not manage the underlying table for the entity, and you are assuming all responsibility for any schema changes.
orderby Optional You may pass a comma-delimited list of properties to sort by.
public Optional false If true, allows the entity to be used via the JSON API by non-administrative users. If false, users must be logged in, and have access to the specific site in order to work with the entity.
readonly Optional false If true, Mura will not allow any new records to be added to the database, or any updates to existing records in the database.
scaffold Optional false If true, allows the entity to be managed via the Mura ORM Scaffolder.
table Required Specify the name to use for a database table to store data about this entity.
usetrash Optional true If false, entities will not be moved to the Trash Bin, and will be hard deleted instead.

Example Mura ORM Component

component 
  extends="mura.bean.beanORM"
  entityname="something"
  table="somethings"
  bundleable="true"
  displayname="SomethingBean"
  public=true
  orderby="{someproperty}" {

  // Properties & Methods

}

Mura ORM Component Properties

To describe the various properties/attributes and relationships of Mura ORM entities, use CFML properties. The following table describes the available attributes for Mura ORM component properties.

Attribute Req/Opt Default Description
cascade Optional If the property is a related entity and the value is "delete", when the parent entity/object is deleted, any related entities will also be deleted.
cfc Optional If the property is a related entity, set to the related entity's "entityname" value.
comparable Optional true If false, the property will not be included in entity comparisons. For example:
entityDifferences = entityA.compare(entityB);
datatype Optional varchar

Valid options are:

  • datetime
  • int
  • text
  • tinyint
  • smallint
  • varchar
  • char
  • number
  • boolean
  • float
  • double
  • blob
default Optional If the required attribute is set to "no", or not specified, you may supply a default value to use.
fieldtype Optional

If property is a primary key, you should set fieldtype to "id". Mura will then create a UUID for each new object stored in the database.

If property is a related entity, you may specify the relationship type here. The left side of the "to" types listed below is the parent entity, and the right side of the "to" is the related entity. So, for example "one-to-many" would mean one parent entity could have many related entities, and so on. Valid options are:

  • one-to-one
  • one-to-many
  • many-to-one
  • many-to-many
  • index (sets a non-unique index on the column in the underlying database)
fkcolumn Optional

If the property is a related entity, you may specify the attribute of the entity to store in this field. This is useful when creating relationships to Mura's entities. For example:

property name="site" 
         cfc="site" 
         fieldtype="many-to-one" 
         fkcolumn="siteid";
html Optional false

This property is parsed only if stricthtml=true in the settings.ini.cfm file.

If true, Mura will allow HTML in the string to be saved. If false, Mura will return an error if the string contains HTML when being saved.

length Optional 50 If the datatype attribute is set to "varchar" or "char", you may specify the maximum number of characters to allow.
loadkey Optional If the property is a related entity, you may specify the attribute of the entity to load by.
message Optional If required, or fails validation, the error message to return to the user.
relatesto Optional Alias for "cfc"
name Required Name of the property.
nullable Optional false If true, empty values will be stored as NULL.
orderby Optional If property is a related entity, you may specify the related entity's property you wish to sort by.
persistent Optional true If false, a column in the related table will not be created for the property.
required Optional false If true, an error will be sent to the user if a value is not set.
singluarname Optional

If the property is a related entity, this allows you to use a singular name in lieu of the property name itself. For example:

property name="addresses" 
         singularname="address" 
         cfc="address" 
         fieldtype="one-to-many";
validate Optional

If you desire the entity to be validated before saving, you may use one of the following options:

  • date
  • datetime
  • numeric
  • email
  • regex
  • url

Validation Attributes

If property has a "validate" attribute, you may include additional attributes to the property for validation.

Attribute Description
regex The regular expression to use for validation.
minvalue The minimum value to allow.
maxvalue The maximum value to allow.
minlength The minimum length to allow.
maxlenth The maximum length to allow.
inlist The list to compare against.
method A custom entity method to execute.
lte Less than or equal to.
lt Less than.
gte Greater than or equal to.
gt Greater than.
eq Equal to.
neq Not equal to.

The example below illustrates how to use a validation attribute, when a property has a "validate" attribute.

property name="year" datatype="int" validate="numeric" minvalue="1900" maxvalue="2017";

Related Entity Synthesized Methods

If the property is a related entity, and the fieldtype attribute is either "one-to-many" or "many-to-many", the following methods are automatically available to the entity itself. The methods below are executed for both the property's name and singularname.

Method Description
get{PropertyName}Iterator Returns an iterator of related entities. For example: entity.getAddressesIterator()
get{PropertyName} Under the hood, returns get{PropertyName}Iterator.
get{PropertyName}Query Returns a recordset/query of the related entities. For example: entity.getAddressesQuery()
has{PropertyName} Returns a recordcount of related entities. Useful for if/else statements.
add{PropertyName} Pass in the object/entity to save as a related entity, and Mura will automatically save the related entity when executing the call to entity.save(). Useful for adding multiple related entities. For example: entity.addPhoneNumber(phoneNumberObject)
remove{PropertyName} Pass in the object/entity to be deleted, and Mura will automatically delete the related entity when executing the call to entity.save(). For example: entity.removePhoneNumber(phoneNumberObject)

If the property is a related entity, and the fieldtype attribute is either "one-to-one" or "many-to-one", the following methods are automatically available to the entity itself.

Method Description
get{PropertyName} Returns the related entity. For example: entity.getEmergencyContact()
add{PropertyName} Pass in the object/entity to save as a related entity, and Mura will automatically save the related entity when executing the call to entity.save(). For example: entity.addEmergencyContact(emergencyContactObject)
remove{PropertyName} Pass in the object/entity to be deleted, and Mura will automatically delete the related entity when executing the call to entity.save(). For example: entity.removeEmergencyContact(emergencyContactObject)

Example Mura ORM Component With Properties

The following code defines an entity named "something" that contains a few basic properties.

component 
  extends="mura.bean.beanORM"
  entityname="something"
  table="somethings"
  bundleable="true"
  displayname="SomethingBean"
  public=true
  orderby="namefirst,namelast" {

  // primary key
    property name="somethingid" fieldtype="id";

  // attributes
    property name="namefirst" datatype="varchar" length="255" required=true;
    property name="namelast" datatype="varchar" length="255" required=true;

  // methods could go here
}

Working With Entities

The code examples below demonstrate various methods for working with entities. As you'll see, the syntax is no different than working with any other Mura bean object.

// Loading entities
entity = m.getBean('entityName')
          .loadBy(someAttribute='Something');

// Getting attributes
entity.get('attributeName');

// Setting attributes
entity.set('attributeName', 'Some Value')

// Deleting entities
entity.delete();

// Saving entities
entity.save();

// Get a feed of entites
m.getFeed('entityName');

Error Handling

As previously mentioned, working with entities is no different than working with any other Mura bean object. We've already covered a bit of error handling, but it doesn't hurt to review it once again here.

Errors are returned as a structure, where each key in the struct represents the object's attribute "name", and the value contains the error message itself.

entity.save();

// If the bean has errors, then it did not save...
if ( entity.hasErrors() ) {

  // errors are returned as a struct
  errors = entity.getErrors();

  WriteOutput('<h3>Please review the following errors:</h3><ul>');

  // Loop over the errors
  for ( e in errors ) {
    WriteOutput('<li>Attribute: #e# <br> Message: #errors[e]#</li>');
  }

  WriteOutput('</ul>');
} else {
  WriteOutput('<h2>Success :: No Errors!</h2>');
}

Custom Validation

Mura also allows you to include your own custom validation rules. In order to do so, you simply include a custom validate() method in your entity's component file (.CFC), and when a save() call is made on your entity, it will trigger your custom method. The key is you don't "return" anything, you simply add your custom errors to the existing errors struct.

You could also use this to do complex validation requirements. For example, maybe you have a custom radio button or checkbox, and when it's selected, your form displays additional form fields that you will then want to be "required". You could simply check for the existence and/or value of the triggering field, and if it exists, run through each of the additional required form fields in your own validation.

The example below demonstrates how to create your own error messages when validating an attribute of your entity.

public any function validate() {

  // This runs the standard validation ... 
  // which will use your property definition's "validate" requirements, etc.
  // and it also gives you a way to reference the entity object itself
  var obj = super.validate();

  // Obtain a reference to any errors
  var errors = obj.getErrors();

  // Now, you can check anything you want on your properties or entity attributes
  // For example, if you have a property labeled "wantsmoreoptions" and it's true,
  // You could check for the other fields
  if ( obj.get('wantsmoreoptions') ) {

    // Yes, they want more options!
    // Make sure they completed them ...
    if ( !Len(obj.get('option99')) ) {
      errors.option99 = 'Option 99 is required.';
    }    

    // etc. ...
  }

}

Example Mura ORM Entities

To have a more cohesive understanding of how to work with Mura ORM, let's describe a simple application, and the objects we'll need to create for it.

Let's assume our client has asked us to create an "Address Book" type of application. This application would require an authorized user to be able to add, edit/modify, and/or delete Address Book entries. Each authenticated user would have their own Address Book. In other words, if Suzie logged in, she would only be able to see, create and/or modify her own entries.

Again, we're going to try and refrain from overcomplicating things. However, as a developer, It's easy to begin anticipating all kinds of enhancements and/or modifications you may want to make, and that's great. For now though, let's just stick to the script.

Address Book

Since a typical address book is filled with people, we'll start off by creating a Person object. Next, we'll have to consider the kind of properties or attributes a Person object should have. For example, they could have a first name, last name, zero or more phone numbers (PhoneNumber objects), zero or more addresses (Address objects), and so on.

The address book entry (Person) would be treated as a single object. It could be referenced by a single variable containing a pointer to the object. In addition, various helper methods could be associated with the object, such as a method to return the preferred phone number, the home address, and so on.

The following illustration is a sample UML diagram of our objects.

So, using our very basic objects, we can now begin to create our Mura ORM entities.

Person

Using our previous example, we could start our Person.cfc with the following code.

component 
    extends="mura.bean.beanORM" 
    table="custom_persons" 
    entityname="person" 
    bundleable="true" 
    displayname="PersonBean" 
    public=true 
    orderby="namelast,namefirst" {

    // primary key
        property name="personid" fieldtype="id";

    // person attributes
        property 
            name="namefirst" 
            datatype="varchar" 
            length="255" 
            nullable=true;

        property 
            name="namelast" 
            datatype="varchar" 
            length="255" 
            nullable=true;

    // relationships
        property 
            name="phonenumbers" 
            singularname="phonenumber" 
            cfc="personphonenumber" 
            fieldtype="one-to-many" 
            loadkey="personid" 
            cascade="delete" 
            orderby="phonetype";

        property 
            name="addresses" 
            singularname="address" 
            cfc="personaddress" 
            fieldtype="one-to-many" 
            loadkey="personid" 
            cascade="delete" 
            orderby="addresstype";

    // Custom Methods
        public string function getFullName() {
            return get('namefirst') & ' ' & get('namelast');
        }

        public any function getHomeAddress() {
            var rs = QueryExecute(
                ('
                    SELECT addressid 
                    FROM custom_personaddresses 
                    WHERE addresstype = "Home" 
                        AND personid = :pid
                ')
                , { pid=get('personid') }
            );

            return rs.recordcount
                ? getBean('personaddress').loadBy(addressid=rs.addressid)
                : getBean('personaddress').set('adddresstype', 'Home');
        }

}

PersonPhoneNumber

The PersonPhoneNumber.cfc defines our PersonPhoneNumber entity.

component 
    extends="mura.bean.beanORM" 
    table="custom_personphonenumbers" 
    entityname="personphonenumber" 
    bundleable="true" 
    displayname="PersonPhoneNumberBean" 
    public=true {

    // primary key
        property name="phonenumberid" fieldtype="id";

    // foreign key
        property 
            name="person" 
            fieldtype="many-to-one" 
            cfc="person" 
            fkcolumn="personid" 
            nullable=true;

    // attributes
        property 
            name="phonetype" 
            datatype="varchar" 
            length="255" 
            nullable=true; // Home, Office, Mobile, etc.

        property 
            name="phonenumber" 
            datatype="varchar" 
            length="255" 
            nullable=true;
}

PersonAddress

The following code comprises our PersonAddress.cfc and will define our PersonAddress entity.

component 
    extends="mura.bean.beanORM" 
    table="custom_personaddresses" 
    entityname="personaddress" 
    bundleable="true" 
    displayname="PersonAddressBean" 
    public=true {

    // primary key
        property name="addressid" fieldtype="id";

    // foreign key
        property 
            name="person" 
            fieldtype="many-to-one" 
            cfc="person" 
            fkcolumn="personid" 
            nullable=true;

    // attributes
        property 
            name="addresstype" 
            datatype="varchar" 
            length="255" 
            nullable=true; // Home, Office, etc.

        property 
            name="street1" 
            datatype="varchar" 
            length="255" 
            nullable=true;

        property 
            name="street2" 
            datatype="varchar" 
            length="255" 
            nullable=true;

        property 
            name="city" 
            datatype="varchar" 
            length="255" 
            nullable=true;

        property 
            name="state" 
            datatype="varchar" 
            length="255" 
            nullable=true;

        property 
            name="zip" 
            datatype="varchar" 
            length="255" 
            nullable=true;
}

Example

The example below illustrates how to use the entities we've created to get an iterator of a person's phone numbers.

contactBean = m.getBean('person').loadBy(persionid=m.event('pid'));

// Phone Numbers Iterator
itPhones = contactBean.getPhoneNumbersIterator();

while ( itPhones.hasNext() ) {
  phone = itPhones.next();
  WriteOutput( phone.get('phonenumber') );
)

Mura ORM Feed

As covered in the Feed Bean section, Feed beans allow developers to programmatically query Mura for other beans/objects. These same conventions work for custom entities. That said, if you tested some of the code examples from the Example Mura ORM Entities section, you might have some Person objects created, and you could test some of the Feed bean methods out.

The example below simply demonstrates how simple it is to create a feed of a custom entity, using the same methods described from the Feed Bean section.

// Persons Feed, based on the currently logged in user's userid
feedPersons = m.getFeed('person')
              .where()
              .prop('userid')
              .isEQ(m.currentUser('userid'))
              .sort('namelast', 'asc')
              .sort('namefirst', 'desc');

// Persons Iterator
itPersons = feedPersons.getIterator();

Mura ORM Events

As covered in the Mura ORM Configuration section, any .CFC's discovered under a "../handlers/" directory within a "model" directory, will automatically be registered as custom event handlers. For example, ../model/handlers/myhandler.cfc.

In the Mura Events section, we learned the various events that are announced during the lifecycle of the application and each request. In addition to the events documented there, Mura also announces special events for basic "CRUD" activities such as when saving, updating, or deleting an entity. To leverage any of these events, you simply add your methods to your custom event handler(s).

Event Description
onBefore{EntityName}Save Announced before an entity is saved.
onAfter{EntityName}Save Announced after an entity is saved.
on{EntityName}Save Announced after an entity is saved.
onBefore{EntityName}Update Announced before an entity is updated.
onAfter{EntityName}Update Announced after an entity is updated.
onBefore{EntityName}Create Announced before an entity is created.
onAfter{EntityName}Create Announced after an entity is created.
onBefore{EntityName}Delete Announced before an entity is deleted.
onAfter{EntityName}Delete Announced after an entity is deleted.

The example below illustrates using one of the event handlers.

onBefore{Entity}Save(m) {
  // accessing the bean
  var bean = m.event('bean');

  // do something here
}

Supported ORM Entity Events

In addition to using an event handler, you could use an ORM Entity Event. The following table lists supported ORM Entity events. You could simply include any these methods in your entity's .CFC to provide custom logic where needed.

ORM Event Description
preLoad() This method is called before the load operation is complete or before the data is loaded from the database.
postLoad() This method is called after the load operation is complete.
preUpdate() This method is called before the update operation is complete.
postUpdate() This method is called after the update operation is complete.
preCreate() This method is called before the create operation is complete.
preInsert() This method is called before the insert operation is complete.
postCreate() This method is called after the create operation is complete.
postInsert() This method is called after the insert operation is complete.
preDelete() This method is called before the object is deleted.
postDelete() This method is called after the delete operation is complete.

Mura ORM Assembler & Scaffolder

Note: this feature is considered Beta status and functionality may change without notice.

The Mura ORM Assembler & Scaffolder is an extremely powerful user interface for creating, managing, and populating Mura ORM entities (objects).

Mura ORM Assembler

The Mura ORM Assembler enables for the creation and management of Mura ORM entity definitions and relationships. The Assembler creates and stores definitions of Mura ORM entities as JSON, which allows for the ability to export and import them between Mura instances. Mura.js also uses these JSON definitions and converts them into actual Mura ORM entities. The entities are automatically wired into Mura and are immediately available via Mura's JSON API as well as the Mura Scope via m.getBean('yourCustomEntityName'), and all of the other Mura ORM entity methods.

Mura ORM entities auto-created by the Assembler are located under the {context}/modules/dynamic_entities/model/beans/ directory as CFCs (ColdFusion Components).

Note: These Mura ORM entities are not update safe. Any changes you manually make directly to the entity definition itself will be overwritten, if you edit it again using the Assembler.

As you can imagine, creating entities with the Mura ORM Assembler is much easier and convenient than creating them by hand, saving developers a huge amount of time.

Mura ORM Scaffolder

The Mura ORM Scaffolder lets you create, read, update/edit, and delete Mura ORM entity data. Only entities specifically enabled for the Scaffolder (via a flag in the entity's component definition) may be managed using the Scaffolder. In addition, only users in assigned permission groups are able to manage these entities via the Mura ORM Scaffolder.

Using the Scaffolder not only saves developers time, but also saves them from having to create a custom user interface for managing entity data while leveraging Mura's baked-in permission functionality.

 

Enable/Disable Mura ORM Assembler & Scaffolder

To enable or disable the Mura ORM Assembler and Scaffolder, follow the steps below.
  1. From the back-end administration area of Mura, go to Site Settings > Edit Settings
  2. Select the Modules tab
  3. Set "Mura ORM Scaffolding (ALPHA)" to "On" to enable, or "Off" to disable
  4. Then, click Save Settings to save your desired settings.

How to Use the Mura ORM Assembler

As previously mentioned, the Mura ORM Assembler enables for the creation and management of Mura ORM entity definitions and relationships. Once you've enabled the Mura ORM Assembler & Scaffolder, you may follow the steps outlined below to create Mura ORM entity definitions and relationships. Only Super Admin Users are able to use the Mura ORM Assembler.

  1. From the back-end administration area of Mura, select Content on the main navigation, then select the Custom tab.
  2. To create a new Mura ORM entity, click the Add button.
  3. You should be taken to the Custom Mura ORM Entity screen.
  4. Complete the information on the Definition tab. The primary fields are described below.
    • Entity Name
      • A variable-safe name (no spaces, begins with a letter, letters, numbers and underscores only). For instance, if you were creating a "Book" entity, you might call this entity simply "book".
    • Display Name
      • This is the human-readable version of the Entity Name, such as "Department Heads" or "Types of Pets".
    • Table Name
      • This is the table name the entity's data will be stored in. It is usually advisable to match it to the entity name, and is often prefixed with some special name to distinguish it in the database and avoid naming collisions with Mura's own table names. For instance, a "book" entity might have a table name like "custom_books" or "mycompany_books".
    • Order By (optional)
      • This is the default property (column) by which to sort/order your entity data by. It must match one of the Property Names of the entity (see below).
    • Scaffold
      • If enabled, this gives the entity permission to be managed in the Mura ORM Scaffolder for data entry/management.
    • Bundleable
      • If enabled, the entity and any stored data will be included in any site bundles.
    • Publicly Accessible
      • If enabled, the entity may be displayed and/or managed via the front-end of your Mura site. However, you will also be responsible for building the user interface for display and/or management on the front-end itself.
  5. Also on the Definition tab, is where you manage the Entity Properties and relationships. An entity's "properties" are the fields where data will be stored, and will essentially be turned into columns within the defined Table Name. Every entity has a mandatory "id" property, which you may rename, but not delete. In addition, new entities also begin with an optional "siteid" property, which is important for tying the data to a specific Mura site. An entity's "relationships" are the keys that bind your entity to other Mura ORM entities.
    • How to Add /Edit Properties
      • Click the Add Property button, or select a property to edit.
      • You should now see the Property Details screen. The fields are described below.
        • Property Name
          • The variable-safe name for the property. For example, "booktitle" or "bookauthor".
        • Display Name
          • The human readable version of the property name. For example "Book Title" or "Book Author".
        • Data Type
          • The SQL data type the property should be saved as.
          • Valid options are:
            • VarChar
            • Text
            • Int
            • Boolean
            • DateTime
            • Any
        • Field Type
          • Valid options are:
            • None
              • This is the default value, and if selected, no database index will be created.
            • Index
              • If selected, a database index will be created on the property.
        • Default
          • The value entered here will be used as the default value in the Mura ORM Scaffolder. This is useful when the property's Form Field type is set to Dropdown or Radio to pre-select one of the the option list values.
        • Form Field
          • The value selected here will determine how the form field will appear in the Mura ORM Scaffolder when administrators are entering data.
          • Valid options are:
            • Text Field
            • Text Area
            • HTML Editor
            • Dropdown
            • Checkbox
            • Radio
            • Hidden
            • Do not render
        • Length
          • The maximum length of the data field.
        • Required
          • If checked, the property will be required in the Mura ORM Scaffolder.
        • List
          • If checked, the property will be displayed in the list view of the Mura ORM Scaffolder.
        • Filter
          • If checked, end users may use the "Filter By" functionality on this property in the list view of the Mura ORM Scaffolder.
        • Nullable
          • If checked, the property will be set to "NULL" in the database if no value is entered in the Mura ORM Scaffolder.
        • Option List
          • This field will appear if the Form Field type is set to Dropdown or Radio. Enter a carat (^) delimited list of values to be used as the labels for the form field. For example, "Matt^Grant^Steve".
        • Option Value List
          • This field will appear if the Form Field type is set to Dropdown or Radio. Enter a carat (^) delimited list of values to be used as the value which will be stored in the database. For example, "001^002^003".
      • When finished, click the Add Property button.
      • To reorder Entity Properties, click+drag and drop the properties.
      • Click Save when finished.
      • A message will appear at the top of your screen to indicate when the entity definition has been saved.
      • In addition, a ".CFC" file will be auto-generated and saved under {context}/modules/dynamic_entities/model/beans/{EntityName}.cfc. If you wish, you could copy this file and save it under your own ../model/beans/ directory, and then delete the auto-generated file itself.
      • If you wish to delete the entity, simply click the Delete button, and select OK on the Alert dialog window. Also, when deleting entities, only the entity definition itself will be deleted from Mura, and the auto-generated ".CFC" file. However, all data will still be stored in the associated tables.
    • How to Add/Edit Relationships
      • Click the Add Relationship button, or select a relationship to edit.
      • You should now see the Relationship Details screen. The fields are described below.
        • Property Name
          • The variable-safe name that references the related entity. For example, a "book" entity might have related "authors". So, you could enter "author" or "authorid".
        • Display Name
          • The label, or human readable version of the relationship. For example, "Author" or "Author ID".
        • Relationship Type
          • When working with relationships, read the relationship from left to right. In other words, the left word refers to the current entity, and the word on the right refers to the related entity.
          • Valid Options:
            • one-to-many
              • This common type of relationship indicates the current entity may be related to multiple related entities. For example, one "order" entity could be related to multiple "product" entities.
            • one-to-one
              • This very simple relationship indicates there will only be one related entity to the current entity. For example, each "product" entity can be related to only one "manufacturer" entity.
            • many-to-one
              • Similar to the one-to-many relationship, this common relationship type indicates there may be multiple current entities related to a single entity. For example multiple "product" entities can be related to only one "order" entity.
          • If you wish to create a "many-to-many" relationship, the recommended approach is to simply create a one-to-many/many-to-one association between three participating entities. In other words, you would create a special entity to bind the related entities together. For example, you could have an "author" entity, a "book" entity, and a "authorsbooks" entity which stores the primary key values of both "authors" and "books" as a "many-to-one" relationship. For more information on this approach, view our blog post titled Mura ORM and Many-to-Many Relationships.
        • Relates To
          • Select the actual entity that this entity is related to. Only defined entities will appear in the list, in addition to Mura's own entities (such as "content" and "user"). If the desired entity has not been defined yet, you will need to define the entity, and come back to create the relationship.
        • Foreign Key Column
          • In most cases, this should be the related entity's "Primary Key" field. However, there may be an occasion where another field should be used to bind the entities together.
        • Render Field
          • The select menu will be populated with the properties of the selected entity from the "Relates To" field. Select the desired property to display when viewing the entity list in the Mura ORM Scaffolder table. For example, if the related entity is "author", you may wish to select the "Last Name" property instead of some obscure "authorid" property.
        • Load Key
          • This field relates to the Foreign Key Column. If the current entity is related by something other than the Primary Key, you should enter it here.
        • Cascade Delete?
          • Valid Options:
            • none
              • Do nothing if any related entities are deleted.
            • delete
              • If selected, this entity will be deleted when the related entity is deleted.
      • When finished, click the Add Relationship button.
    • The relationship will now display under the Entity Properties area. Simply click the gears icon to edit the relationship again, if desired.
  6. The JSON tab displays the string to be saved in the database as the definition of the entity, and captures all of the properties and relationships created using the Mura ORM Assembler. This is especially useful information when using Mura.js to work with Mura ORM entities.
  7. When finished, click the Save button to save your entity definition.
     

How to Use the Mura ORM Scaffolder

As previously mentioned, the Mura ORM Scaffolder lets you create, read, update/edit, and delete Mura ORM entity data. Once you've enabled the Mura ORM Assembler & Scaffolder, you may follow the steps outlined below to manage your Mura ORM entity data, assuming Mura ORM entities enabled for use in the Scaffolder exist.

Also, by default, Super Admin Users, and members of the Admin group are automatically granted permissions to the Mura ORM Scaffolder. However, members of these groups may also enable other groups access by managing the permissions to this section.

  1. From the back-end administration area of Mura, select Content on the main navigation, then select the Custom tab.
  2. You should be able to view to the Custom Entities table. This landing page lists all entity types available for editing via the Scaffolder. For example, the list below includes "Author", "Book", and "BooksAuthors Relationships".
  3. To view an entities data entries, click the entity name on the list, or select the three-dot menu to the left of the entity name, and click the "List" option.
  4. You should be taken to the entity's listing of entries. For example, in the illustration below, you can see the "Author" entries.
  5. If a column (attribute) is filterable, there will be a text box above the column header. You may enter the search text and click the Enter/Return key or the Apply Filter button to filter the results.
  6. To remove the filter, click the Remove Filter button.
  7. You may also click on a column header to sort data on that column, and an arrow will appear by the header to indicate the sort direction.
  8. To edit a entity, click on an entity attribute, or select the three-dot menu to the left, and click Edit.
  9. You will then be taken to the entity's data entry screen.
  10. To save the entity after making your edits, click the Save button.
  11. To delete an entity, click the Delete button.
  12. To add a new entity, from the entity's listing of entities, click the Add button.
  13. You will be taken to the Edit screen. When creating or editing an entity, the form that appears will be determined by the entity's definition as created in the Mura ORM Assembler.
  14. Complete the form data, then click Save to save your new entity.
  15. Congratulations! You've successfully edited or added an entity.

Mura Iterators

According to Merriam-Webster, iterate means "to say or do again or again and again." In programming, an "iterator" is a design pattern. An iterator wraps a collection or group of objects (e.g., queries/recordsets, arrays, lists, structs, beans, etc.) with common helper methods without the need to fully know or understand the underlying data structure. This allows programmers to use common methods for looping over, or iterating over, many different types of collections or groups of objects.

Mura Iterators are an array of objects, and have a number of common helper methods, allowing developers to loop/iterate over, collections/groups of objects. Mura Iterators wrap an underlying query, and use the returned recordset as the basis for its array of objects. This also means the order of objects in the iterator is determined by the sort order of the underlying recordset.

As you may have already seen in other sections of the documentation, Mura relies on the iterator design pattern itself. Many of Mura's common beans include helper methods which return some type of iterator. For example, in the Content Bean section, you'll see methods such as getRelatedContentIterator(), getCommentsIterator(), etc.

If you've worked with recordsets/queries in the past, you'll find iterators to be quite similar. However, working with iterators gives you so much more power than merely looping over a query/recordset. When iterating over a collection of Mura objects, you'll have access to the entire object itself, including its helper methods. However, if you were merely looping over a query/recordset using <cfoutput> tags, you would only have access to the fields included in the record itself. In addition, when working with a standard query/recordset, it can be cumbersome to do things like pagination, or even loop backwards without having to rewrite the underlying query itself. Mura Iterators can do all of these things for you, and so much more.

Iterator Basics

As an object itself, Mura Iterators have a number of attributes you can inspect using its helper methods. Iterators hold data pertinent to looping over collections or groups of objects. For example, Mura Iterators know how many objects there are in the collection it's working with. They also know if you've started to loop/iterate over the collection, what object you're currently working with, which "page" your on, and so on.

You can use these helper methods to ask the iterator for the next object, or even the previous object, and then do whatever you want with the object itself, and when finished, move on to the next object. For example, you could manipulate each object, and then re-save it. If the objects were content items, you could loop over each object, and then output the associated image and a title to create a slideshow. The possibilities are endless.

While Mura Iterators offer several helper methods, there are only two primary methods you need to know to begin working with them; hasNext() and next().

hasNext()

The hasNext() method inspects the underlying collection/array of objects, and then returns true if there are still objects in the array of objects you're working with, or false, if there are no more objects in the array to work with.

next()

The key method, next(), queues up the next object in the collection/array of objects, and gives you a way to reference the current item/object so you can work with it.

Basic Examples

The examples below demonstrate using the basic hasNext() and next() methods.

Tag-based Example

<cfloop condition="iterator.hasNext()">
  <cfset item = iterator.next()>
  <!--- Do something with 'item' --->
  <cfdump var="#item#">
</cfloop>

Script Example

while ( iterator.hasNext() ) {
  item = iterator.next();
  // Do something with 'item'
  WriteDump( item );
}

// OR

do {
  item = iterator.next();
  // Do something with 'item'
} while ( iterator.hasNext() );

Conditionals

You could also wrap the prior examples with a conditional, and return something in the event that the iterator is empty.

if ( iterator.hasNext() ) {
  // Iterator has objects
} else {
  // Iterator is empty ... no objects
}

Key Iterator Methods

The key Mura Iterator methods are described below.

Method Returns Description
hasNext boolean Returns true if iterator has objects in the array, and hasn't finished looping. Returns false, if iterator is empty, or has finished looping. This method "looks right" or forward, in the array of objects.
next object Moves the "index" to the next object, or next record in the underlying query's recordset. Returns the next object in the iterator (array of objects). Usually assigned to a variable, in order to have a way to reference the current object being looped over in the array of objects.
peek object Returns the object after the current object being looped over (or next) in the iterator. It's a way to "peek" at what's up next.
reset n/a Resets the "index" back to the beginning. Useful for when you need to loop over an iterator more than once.
end n/a Moves the "index" to the last object, or last record in the underlying query's recordset.
hasPrevious boolean Similar to hasNext, except it "looks left" or backward, in the array of objects. Returns true if iterator has an object that it has previously looped over, or false, if the index is at the beginning.
previous object Moves the "index" to the previous object, or previous record in the underlying query's recordset. Usually assigned to a variable, in order to have a way to reference the current object being looped over in the array of objects.
setQuery n/a Expects a query parameter, and an optional maxRecordsPerPage (defaults to 1000) parameter, to set the underlying query used for the iterator.
getQuery query Returns the underlying query used for the iterator.
getRecordCount numeric Returns the total number of records in the underlying query used for the iterator.
setPage n/a Expects a pageIndex parameter, to set the page of records/objects the iterator should be working with.
getPage numeric Returns the page index of records/objects the iterator is currently working with.
pageCount numeric Returns the total number of pages the iterator has, based on the total number of items/records and number returned from getItemsPerPage().
setItemsPerPage n/a Expects a itemsPerPage parameter, to set how many items per page the iterator should be working with. Formerly known as setNextN().
getItemsPerPage numeric Returns the number of items per page the iterator is currently working with. Formerly known as getNextN().
setStartRow n/a Expects a startRow parameter, to set which row of the underlying query the iterator should be working with.
getStartRow numeric Returns the row of the underlying query the iterator is currently working with.
getCurrentIndex numeric Returns the index of the iterator is currently working with.

Examples

In addition to the examples listed below, be sure to view the examples in the Common Beans section, where many of Mura's objects include their own iterator methods.

Feed Bean Iterator

The example below demonstrates how to use a Feed Bean, and use its getIterator() method.

<cfset feedBean = m.getFeed('content').where().prop('credits').containsValue('Steve')>
<cfset it = feedBean.getIterator()>

<cfif it.hasNext()>
  <cfloop condition="it.hasNext()">
    <cfset item = it.next()>
    <div>
      <a href="#item.get('url')#">
        #esapiEncode('html', item.get('title'))#
      </a>
    </div>
  </cfloop>
<cfelse>
  <p class="alert alert-info">
    This iterator doesn't have any items.
  </p>
</cfif>

Content Iterator

You can use any query to populate and use Mura's contentIterator bean, as long as the query contains a siteid and contentid column.

rs = QueryExecute("
  SELECT contentid, siteid
  FROM tcontent
  WHERE active = 1
");

Now, use the contentIterator bean's setQuery method, and use the iterator.

it = m.getBean('contentIterator').setQuery(rs);

if ( it.hasNext() ) {
  item = it.next();
  // Do something with the item
  WriteDump( item.getAllValues() );
} else {
  // No items/records exist
}

Query Iterator

The example below demonstrates how to use a regular query with Mura's queryIterator bean.

rs = QueryExecute("
  SELECT *
  FROM someTable
");

Now, use the queryIterator bean's setQuery method, and use the iterator.

it = m.getBean('queryIterator').setQuery(rs);

if ( it.hasNext() ) {
  item = it.next();
  // Do something with the item
  WriteDump( item );
} else {
  // No items/records exist
} 

Summary

Mura's beans and objects are an integral part of Mura development. Throughout this section, we've covered everything from the basic syntax of working with Mura bean objects, what the most common bean objects are, and how the Mura Scope objects are merely wrappers of common beans. Additionally, we've covered creating and managing custom objects including custom Class Extensions, and Mura ORM. Finally, we learned about Mura Iterators, why they're important, and how to use them in our development.

Mura Events

As a CFML application, Mura has CFML-specific application-wide settings, variables, and event handlers. In addition to the CFML-specific events (e.g., onApplicationStart, onSessionStart, onRequestStart, etc.), Mura has several of its own events, to allow developers the opportunity to add to, or in some cases, even replace, Mura's own business logic and functionality.

Throughout this section, we'll cover the differences between lifecycle events and contextual events, the Mura "event" scope, and the wide variety of possible events that may occur during any given request, as well as how you can register your own event handlers targeting the specific event(s) related to the task(s) you wish to accomplish. Lastly, you'll learn how you can create your own custom events, and how to use Mura's event log for debugging and performance optimization purposes.

Lifecycle Events vs. Contextual Events

Before you continue through this section, you'll need to understand the differences between Mura's lifecycle events and Mura's contextual events.

  • "Lifecycle" events generally get triggered on every page request, and represent the execution of Mura's normal request flow. Mura consists of two unique lifecycles: the Front-end Lifecycle, and the Admin Lifecycle.
  • "Contextual" events aren't triggered on every page request, because they occur in response to the specific event(s) or action(s) that preceded them.

For example, Mura-related events that occur when editing a "User" are only triggered when a user is being created or updated, and would not necessarily be triggered in the course of a normal front-end request.

Note: Contextual events only contain data that is directly supplied to it by the Mura core. If you need to access to the full event scope itself (i.e., during a front-end or admin rendering event), it can be accessed via m.getGlobalEvent().

Mura "event" Scope

When working with events, keep CFML's scopes in mind. As noted in the Mura Scope section, in addition to the CFML scopes, Mura has its own scope called the Mura Scope.

The Mura Scope has a special subscope called the "event" scope, and it is created on every request. It can be referenced via m.event(). If you need to access the global event scope from within a contextual event, you may use m.getGlobalEvent().

Mura's event scope wraps CFML's request, form, and URL scopes. Mura also injects additional data and helper methods into the event scope, and the availability of the data is dependent upon the context in which you may be accessing it. As covered in the Mura Objects section, you may also store your own data in Mura's event scope for use within your own application logic.

Syntax

You can use the following examples to "get" and/or "set" various attributes of Mura's event scope. Use whichever syntax feels best to you.

Getters

m.event('attributeName')
m.event().get('attributeName')
m.event().get{AttributeName}()
m.event().getValue('attributeName')

Setters

m.event('attributeName', 'someValue')
m.event().set('attributeName', 'someValue')
m.event().set{AttributeName}('someValue')
m.event().setValue('attributeName', 'someValue')

Event Hooks

In this section, we'll cover Mura's various event hooks/methods, or trigger points, you can use to either add to, or even replace Mura's business logic and functionality.

One thing you'll want to think of when using this section is determining the point(s) in which you wish to add your logic. For example, are you trying to do something that occurs while the Mura application is loading up? Or, are you attempting to do something during a normal, front-end request? Are you trying to do something when a content item is being updated? Maybe you're attempting to do something when a session begins. These are all good questions that can point you in the right direction. Once you've determined the answers to these types of questions, you can dig into the available event hooks, register your event handlers, and implement your custom application logic.

Also, when working with Mura events, keep in mind that all events are automatically passed in the Mura (m) scope as an argument. The example below illustrates a sample method with the argument being passed in.

public any function onSomeEvent(m) {
  // do something here
}

To can obtain the event scope via the Mura scope, follow the example below.

public any function onSomeEvent(m) {
  // obtain the `event` scope via the Mura Scope
  var e = arguments.m.event();
}

Keep in mind when working with some contextual events, you may need to access the "global" event scope itself. The example below demonstrates accessing the global event, if necessary.

public any function onSomeEvent(m) {
  // access the global event scope
  var globalEvent = arguments.m.getGlobalEvent();
}

Finally, you may add any of these events to a registered event handler, in order for Mura to execute them. See the Event Handlers section for more information.

Event Prefixes

As you review the available events, you'll notice a common naming convention. Mura's events all start with a prefix, which informs you whether you're replacing Mura's business logic, or adding to Mura's business logic. Keep the following information in mind as you dive into the rest of this section.

  • standard{Event}
    • All "standard" events are points where you can replace Mura's business logic and functionality. These events are all defined within the file located at {context}/core/mura/Handler/standardEventsHandler.cfc
  • on{Event}
    • All "on" events are points where you can add to Mura's business logic and functionality. The important thing to understand about "on" events is that these are not necessarily "defined" anywhere. These types of events are merely "announced" during the normal execution of a request.

Global vs. Site

In addition to the "standard" vs. "on" prefixes, you'll notice many events also have additional common names after the prefix:

  • onGlobal{Event}
    • All "global" events are those that occur throughout the entire instance of Mura, and are not bound to any specific site.
  • onSite{Event}
    • All "site" events occur site-wide, and are bound to the specific site they have been registered to.

Before vs. After

Another common naming convention includes using the words "before" and "after" to indicate whether something is happening immediately before Mura is about to do something, or after Mura has finished doing something.

  • onBefore{Event}
    • All "before" events are announced immediately before Mura is about to execute its normal functionality.
  • onAfter{Event}
    • All "after" events are announced immediately after Mura has executed its normal functionality.

CFML Application Events

As a CFML developer, you should be familiar with using application event handlers. The most commonly used CFML methods are outlined below, with a direct mapping to Mura's methods. Using Mura's methods in your own event handlers allows you to stay on the upgrade path, without having to modify Mura's Application.cfc directly.

CFML Method Analogous Mura Event
onApplicationStart onApplicationLoad
onApplicationEnd Not implemented in Mura at this time
onSessionStart onGlobalSessionStart
onSessionEnd onGlobalSessionEnd
onRequestStart onGlobalRequestStart
onRequest Not implemented in Mura at this time
onRequestEnd onGlobalRequestEnd
onMissingTemplate onGlobalMissingTemplate
onError onGlobalError

Standard Events

As previously discussed, events prefixed with "standard" are points where you can replace Mura's business logic and functionality. To view their method definitions, and what they're doing specifically, open the file located under {context}/core/mura/Handler/standardEventsHandler.cfc. You can also view these methods via Mura's Component API at http://www.getmura.com/component-api/7.1/index.html?mura/Handler/standardEventsHandler.html.

There are three primary suffixes for "standard" events:

    • validator
      • Validators inspect the "event" or "request" itself, and forward the request/event to the appropriate handler, if necessary.
    • handler
      • Handlers usually set values in the "global event", or redirect the user somewhere.
    • translator
      • Translators check to see if the requested returnformat is JSON, and if so, utilizes Mura's JSON API to handle the request.
    Method When run
    standard404Handler Called by standard404Validator when the content bean in the current request is new.
    standard404Validator Called by standardSetContentHandler on each request.
    standardDoActionsHandler
    standardDoResponseHandler
    standardEnableLockdownHandler
    standardEnableLockdownValidator
    standardFileTranslationHandler
    standardFileTranslator
    standardForceSSLHandler
    standardForceSSLValidator
    standardJSONTranslator
    standardLinkTranslationHandler
    standardLinkTranslator
    standardMobileHandler
    standardMobileValidator
    standardPostLogoutHandler
    standardRequireLoginHandler
    standardRequireLoginValidator
    standardSetContentHandler
    standardSetIsOnDisplayHandler
    standardSetLocaleHandler
    standardSetPermissionsHandler
    standardSetPreviewHandler
    standardTrackSessionHandler
    standardTrackSessionValidator
    standardTranslationHandler
    standardWrongDomainValidator
    standardWrongFilenameHandler
    standardWrongFilenameValidator

    Custom Events

    In addition to all of Mura's event hooks, developers may also create their own, custom event hooks. The main decision you'll want to make is whether you wish to "announce" an event, or "render" an event. Regardless of whether you choose to announce an event, or render an event, Mura will trigger your event as illustrated below, and in turn, any registered event handlers with these methods defined, will be executed:

    on{YourEvent}
    

    Announcing Events

    When you "announce" an event, you're essentially sending a notification throughout Mura that something in particular is about to occur, or has just occurred. You may omit the "on" from your event name when announcing it. However, your method itself should contain the "on" prefix.

    Syntax

    The basic syntax for announcing an event is:

    m.announceEvent('YourEvent', m);
    // OR
    m.announceEvent('onYourEvent', m);
    

    When Mura encounters the code above, it will search for any registered event handlers to locate onYourEvent(), and if found, the method will be executed.

    Example

    The following example illustrates a very simple form, with a text field named myField. The value attribute will be pre-populated with its own value. In other words, since the event scope itself wraps CFML's URL, Form, and Request scopes, we can check the event scope for the value, and if the form is submitted, then any value entered will be used to pre-populate itself.

    <form method="post">
      <input type="text" name="myField" value="[m]esapiEncode('html_attr', m.event('myField'))[/m]">
      <input type="hidden" name="myFormIsSubmitted" value="true">
      <input type="submit">
    </form>

    In the example form above, we're using a hidden field named myFormIsSubmitted with its value set to "true". By checking the event scope for this value, if found, we can trigger an event to occur, as shown the example below.

    // check form submission via `onRenderStart`, and if found, announce your custom event.
    public any function onRenderStart(m) {
      if ( arguments.m.event('myFormIsSubmitted') == 'true' ) {
        // announcing the event
        arguments.m.announceEvent('CustomFormSubmitted', arguments.m);
      }
    }
    

    If the form has been submitted, our customFormSubmitted event is announced, triggering any registered event handlers to execute.

    public any function onCustomFormSubmitted(m) {
      // you could do whatever you want here
      // including dumping out the entire event itself to inspect it
      // or simply dumping out the value of a specific form filed
      // and then halt the rest of the process (for example only)
      WriteDump(var=arguments.m.event('myField'), abort=true);
    }
    

    Rendering Events

    If you wish to "render" an event, in addition to sending a notification throughout Mura that something in particular is about to occur, or has just occurred, you are allowing for the possibility to render something to the browser. In other words, your code should be checking for a string to return, and if nothing is returned, then you'll most likely want to display something else by default.

    Syntax

    The basic syntax for rendering an event is:

    m.renderEvent('YourEvent');

    Examples

    There are several ways you could choose to render event output. The following example illustrates a simple way to output the result of a rendered event.

    <cfoutput>
      #esapiEncode('html', m.renderEvent('YourEvent'))#
    </cfoutput>

    The example below is typical of how Mura renders events in its own code.

    eventOutput = esapiEncode('html', m.renderEvent('YourEvent'));
    
    if ( Len(eventOutput) ) {
      WriteOutput(eventOutput);
    } else {
      // render something else / default output
    }

    Event Lifecycles

    Lifecycle events are generally triggered on every page request, and represent the execution of Mura's normal request flow. Mura consists of primarily two unique lifecycles: the Front-end Request Lifecycle, and the Admin Request Lifecycle. During the flow of these lifecycles, other contextual events are triggered, and are dependent upon the requested action that precedes it.

    Admin Request Lifecycle

    The administration area of Mura has a multitude of contextual events that could be triggered, depending on the requested action. For example, a user could be attempting to create new content, updating a user, or deleting a category. Each of these actions would trigger their own contextual events, in addition to the routine request flow outlined below.

    onGlobalRequestStart
      onAdminRequestStart
    
        onAdminHTMLHeadRender         // renders just before the closing </head> tag
    
          onAdminMFAChallengeRender   // Multi-factor Auth area of login screen
    
          onDashboardReplacement      // render a completely new dashboard
    
          onDashboardPrimaryTop       // renders in the top of the dashboard
          onDashboardPrimaryBottom    // renders in the bottom of the dashboard
          onDashboardSidebarTop       // renders in the top of the dashboard sidebar
          onDashboardSidebarBottom    // renders in the bottom of the dashboard sidebar
    
          onAdminNavMainRender        // render additional main nav menu items
          onFEToolbarExtensionRender  // render additional front-end toolbar menu items
          
          on{Type}SecondaryNavRender
          on{Type}{Subtype}SecondaryNavRender
    
          on{Type}{Subtype}NewContentMenuRender
          onNewContentMenuRender
    
        onAdminHTMLFootRender         // renders just before the closing </body> tag
    
      onAdminRequestEnd
    onGlobalRequestEnd
    

    Front-End Request Lifecycle

    While there may be any number of contextual events that are triggered, dependent upon the requested action, the events listed below generally occur during a typical front-end request.

    When it comes to the front-end request lifecycle, if you're most interested in the final string of code that will be returned to the browser, then you'll want to be aware of m.event('__MuraResponse__'). That's a double-underscore in front of, and after "MuraResponse". As noted in the lifecycle below, the "MuraResponse" will be sent to the browser.

    onGlobalRequestStart
      onSiteRequestInit
      onSiteRequestStart
    
        standardEnableLockdownValidator
          standardEnableLockdownHandler // if lockdown enabled
    
        standardSetContentHandler
          // if `previewid` exists
          standardSetPreviewHandler 
          // else
          standardSetAdTrackingHandler
    
          standardWrongFilenameValidator
            standardWrongFilenameHandler // if wrong filename
    
          standard404Validator
            standard404Handler // contextual: if content item not found
              onSite404 
        
        standardWrongDomainValidator
          standardWrongDomainHandler // if invalid domain
    
        standardTrackSessionValidator
          standardTrackSessionHandler // if session tracking enabled
    
        standardSetIsOnDisplayHandler
        standardDoActionsHandler // checks `doaction` (e.g., login, logout, etc.)
    
        standardSetPermissionsHandler
        standardRequireLoginValidator
          standardRequireLoginHandler // if login required
    
        standardSetLocaleHandler
        standardMobileValidator
          standardMobileHandler // if `request.muraMobileRequest` and no `altTheme`
    
        standardSetCommentPermissions
    
        standardDoResponseHandler
          standardForceSSLValidator
            standardForceSSLHandler // if necessary
    
          // This is a great place to put your logic
          // Mura begins compiling the code that will be returned to the browser
          onRenderStart
    
            // if content type is link
            standardLinkTranslationHandler
            // else if content type is file
            standardFileTranslationHandler
              standardFileTranslator
                onBeforeFileRender
                onAfterFileRender
            // else
            standardTranslationHandler  // sets m.event('__MuraResponse__')
              // if `returnformat` is JSON
              standardJSONTranslator
                onAPIResponse
                on{Type}APIResponse
                on{Type}{Subtype}APIResponse
                onAPIError  // if an error occurs
    
              // else
              standardHTMLTranslator // parses layout template and renders queues
                // other events could be announced, based on template code
                // `m.dspBody()` invokes several events (and is included in most templates)
                // if event('display') is `search`
                onSiteSearchRender
    
                // else if event('display') is `editprofile`
                onSiteEditProfileRender
    
                // else if event('display') is `login`
                onSiteLoginPromptRender
    
                // else if content is restricted and user not allowed
                onContentDenialRender
    
                // else if content is not on display
                onContentOfflineRender
    
                // else if `m.event('display')` is not an empty string
                onDisplayRender  // allows you to have custom displays
    
                // else
                  // default body rendering
    
                  on{Type}{Subtype}BodyRender  // e.g., onPageDefaultBodyRender
                  // if a string isn't returned, then
                  on{Type}BodyRender  // e.g., onPageBodyRender
    
                  // if a string still isn't returned, then
                  // Mura will look for files by content type
                  // `../{ThemeName}/content_types/{Type}_{Subtype}/index.cfm`
                  // Then, `../{ThemeName}/content_types/{Type}/index.cfm`
                  // and so forth.
                  // See the "Mura Rendering" section for more information
    
          // Mura has finished compiling the code to return to the browser
          // In other words, the train is about to leave the station ...
          onRenderEnd  // m.event('__MuraResponse__') is ready
    
      onSiteRequestEnd
    onGlobalRequestEnd
    

    Event Handlers

    Mura event handlers are simply ColdFusion components (CFCs), or files saved with the extension .cfc. They contain methods/functions, and may contain other variables and/or data. Most event handlers will contain one or more of Mura's event hooks, and/or custom event hooks.

    The event hooks are merely "announced" during specific points of a request. Registered event handlers will be parsed when the event they're registered for occurs, and if any event hooks are found, they will be executed. For example, if you have two registered even handlers, and both of them contain a method listening for the same event hook, both methods will execute, whenever the event is announced.

    As described in the Standard Events section, Mura's standard events handler is located under {context}/core/mura/Handler/standardEventsHandler.cfc.
    A list of the available methods and properties is available at via Mura's Component API at http://www.getmura.com/component-api/7.1/index.html?mura/Handler/standardEventsHandler.html.

    Developers may have several Mura event handlers throughout your sites, themes, content types, modules, and plugins.You may also register your event handler(s) for specific entities. In addition, you can choose to register your event handlers by convention, or explicitly register them via a known event handler. Examples and options are described below.

    Example Event Handler

    With the exception of the "Site" event handler, or the "Theme" event handler, you can name your event handler anything you want, as long as it has the .cfc file extension. All event handlers should extend mura.cfobject. By doing so, you allow yourself the ability to leverage many of Mura's baked-in methods and functionality for working with Mura objects.

    CFScript-based .CFC Example

    component extends="mura.cfobject" {
    
      public any function onSomeEvent(m) {
        // arguments.m is "The Mura Scope"
        // Do something
      }
    
    }
    

    Tag-based .CFC Example

    <cfcomponent extends="mura.cfobject" output="false">
    
      <cffunction name="onSomeEvent" access="public" returntype="any">
        <cfargument name="m" hint="Mura Scope">
        <!--- Do something --->
      </cffunction>
    
    </cfcomponent>

    Plugin Event Handlers

    Plugin event handlers should extend mura.plugin.pluginGenericEventHandler, instead of using the typical mura.cfobject. Doing so allows you access to the plugin's own configuration information via variables.pluginConfig, as well as Mura's configuration data via variables.configBean.

    Registering event handlers in plugins is covered in the Plugins section.

    How To Register Event Handlers By Convention

    One way to register a Mura event handler is by convention. This means, if you create a .CFC file, and place it in a "known" location, Mura will automatically register it, and any event hooks contained within it will be executed when announced.

    The "Site" Event Handler

    Mura automatically checks every site for a specific file, labeled eventHandler.cfc,under the following location:

    {context}/sites/{SiteID}/eventHandler.cfc

    If this file is found, Mura will instantiate it on every front-end request. This means that any methods placed within this file should be specific to front-end requests. If you attempt to add any other request types here, for example, any events which occur only during the admin request lifecycle, they will not be executed.

    The "Theme" Event Handler

    Mura automatically checks the theme for a specific file, labeled eventHandler.cfc, under the following location:

    {context}/sites/{SiteID}/themes/{ThemeName}/eventHandler.cfc
    // OR
    {context}/themes/{ThemeName}/eventHandler.cfc
    

    Mura checks for this file during the onApplicationLoad event. When discovered, Mura instantiates it, and places the file into the application scope.

    Note: Since the file is only instantiated when the application loads, you must reload Mura anytime you make a change to the file itself.

    Content Type & Module Event Handlers

    Mura automatically scans both the content_types and modules directories for the following directory structure:

    ../model/handlers/{anyFilename}.cfc

    Any .cfc discovered under these ../model/handlers/ directories will be registered as event handlers. Currently, content_types and modules directories can reside under a site and/or a theme.

    // Sites
    {context}/sites/{SiteID}/content_types/{Type}_{Subtype}/model/handlers/
    {context}/sites/{SiteID}/modules/{module}/model/handlers/
    
    // Themes
    ../themes/{ThemeName}/content_types/{Type}_{Subtype}/model/handlers/
    ../themes/{ThemeName}/modules/{module}/model/handlers/

    Additional information regarding content types and modules can be found in the Mura Rendering section.

    How To Explicitly Register Event Handlers

    The basic syntax for explicitly registering a custom event handler is:

    m.getBean('pluginManager').addEventHandler( 
      {handlerObject or path}
      , {SiteID} 
    );
    

    For example, you may use one of the "known" registered event handlers, such as either the "Site" or "Theme" event handler. Then, using the onApplicationLoad event, you can register your custom event handler as follows:

    public any function onApplicationLoad(m) {
      // Assuming the customHandler.cfc file exists in the same directory as this file
      var myHandler = new customHandler();
    
      // Register the custom handler
      arguments.m.getBean('pluginManager').addEventHandler(
        myHandler
        , arguments.event.get('siteid')
      );
    }

    Entity Event Handlers

    Developers may register event handlers for specific beans/objects or entities such as content beans, user beans, or even your own custom entities/objects. This allows developers the ability to create more focused business logic and code clarity.

    For example, if you registered an event handler for "onRenderStart", your method will be invoked on each and every front-end page request, regardless of the content type, etc. Using targeted logic allows you to specify not only the object and type, you could also target a very specific object by loading it up, and attaching a custom event handler to it.

    To target a specific bean/object, we begin by using the "onApplicationLoad" event of a registered event handler.

    public any function onApplicationLoad(m} {
      // This is where our code will be
    }

    Then, we'll target a specific bean/object, and use either the "on" or "addEventHander" methods described below.

    on

    A bean/object helper method to dynamically register an event handler. Methods may be chained to attach multiple event handlers.

    Function Syntax

    m.getBean('someBean').on( eventName, fn )

    Parameters

    Parameter Type Req/Opt Description
    eventName string Req The name of the event you wish to register, without the "on" prefix. For example, if you wish to target the "onRenderStart" method, set this to "renderStart".
    fn function Req A function, or name of a function containing the code you wish to execute.

    Examples

    In this example, we're going to target a specific content bean, and attach a custom "onRenderStart" event handler to it.

    public any function onApplicationLoad(m) {
    
      m.getBean('content')
        .loadBy(title='Contact Us')
        .on('renderStart', function(m) {
          // do something
        });
    
    }

    In this example, we're targeting a user bean, and attaching a custom "onUserDelete" event handler.

    public any function onApplicationLoad(m) {
    
      m.getBean('user')
        .loadBy(useranme='steve')
        .on('userDelete', function(m) {
          // do something
        });
    
    }

    In this example, we're targeting the global config bean, and attaching a custom "onRenderEnd" event handler.

    public any function onApplicationLoad(m) {
    
      m.getBean('configBean')
        .on('onRenderEnd', function(m) {
          // do something
        });
    
      // OR
    
      m.globalConfig()
        .on('onRenderEnd', function(m) { 
          // do something   
         }); 
    
    }

    Note: When attaching event handlers to the global config bean, the event handler(s) will apply throughout the entire instance of Mura, across all sites.

    In this example, we're loading a content bean to target a specific form, and attaching a custom "onSubmitSave" event handler, and then chaining an additional handler for the "onSubmitResponseRender" event.

    public any function onApplicationLoad(m) {
    
      m.getBean('content')
        .loadBy(title='Information Request Form')
        .on('submitSave', function(m) {
          // do something
        })
        .on('submitResponseRender', function(m) { 
          // do something   
        });
    
    }

    In this example, we're targeting the global config bean, and attaching custom handlers for a custom ORM bean which will be invoked throughout all of Mura, regardless of which site is running.

    public any function onApplicationLoad(m) {
    
      m.getBean('configBean')
        .on('beforeWidgetSave', function(m) {
          // do something
        })
        .on('widgetSave', function(m) { 
          // do something   
        })
        .on('widgetSave', function(m) {   
          // do something   
        });
    
    }

    addEventHandler

    A bean/object helper method to dynamically register event handlers.

    Function Syntax

    m.getBean('someBean').addEventHandler( component )

    Parameters

    Parameter Type Req/Opt Description
    component struct Req A struct of key/value pairs of eventNames and functions to execute as event handlers for the specified eventName(s).

    Examples

    In this example, we're targeting the global config bean, and attaching custom handlers for a custom ORM bean which will be invoked throughout all of Mura, regardless of which site is running.

    public any function onApplicationLoad(m) {
    
      m.getBean('configBean')
        .addEventHandler({
          beforeWidgetSave = function(m) {
            // do something
          }
          , widgetSave = function(m) {
            // do something
          }
          , afterWidgetSave = function(m) {
            // do something
          }
        });
    
    }

    In this example, we're targeting a specific custom ORM bean and attaching some custom event handlers.

    public any function onApplicationLoad(m) {
    
      m.getBean('widget')
        .loadBy(id='someID')
        .addEventHandler({
          beforeSave = function(m) {
            // do something
          }
          , save = function(m) {
            // do something
          }
          , afterSave = function(m) {
            // do something
          }
        });
    
    }

    Mura's Event Log

    Mura has the capability to output an event log as a stack trace, including the time it took to execute, in milliseconds, directly to the browser. This feature is quite useful for troubleshooting and debugging, as well as discovering which areas of your code may benefit from applying performance optimization techniques.

    How to Enable/Disable the Stack Trace

    To enable the stack trace, you must first be logged in to Mura as an Administrator. Then, simply append /?showtrace=1 to the URL, and reload your browser. Once you do this, Mura will append a stack trace output to the end of your page, similar to the following example.

    Mura will then set a cookie, so you don't have to continually add it to each and every page you visit.

    If you wish to disable this feature, simply append /?showtrace=0 to the URL, and the stack trace should disappear after reloading your browser.

    How to Read/Use the Stack Trace

    The stack trace displays information useful for both debugging or troubleshooting, as well as identifying areas of code execution that might benefit from using some performance optimization techniques, such as caching, or possibly even rewriting the code to run more efficiently.

    Each item in the stack trace is listed in the order in which it was encountered during the execution of the request. Some items in the list simply display informational messages, and don't necessarily point to any specific line of code, while others output the entire path to the file or method being parsed.

    In the parenthesis to the right of each item in the list are two numbers. The number on the left indicates the number of milliseconds it took the server to parse the specific file or method, while the number on the right reflects the point in the total request (in milliseconds) when the file or method finished executing.

    So, for example, if you review the following stack trace, note item number 14.

    Item number 14 indicates that Mura was parsing the two_column_SR.cfm layout template, and it took the server a total of 334 milliseconds to execute, and completed its execution at the 482nd millisecond.

    Item number 15 indicates a method call to contentRenderer.dspPrimaryNav, which in turn was invoked during the execution of parsing the two_column_SR.cfm file. The dspPrimaryNav method finished executing at the 255th millisecond. Subsequent items were then executed in the order listed.

    While you may not necessarily have the ability to optimize some of Mura's core methods, you can see the two_column_SR.cfm layout template is code you have direct control over. Maybe some of the code within the layout template could benefit from using a caching strategy, or some other form of performance optimization.

    Custom Stack Trace Points

    As a Mura developer, you may add your own custom messages to the stack trace output. This is useful for inspecting how long it takes for your custom code to execute, quickly and easily.

    The example below illustrates how to add your own trace point(s).

    <!--- Place this either at the top of your file, or at the beginning of a block of code you wish to trace --->
    <cfset myTracePoint = $.initTracePoint('your filename, method name, or other description to identify this trace point goes here ... this is what will output in the Stack Trace') />
    
    <!--- file content or block of code goes here --->
    
    <!--- Place this either at the bottom of your file, or at the end of a block of code you wish to trace --->
    <cfset m.commitTracePoint(myTracePoint) />

    This is an example stack trace output, using the above code in the footer.cfm layout template of the default theme.

    Summary

    Throughout this section, we covered the differences between lifecycle events and contextual events, the Mura "event" scope, and the wide variety of possible events that may occur during any given request, as well as how to register your own event handlers targeting the specific event(s) related to the task(s) you wish to accomplish. Finally, you learned how to create your own custom events, and how to use Mura's event log for debugging and performance optimization purposes.

    We hope that as you completed this section, you have a much better understanding on how you, as a Mura developer, can add to, or in some cases, even replace, Mura's own business logic and functionality.

    Mura File Structure

    There are a few primary directories and files you'll want to be aware of in Mura. The image below displays the directory structure of Mura, highlighting its required directories and files. The directories and files highlighted in red may be updated when updating Mura.

    • Website Root
      • admin files should never be modified
      • config
      • core files should never be modified
      • modules
      • resource_bundles
      • sites
      • themes
      • .dockerignore
      • .gitignore
      • Application.cfc
      • README.MD
      • box.json
      • index.cfm

    Note: Unless explicitly stated to the contrary, none of the directories or files listed below should be modified.

    Directory or File Editable Description
    {context}/admin/ No These files should never be modified. The files located here comprise the view and much of the functionality used for the administrative features of Mura.
    {context}/config/ Yes This directory contains configuration files. See The "config" Directory section for more information.
    {context}/core/ No These file should never be modified. The files located here are essentially the beating heart of Mura. We'll discuss a few files specific to Theme Developers in other sections. That said, it's best to simply leave the files in this directory alone.
    {context}/sites/ Yes* The directory itself is required, however the contents may be edited and will not be overwritten when updating Mura. This directory will be covered in the "Site Files" section.
    {context}/themes/ Yes* The directory itself is required, however the contents may be edited and will not be overwritten when updating Mura. This directory was covered in the "Theme Layout Templates" section of the Theme Developer Guide.
    {context}/modules/ Yes* The directory itself is required, however the contents may be edited and will not be overwritten when updating Mura.
    {context}/resource_bundles/ Yes* The directory itself is required, however the contents may be edited and will not be overwritten when updating Mura.
    {context}/plugins/ Yes* The directory itself is required, however the contents may be edited and will not be overwritten when updating Mura. This directory is used by experienced developers when creating Mura plugins.
    {context}/Application.cfc No This file contains Mura's application-specific variables and settings.
    {context}/box.json No This file is used by the Mura Team to run various tests using CommandBox and TestBox.
    {context}/index.cfm No This is Mura's "start" file, and should not be modified.
    {context}/README.md No A simple README file. When viewed on Github, a badge from Travis CI will display whether or not the current build is passing its required tests.

     

    The "config" Directory

    This directory contains Mura's configuration files. The files and their purpose are listed below.

    Directory Editable Description
    {context}/config/Application.cfc No This file prevents site visitors from navigating directly to any of the .CFM files contained within this directory.
    {context}/config/cfapplication.cfm Yes Experienced developers may add custom Application.cfc variables and/or ColdFusion/CFML mappings here.
    {context}/config/README.md No This file contains information about the files contained in this directory.
    {context}/config/settings.ini.cfm Yes This is an extremely important file, and contains many of the default settings for Mura. View the settings.ini.cfm reference guide for details.

    The "settings.ini.cfm" File

    This extremely important file contains many configuration settings for Mura. The ".ini" file format is an informal standard for configuration files. It's a simple text file with a basic structure composed of sections with key-value pairs.

    Example

    [production]
    adminemail=steve@blueriver.com
    dbtype=mysql
    yourCustomKey=Your Value
    

    Semicolons (;) at the beginning of the line indicate a comment. Comment lines are ignored.

    Reference

    A reference for Mura's configuration settings is outlined below.

    Settings Section

    Key Type Default Description
    appreloadkey string appreload The URL variable used to trigger Mura to reload the entire application. For security purposes, it is highly recommended to change this setting for production environments.
    mode string production

    Sets the mode per the appropriate settings in the section defined as [production], [development], etc. A complete set of key-value pairs is required for each mode. Duplicate the default production set, and edit as necessary. Valid options:

    • production
    • development
    • staging
    • etc.

    Production Section

    Key Type Default Description
    admindomain string empty string Controls the URL for accessing the back-end administration area of Mura. Especially useful for when you wish to use SSL for the admin area.
    admindir string /admin A custom path to where the admin directory is located. The path must not be nested in a subdirectory (e.g., /some/custom/path is bad). Essentially, this setting merely allows you to rename, the admin directory, not necessarily move it. If you change this setting, you must reload Mura from the webroot, and not from the back-end administration area.
    adminemail string empty string Default email address of the primary administrator for the installation of Mura.
    adminssl boolean false

    Valid options:

    • false
      • SSL certificate is not installed for the administration domain.
    • true
      • SSL certificate is installed for the administration domain.
    aggregatecontentqueries boolean false

    This setting is only used when the "Extranet" setting is set to "On", as found under Site Settings > Modules tab > Extranet (Password Protection), and will only apply to "content" feed beans using the "aggregate" functionality.

    • false
      • Returns an error, unless the logged in user is a member of the "Admin" group or "Super User".
    • true
      • Protected content could be included in any "content" feed beans created using the "aggregate" functionality.
    allowautoupdates boolean true

    Determines whether links to update the Core and Site files are displayed. Valid options:

      • false
        • Do not display links to Update Mura Core, or Update Site(s)
      • true
        • Display links to Update Mura Core, and Update Site(s)
      allowedindexfiles string index.cfm, index.json, index.html Filenames allowed to be used with Mura's directory path URLs.
      allowlocalfiles boolean false When importing files, Mura checks to see if you allow files to be imported from the local file system.
      allowsimplehtmlforms boolean true If false, only FormBuilder forms will be allowed. This is often used for security purposes.
      assetdir string empty string A custom file path to where your site assets should be stored.
      assetpath string empty string The URL to where your assets are stored. Can be a domain or a root relative path.
      autodiscoverplugins boolean false If true, Mura will scan the plugins directory for any new plugins, and if found, will auto-register them.
      autoresetpasswords boolean false When using the "Lost Password" functionality, if true it will send a new password via email, otherwise if false, it will send a link via email to auto-login and prompt user to change password.
      autoupdatemode string default

      This feature is used when Mura is releasing a public Beta of the next version. Valid options include:

      • default
        • Don't update to Beta version, when performing an auto-update.
      • preview
        • Update to Beta version, when performing an auto-update.
      bcryptpasswords boolean true Determines whether or not passwords go through the BCrypt process. If false, passwords are hashed instead.
      clientmanagement boolean false This is an Application.cfc variable.
      clientstorage string empty string This is an Application.cfc variable.
      confirmsaveasdraft boolean true If true, a confirmation alert will appear when you navigate away from a content item without saving it.
      context string empty string If Mura is installed under a subdirectory of the webroot, you should enter the path here. Mura will typically populate this field automatically, but you may have to enter the information if you move Mura after being installed.
      cookiedomain string empty string Attribute to describe the scope of cookies. Read more
      cookiepath string empty string Another attribute to describe the scope of cookies. Read more
      customtagpaths string empty string A comma-delimited list of custom tag paths. For example, "c:\mapped1,c:\mapped2"
      datasource string empty string This is the datasource (DSN) Mura should use to access the database.
      dbcasesensitive boolean false If your database is case sensitive, (e.g., Oracle, Postgres), then set this to true
      dbpassword string empty string Do not use this, if you're able to set your able to define your datasource.
      dbtype string empty string

      The database type being used by the datasource. Valid options:

      • MS SQL
      • MySQL
      • Postgres
      • Oracle
      dbtablespace string USERS Used only when dbtype=Oracle
      dbusername string empty string Do not use this, if you're able to set your able to define your datasource.
      debuggingenabled boolean true Controls whether or not Mura should display CFML errors to the browser. If set to false, Mura will display the file found under errortemplate.
      defaultfilemode integer 777 If Mura is installed on a Unix based system, this setting will control the default permissions, in numeric notation.
      defermurajs boolean true If true, the script reference to Mura.js will include the "defer" attribute, which means it be executed after the document has been parsed. This will improve the page load performance time.
      enablemuratag boolean true At a global level, controls whether or not Mura will parse content for Mura [m] tags. May also be controlled at a site-level via the contentRenderer.cfc, on a site-by-site basis.
      encryptionkey string hash of getCurrentTemplatePath() Available for developers to use as an application-level storage for a custom encryption key.
      errortemplate string /muraWRM/config/error.html Specifies the location of a file Mura should use when a CFML error occurs, and debuggingenabled=false.
      filedir string empty string Optionally specify a custom directory to store Mura files, such as associated images (this does not include files uploaded via the web editor). Defaults to {context}/{SiteID}/cache/file/
      fmshowapplicationroot boolean true Controls whether or not the File Manager (CKFinder) displays the application root.
      fmshowsitefiles boolean true Controls whether or not the File Manager (CKFinder) displays site files.
      forceadminssl boolean true If true, Mura will force "https" protocol.
      hashurls boolean false If true, Mura will generate URLs with directory names as hashes.
      haslockablenodes boolean false If true, when a content manager is editing content, other content managers will be unable to edit the same content.
      hstsmaxage integer 1200 View https://en.wikipedia.org/wiki/
      HTTP_Strict_Transport_Security
      for more information.
      imageinterpolation string highQuality

      Specify a specific interpolation algorithm by name, by image quality, or by performance. See ImageResize for more information. Valid options:

      • highestQuality
      • highQuality
      • mediumQuality
      • highestPerformance
      • highPerformance
      • mediumPerformance
      • nearest
      • bilinear
      • bicubic
      • bessel
      • blackman
      • hamming
      • hanning
      • hermite
      • lanczos
      • mitchell
      • quadratic
      indexfileinurls boolean true

      Controls whether or not "/index.cfm" appears in Mura-generated URLs. Valid options:

      • false
        • Does not include "/index.cfm" in Mura-generated URLs. This option also requires the use of URL rewrite rules for your web server. See web.config.txt or htaccess.txt for more information.
      • true
        • Includes "/index.cfm" in Mura-generated URLs.
      javasettingsloadcoldfusionclasspath boolean true This is an Application.cfc variable.
      javasettingsloadpaths string /requirements/lib This is an Application.cfc variable.
      javasettingswatchextensions string jar,class This is an Application.cfc variable.
      javasettingswatchinterval integer 60 This is an Application.cfc variable.
      legacyappcfc boolean false Prior to v7.1, Application.cfc lifecycle files existed under the config/appcfc/ directory, and Mura Plugin developers sometimes linked to some of these files. To ease the upgrade path for legacy plugins, Mura will add stub files back to this location if this setting is set to true
      locale string server

      Renders dates and times per the selected locale. Valid options are:

      • server
      • client
      loginstrikes integer 4 The number of times a user can attempt to login before being locked out.
      mailserverip string empty string SMTP server address to use for sending email messages.
      mailserverpassword string empty string A password to send to SMTP servers that require authentication.
      mailserverpopport integer 110 POP port
      mailserversmtpport integer 25 TCP/IP port on which SMTP server listens for requests
      mailserverssl boolean false Whether to use Secure Sockets Layer
      mailservertls boolean false Whether to use Transport Level Security
      mailserverusername string empty string A username to send to SMTP servers that require authentication.
      maxarchivedversions integer 50 The number of versions to store in Mura.
      maxportalitems integer 1000 Limits the number of total content items to display for pagination purposes on Folder displays.
      maxsourceimagewidth integer 4000 Mura attempts to store the source image when uploading files. If the image size exceeds the width setting here in pixels, Mura will resize down to this setting.
      mfa boolean false Experimental. Multi-factor authentication setting. If set to true, Mura will send email to user with authentication token to use to login.
      mfaperdevice boolean false Experimental. Determines whether you have to use MFA only once per device.
      mfasendauthcode boolean true Experimental. Set to false, if sending MFA token via a custom method.
      notifywithversionlink boolean true Tells Mura when notifications for review are sent whether to send return link to the version history page or directly to the version edit form.
      ormautomanagesession boolean true This is a CFML ORM setting.
      offline404 boolean true Controls whether Mura returns a 404 status code for offline content.
      ormcfclocation string empty string This is a CFML ORM setting.
      ormdatasource string empty string This is a CFML ORM setting.
      ormdbcreate string update This is a CFML ORM setting.
      ormenabled boolean true This is a CFML ORM setting.
      ormeventhandling boolean true This is a CFML ORM setting.
      ormflushatrequestend boolean false This is a CFML ORM setting.
      ormlogsql boolean false This is a CFML ORM setting.
      ormsavemapping boolean false This is a CFML ORM setting.
      ormskipcfcwitherror boolean false This is a CFML ORM setting.
      ormusedbformapping boolean false This is a CFML ORM setting.
      ping boolean false

      Controls the scheduled task Mura creates for publishing scheduled content, sending scheduled emails, etc. Valid options:

      • true
        • Mura will attempt to create the scheduled task(s). However, some CFML servers do not allow this to be done programmatically.
      • false
        • Mura will not attempt to create any scheduled task(s).
      plugindir string empty string A custom filepath to where your plugins will be installed. Example: c:\inetpub\wwwroot\plugins
      port integer ${cgi.SERVER_PORT} The Port that Mura runs on, and is included in Mura generated links. The Port needs to be the same for both the front end and back end of the site.
      proxypassword string empty string Network proxy password for external http calls, if needed.
      proxyport integer ${cgi.SERVER_PORT} Network proxy port for external http calls, if needed.
      proxyserver string empty string Network proxy server for external http calls, if needed.
      proxyuser string empty string Network proxy user for external http calls, if needed.
      purgedrafts boolean true Whether or not to purge drafts when a new, active version is published.
      recaptchasitekey string empty string The global Google reCAPTCHA site key. Used if no setting is entered at a site level via Site Settings.
      recaptchasecret string empty string The global Google reCAPTCHA secret. Used if no setting is entered at a site level via Site Settings.
      recaptchalanguage string en The global Google reCAPTCHA language. Used if no setting is entered at a site level via Site Settings.
      rendermuraalerts boolean true Whether or not to display alerts in the Mura administration area.
      requesttimeout integer 1000 This is an Application.cfc variable.
      sameformfieldsasarray boolean false This is an Application.cfc variable.
      scriptprotect boolean true This is an Application.cfc variable.
      scriptprotectexceptions string body,source,params This is Mura's internal parsing for script protection. A comma-delimited list of fields to ignore for script protection.
      manageSessionCookies boolean true This determines whether Mura will manage session cookies.
      securecookies boolean false This is an Application.cfc variable.
      sessioncookiesexpires string never This is an Application.cfc variable.
      sendfrommailserverusername boolean true Whether or not to send email messages via the mailserverusername setting.
      sessionhistory integer 0 Number of days to be stored for session tracking stats.
      sessiontimeout integer 180 This is an Application.cfc variable.
      showadminloginhelp boolean true Whether or not to display help when attempting to log in to the Mura administration area.
      siteidinurls boolean false Whether or not to include the {SiteID} in links generated by Mura.
      sortpermission string editor

      Controls which Mura "role" has the ability to sort content. Valid options are:

      • editor
      • author
      strictfactory boolean true This controls the DI/1 strict setting. If true, DI/1 will throw an exception if it cannot resolve a bean implied by a constructor argument, setter name, or property name. If false, DI/1 simply calls logMissingBean() which writes the failure to the Java console.
      stricthtml boolean false If true, Mura will not allow HTML syntax to be used in class extension's extended attributes, unless explicitly excluded via the stricthtmlexclude setting.
      stricthtmlexclude string empty string If stricthtml=true, Mura will check this setting for a list of extended attributes to allow HTML syntax. It is not necessary to specify any extended attributes where its type is set to HTMLEditor.
      strongpasswordregex regex (?=^.{7,15}$)(?=.*\d)(?![.\n])(?=.*[a-zA-Z]).*$ If strongpasswords=true, Mura will use the regular expression (regex) entered here when users create their passwords.
      strongpasswords boolean false If true, Mura enforces the strongpasswordregex setting.
      tempdir string empty string A custom path for temporary storage for uploaded files.
      title string Mura CMS Text to prepend the Site Name in the <title> tag of the Mura Administrator.
      usedefaultsmtpserver boolean true Whether or not to use the SMTP server settings stored in the CFML administrator.
      usefilemode boolean false Option to specify that Mura set file permissions for Unix-based systems.
      windowdocumentdomain string empty string This setting controls the document.domain setting for Mura's administration UI when editing content via the front-end for potential cross-subdomain issues. See Same-origin policy on the Mozilla Developer Network for more information.

      The "core" Directory

      Once you've determined where Mura has been installed, you'll want to be able to identify Mura's "core" files. This is important to know, because the last thing you want to do is modify a file you shouldn't have touched to begin with.

      As you'll see later, Mura offers an easy way to keep your installation of Mura up-to-date, and if you're modifying "core" files, there's a very good chance your changes would be overwritten. So, in this section, we'll cover where "core" files reside, and which ones you can safely modify, to stay on the upgrade path.

      Mura Directory Structure

      There are a few primary directories and files you'll want to be aware of in Mura. The image below displays the "core" directory structure.

      Directory or File Editable Description
      {context}/core/appcfc/ No The files contained in this directory are included in Mura's main Application.cfc file. See The "appcfc" Directory section for details.
      {context}/core/docker/ No This directory contains files related to running Mura on Docker.
      {context}/core/modules/ No The directories and files located here represent the default display objects and modules used by Mura. This is covered more extensively in the Mura Modules/Display Objects section.
      {context}/core/mura/ No The files located here are essentially the beating heart of Mura. We'll take a deeper look at some of its directories and files later.
      {context}/core/setup/ No This directory contains files required during the installation process.
      {context}/core/templates/ No This directory contains "templates" used to create core files such as the settings.ini.cfm, robots.txt, and some site files such as the SitecontentRenderer.cfc, and Site eventHandler.cfc. These files are typically only used during the installation process.
      {context}/core/tests/ No This directory contains files for conducting unit tests.
      {context}/core/vendor/ No This directory contains third-party code which Mura relies on for various parts of its functionality such as CKEditor.
      {context}/core/.gitignore No The Mura Team uses Git for distributed version control, and this is the site .gitignore file used to specify intentionally untracked files.
      {context}/core/Application.cfc No This particular file merely prevents users from navigating to any of the .cfm or .cfc files directly via the browser.

      The "appcfc" Directory

      The files stored under {context}/core/appcfc/ contains code used in Mura's root Application.cfc.

      Each file represents either an Application.cfc event method, or the code that's invoked and/or executed in the specified application event. The CFML methods are all broken out into separate "{event}_include.cfm" and "{event}_method.cfm" files.

      For example, the onApplicationStart_method.cfm file simply contains the standard CFML onApplicationStart method, which in turn includes the onApplicationStart_include.cfm file.

      As you might have already figured out, this is useful for Mura developers creating plugins and/or applications, and wish to include Mura's logic in their own Application.cfc.

      You'll learn about Mura's event flow, and how you can inject your own business logic, in the Mura Events section.

      The "sites" Directory

      You can host multiple websites under one installation of Mura. Each website can have its own, unique domain, assets, groups, users, etc. That said, one important thing to keep in mind when working with theme development in Mura is to make sure you're working with the right directory.

      {context}/sites/

      Every Mura installation has a directory labeled "sites", located directly under the Mura root or context. This directory name should never be modified, under any circumstances. It contains one file, labeled the Application.cfc (see table below), and a minimum of one sub-directory.

      Directory or File Editable Description
      {context}/sites/Application.cfc Yes Required.
      {context}/sites/default/ Yes Required. Each Mura installation also has a minimum of one site, labeled the "default" directory. The "default" directory name itself should not be modified, nor should any other Mura-generated directory name located under the "sites" directory. Doing so will break functionality. See below for more information on the "default" directory.

      {context}/sites/v10/

      Every Mura installation has a directory labeled "default." This directory name should never be modified, under any circumstances. The directories and files located under the "default" directory comprise the "site" files for the "Default" site.

      In addition to holding all of the "site" files for the "Default" site, the directory label is also the "Site ID," also referred to as the "SiteID" (no space). To verify a site's SiteID, follow the steps below.

      1. From the back-end administration area of Mura, select Site Settings, then click Edit Settings.
      2. On the Site Settings screen, the first field located under the Basic tab is labeled "Site ID."
      3. The "Site ID" field is a "read only" field, and cannot be modified. This is intentionally designed to work this way.
      4. When creating a new site in Mura, you specify the Site ID. This is important, because whatever you enter into that field will become the directory name. So, do not use any punctuation, special characters, etc. when doing so. See the "How to Add a New Site" section for more information on creating a new site.

      The key to remember here is each site in Mura has a unique SiteID which also matches the directory name. So, the path to any particular site you desire to work with can be notated as {context}/sites/{SiteID}/.

      Note: Whenever you create a new site in Mura, all of the files located under the "default" directory are copied over to the newly created site's directory.

      {context}/sites/{SiteID}/

      As a Mura theme developer, you'll be spending most of your time working with files located within your theme. The directory structure of a Mura site is detailed below.

      Directory or File Editable Description
      {context}/sites/{SiteID}/assets/ Yes* Required. The "assets" directory stores any files uploaded through Mura's web file manager, currently CKFinder, when editing content via the user interface. It's best to simply use the web file manager itself to manage these files. However, we understand you may want, or need, to perform a bulk upload of files to a specific directory. When doing so, please be sure not to alter the primary directory structure, as you could potentially break links to files referenced here.
      {context}/sites/{SiteID}/cache/ No Required. You should not touch this directory. Any images, files, etc. uploaded as actual content or primary associated files are renamed and stored here. References to all files are also stored in Mura's database.
      {context}/sites/{SiteID}/modules/ Yes Optional. This directory contains the files Mura uses to render display objects in the browser. See The "modules" Directory section for more information.
      {context}/sites/{SiteID}/resource_bundles/ Yes Optional. This directory contains ".properties" files containing locale-specific data as a way of internationalising Mura. See The "resource_bundles" Directory section for more information.
      {context}/sites/{SiteID}/themes/ Yes Optional. This directory contains a site's themes. Themes stored under this directory are not accessible to any other site, unless the sites are using the same "Display Object Pool" as defined under Site Settings > Edit Settings > Share Resources tab. See The "themes" Directory section for more information.
      {context}/sites/{SiteID}/.gitignore No Optional. The Mura Team uses Git for distributed version control, and this is the site .gitignore file used to specify intentionally untracked files.
      {context}/sites/{SiteID}/contentRenderer.cfc Yes Optional. This is the Site contentRenderer.cfc file. You can add site-specific methods here. This is covered in the Mura contentRenderer.cfc section.
      {context}/sites/{SiteID}/eventHandler.cfc Yes Optional. This is the Site eventHandler.cfc file. You can add site-specific event handlers/listeners here. This is covered in the Event Handlers section.
      {context}/sites/{SiteID}/index.cfm No Required. This is the site's "start" file, and should not be modified.

      The "themes" Directory

      As a Mura Developer, you may spend some time working with themes. Themes may be located under one of the following locations:

      • Global Themes
        • {context}/themes/
        • Themes located under the "Global" themes directory will automatically be available as a theme option for all sites under the same Mura instance.
      • Site Themes
        • {context}/sites/{SiteID}/themes/
        • Themes located under a site's "themes" directory are available to the specified site, and/or any other sites sharing the same "Display Object Pool" as defined under Site Settings > Edit Settings > Shared Resources tab.

      Mura automatically checks these "themes" directories for any subdirectories. If Mura discovers any subdirectories, it assumes it is a theme, and uses the name of each directory as the "Theme Name". The "Theme Name" will then be listed as an option for authorized administrators to apply as a theme to their site. You may have as many themes as you wish under a "themes" directory.

      When Mura is first installed, if a theme does not exist under either of these directories, Mura will attempt to download a theme using the "defaultthemeurl" setting found in the {context}/config/settings.ini.cfm file.

       

      The "modules" Directory

      Mura "modules" can be used to either enhance your layout via the use of "display objects", and/or even add functionality to your site(s). Mura's  Modules allow you to quickly and easily add visual elements such as Collections, Components, Forms, navigational aids, and so much more.

      Lookup Hierarchy

      Mura uses the following lookup hierarchy when searching for modules, and aborts the lookup process once the target module has been located:

      • Registered Module Directory
        • {RegisteredModuleDirectory}/
        • A pre-registered module directory path.
      • Module
        • ../{module}/modules/
        • A nested "modules" directory nested within a known module.
      • Theme
        • ../{ThemeName}/modules/
        • Theme modules are only used when the specified theme is actively assigned to a site. Keep in mind themes may be located under either the global themes directory ({context}/themes/{ThemeName}), or under a site ({context}/sites/{SiteID}/themes/{ThemeName}).
      • Site
        • {context}/sites/{SiteID}/modules/
        • Site modules are shared across all themes within the specific site.
      • Global
        • {context}/modules/
        • Global modules are shared across all sites under a single Mura instance.
      • Core
        • {context}/core/modules/v1/core_assets/modules/
        • These are the "core" modules or display objects which may be copied and placed into any of the directories above to be safely modified.
           

      Note: You should not edit the "core" modules directly. If you do, you run the risk of losing your edits or changes whenever you update Mura to the latest version.

      Learn More

      Developers may customize existing modules, or even create custom modules, unique to your organization's needs such as a stock ticker, and even create full-blown applications to be embedded within a single page or an entire section of your site. You will learn more about creating custom modules in the Mura Modules & Display Objects section.

      The "resource_bundles" Directory

      Mura utilizes resource bundles to internationalize various areas of the user interface, making the code locale-independent.

      Resource bundles are .properties files, located under specified directories in Mura. These .properties files, or resource bundles, are named to indicate the language code, and country code. For example, the language code for English (en) and the country code for United States (US) would be represented as en_US.properties. If Mura cannot find a direct language and region match, it will search for a language match. If a locale's language cannot be found, Mura will fall back to its default of English, or en_US.properties.

      The file itself is comprised of key-value pairs. The keys remain the same throughout each of the .properties files, and the value is translated into the file's designated language. If Mura is searching for a specific key-value pair within a translation, and cannot locate it, Mura will fall back to the English translation.

      The image below illustrates the en_US.properties file side-by-side with the es_ES.properties file:

      Lookup Hierarchy

      Mura automatically searches for resource bundles under specific directories, and uses the key-value pair(s) found in the order outlined below. If a resource_bundles directory does not exist in the following locations, you may safely create one, and place your resource bundle files there.

      • Module
        • ../{module}/resource_bundles/
        • Module (aka "Display Object") resource bundles are used for the specific module itself.
      • Content Types
        • ../content_types/{type}_{subtype}/resource_bundles/
        • Content Types resource bundles are used for the specified content type.
      • Theme
        • ../{ThemeName}/resource_bundles/
        • Theme resource bundles are only used when the specified theme is actively assigned to a site.
      • Site
        • {context}/sites/{SiteID}/resource_bundles/
        • Site resource bundles are shared across all themes within the specified site.
      • Global
        • {context}/resource_bundles/
        • Global resource bundles are shared across all sites under a single Mura instance.
      • Core
        • {context}/core/modules/v1/core_assets/resource_bundles/
        • If the requested key-value pair is not defined in any of the locations above, Mura will use the "core" resource bundles located here for Mura's modules.

      Note: Admin-area resource bundles are located under {context}/core/mura/resourceBundle/resources/. However, as of v7.1, many key-value pairs are not able to be overwritten using the technique described above at this time. Allowing for this option is under consideration for a future version.

      Contribute

      If you would like to contribute to the translations project, please visit https://crowdin.com/project/muracms. Your help will be greatly appreciated!

      More Info

      More information on resource bundles, including how to customize and create your own key-value pairs, is covered in the Internationalization & Localization section.

      How to Update Mura

      Mura offers a way to keep your installation up-to-date, with the click of a button. Please be sure to back up the database, and your filesystem before attempting to use this feature. We also recommend performing this in a development or testing environment before using this feature in a "live" or "production" environment.

      Follow the steps below to update Mura.

      1. From the back-end administrator area of Mura, select Global Settings on the main navigation, then click Update Mura Core. Or, from the Global Settings area, you may also click the "Update Core Files" button, located near the top of the screen
      2. You should see the Update Mura Core confirmation window, with a warning instructing you not to update the core files unless you've back up your current Mura install. If you wish to proceed, select the "OK" button, otherwise, click "Cancel" to abort the update process
      3. If you selected the "OK" button, Mura will use the value of the autoupdateurl variable entered in the file located at {context}/config/settings.ini.cfm. No files are ever deleted during this process, files are only overwritten if they exist, or created if they don't. Once the process completes, Mura will display a message, along with the version you have been updated to as found in the {context}/box.json file
      4. When you're finished, you may click the List Sites button to return to the Global SettingsSites view

      Mura Modules

      Introduction

      Modules are content integrations that come in a variety of types: some are designed for laying out content, such as Headers and Containers, while others provide sophisticated dynamic functionality like Forms and Navigation. Some are placed onto a page via the Layout Manager, while others function in the background and have no visual component at all.

      Mura's baked-in modules are managed via the front-end user interface. Modules were formally referred to as "display objects" because often times, that's how the module itself is used. However, a Mura module does not have to contain a "display" of any kind. Modules can be complete applications, or simply contain some custom logic that is triggered based on a desired event.

      Note: If you aren't familiar with Mura's default modules (display objects), you should review the Modules of the Using Mura guide, before continuing in this section.

      Mura includes a variety of baked-in modules (display objects) by default while offering content managers the ability to quickly and easily add modules such as Collections, Components, Containers, Forms, Navigation, and many more.

      Each of Mura's modules contain some server-side logic, basic markup, styling, and sometimes include JavaScript. In this section, you'll learn how to safely modify this logic, markup, styling, and/or JavaScript, and do so without worrying about your changes getting overwritten whenever Mura's files are updated. In addition, you'll learn how to create your own, custom modules too.

      Custom Modules

      First, create a directory under a known or registered "modules" directory. For example, ../modules/mymodule/. Within the {module} directory, you may have the following files and/or directories:

      File or Directory Req/Opt Description
      index.cfm Optional If your module will be used as a "display object", this file will contain the body or view used by Mura for display file itself. See example "index.cfm" file section below.
      config.xml.cfm Optional This is the configuration file for the module itself. This file also allows you to include custom image sizes, class extensions, and more. See example "config.xml.cfm" file section below. Also visit the Elements Of The "config.xml.cfm" File section for information about available elements and attributes.
      configurator.cfm Optional This file allows developers to include a form with configuration options which appear on the module configuration panel when using the Inline Edit feature. See example "configurator.cfm" file section below.
      /model/ Optional If you wish to leverage Mura ORM, the module could have its own "model" directory, which in turn could also include a "beans" and/or "handlers" directory too. See Mura ORM Configuration section for more information about the "model" directory.
      /content_types/ Optional You may also include custom content types. This is useful for keeping content types directly related to specific module(s) together, in a unified directory structure. See Content Types section for more information.
      /modules/ Optional You may also nest custom modules. This is useful for keeping related modules together, under one, unified module itself. One caveat is that if you choose to display the nested object on the parent object, content managers will not be able to access the nested object's configurator, if it has one. Because of this, you may wish to either omit the "contenttypes" attribute from your config.xml.cfm's <mura ...> node, or explicitly add contenttypes="" so that the nested object will not appear in the module panel's UI. If you need configurable options for the nested object, you should include the options on the parent object's configurator. To include the nested object in the view of the parent object, use the following syntax: #m.dspObject(object='{objectName}', objectparams=objectparams)#.

      Example "index.cfm" File

      The example below illustrates what an "index.cfm" file could contain. However, you may include your own custom markup, code, and more.

      <cfparam name="objectparams.mytext" default="" />
      
      <cfoutput>
        <h2>My Object</h2>
      
        <cfif Len(objectparams.mytext)>
          <p>
            This object has a configurator, and the value of "objectparams.mytext" is:<br>
            <strong>#esapiEncode('html', objectparams.mytext)#</strong>
          </p>
        <cfelse>
          <!--- No value entered for "objectparams.mytext" --->
        </cfif>
      </cfoutput>

      If you wish to add any CSS or JavaScript to the browser for your module, you'll need to use MuraJS to help you. The example below leverages the loader() method to load both CSS and JavaScript. Simply add this code to your "index.cfm" file. You may have to change the path(s) to match your specific directory structure, and needs.

      <!--- Add the following to your "index.cfm" file --->
      <script>
        Mura(function(m) {
          m.loader()
            .loadcss(m.themepath + '/modules/myobject/my.css')
            .loadjs(
              m.themepath + '/modules/myobject/my.js',
              m.themepath + '/modules/myobject/other.js',
              function() {
                // Do something with the loaded JS, if desired
              }
            );
        });
      </script>
      

      Visit the MuraJS section to learn more about Mura.loader() capabilities.

      Rendering Via JavaScript

      If your module will be rendered using only JavaScript, your index.cfm file should only contain the following code:

      <cfscript>
          objectparams.render="client";
          objectparams.async="false";
      </cfscript>
      
      Setting Description
      objectparams.render
      • client: Tells Mura that the module should be rendered client-side. This is required for JS-rendered modules.
      • server: Tells Mura that the module should be rendered server-side. This is required for template-rendered modules.
      objectparams.async
      • true: Module will be rendered ascynronously.
      • false:Module will be rendered scynronously (the module will not request data from the server).

      Choosing this option means you'll have to load your JavaScript by using a custom event handler method, as shown below.

      // ../yourmodule/model/handlers/yourhandler.cfc
      
      component extends='mura.cfobject' {
      
        function onRenderStart(m) {
          // if script should be included in the <head>
         if(len(m.event('amp')) || !(isBoolean(m.event('amp')) && !m.event('amp'))){
            m.addToHTMLFootQueue('<script src="#m.siteConfig().getRootPath(complete=true,useProtocol=false)#/modules/yourmodule/assets/yourjs.js" #m.getMuraJSDeferredString()#></script>');
          }
      
          // OR
      
          // if script should be included before closing </body> tag
          if(len(m.event('amp')) || !(isBoolean(m.event('amp')) && !m.event('amp'))){
            m.addToHTMLFootQueue('<script src="#m.siteConfig().getRootPath(complete=true,useProtocol=false)#/modules/yourmodule/assets/yourjs.js" #m.getMuraJSDeferredString()#></script>');
          }
        }
      
      }
      

      Please also see the Modules and Display Objects With Mura.js section, for more information on rendering modules via JavaScript.

      Example "config.xml.cfm" File

      The example below illustrates a simple example of the "config.xml.cfm" file. Visit the Elements Of The "config.xml.cfm" File section for more details on available elements and attributes.

      <?xml version="1.0" encoding="UTF-8"?>
        <mura name="My Module" contenttypes="*" iconclass="mi-rebel">
          <!-- May also include other elements here -->
        </mura>

      In this example, the module will be available to all content types contenttypes="*". If you want to limit the module to specific content types, include them as a comma-delimited list contenttypes="page,folder,calendar". Also, when the module appears in the Layout Manager menu, the icon associated to the module is identified in the iconclass attribute iconclass="mi-rebel". You can use any of the available base Font Awesome icons, prefixed with mi- instead of fa- as per Mura conventions.

      Configurators

      Mura's code conventions for custom modules also include a markup standard for consistency and ease of development.

      Configurator Usage

      The configurator manages any special settings, values or controls for that particular usage of the display object, and is displayed in the sidebar in the front end editing view.

      When coding the "./configurator.cfm" rendering template for any custom display object, these conventions should be used to provide optimal results.

      Basic Input / Label Grouping

      Mura's front-end editor automatically provides the outer markup for any configurator forms rendered in the sidebar, including the html <form> wrapper. The contents of a configurator.cfm file consist of a few basic form elements to create the specific controls for the relevant display object.

      Within the <cf_objectconfigurator>  tag (see overview example below), each form input is paired with its corresponding <label> element, inside a wrapper with the "mura-control-group" class.

      For example, a standard text input:

      <!--- Text --->
      <div class="mura-control-group">
        <label>Text</label>
        <input type="text" name="exampletext" class="objectParam" value="#esapiEncode('html_attr',objectParams.exampletext)#"/>
      </div>
      

      Note the use of the objectParam class on the input itself, which is required for editing functionality, along with the dynamic value for the input. These conventions are used universally when creating custom display object configurators.

      Standard Input Types

      In addition to basic text inputs, other standard form elements can be used, with a similar syntax.
      For example, a textarea:

      <!--- TextArea --->
      <div class="mura-control-group">
        <label>Text Area</label>
        <textarea name="exampletextarea" class="objectParam">#objectParams.exampletextarea#</textarea>
      </div>
      

      A select box:

      <!--- Select --->
      <div class="mura-control-group">
        <label>Select</label>
        <select class="objectParam" name="exampleselect">
           <option value="red"<cfif objectParams.exampleselect is 'red'> selected</cfif>>Red</option>
           <option value="green"<cfif objectParams.exampleselect is 'green'> selected</cfif>>Green</option>
           <option value="blue"<cfif objectParams.exampleselect is 'blue'> selected</cfif>>Blue</option>     
        </select>
      </div>
      

      Configurator elements with variables or automated values may also be used. For example, a select box with dynamic options via cfloop:

      <!--- Select --->
      <div class="mura-control-group">
        <label>Select</label>
        <select class="objectParam" name="exampleselect">
          <option value="">Choose a Value</option>
          <cfloop list="1,2,3" item="i">
            <option <cfif objectParams.exampleselect eq i>selected </cfif>value="#i#">#i#</option>
          </cfloop>
        </select>
      </div>
      

       

      Checkbox and Radio Inputs

      One or more checkbox inputs can be grouped inside the same "mura-control-group" wrapper.
      A standard <label> element can be used to describe the group, followed by any number of checkbox inputs, grouped in a "checkbox-group" container. Each input is wrapped with a label element using the "checkbox" class.

      <!--- Checkboxes --->
      <div class="mura-control-group">
        <label>Checkboxes</label>
        <div class="checkbox-group">
        <cfloop list="Red,Green,Blue" item="i">
          <label class="checkbox"><input type="checkbox" class="objectParam" name="examplecheckbox" value="#lcase(i)#"<cfif listFindNoCase(objectParams.examplecheckbox,i)> checked</cfif>>#i#</label>
        </cfloop>
        </div>
      </div>

      Radio buttons use a similar syntax, nested inside a "radio-group" container, and labels with the "radio" class. 

      <!--- Radio Group --->
      <div class="mura-control-group">
        <label>Radio Buttons</label>
        <div class="radio-group">
          <label class="radio"><input type="radio" class="objectParam" name="exampleradio" value="true" <cfif objectParams.exampleradio> checked</cfif>/>Yes</label>
          <label class="radio"><input type="radio" class="objectParam" name="exampleradio" value="false" <cfif not objectParams.exampleradio> checked</cfif>/>No</label>
        </div>
      </div>
      


      Configurator Overview

      When utilizing these options in a single configurator.cfm file, your markup will look something like this:

      <!--- set defaults for form values --->
      <cfsilent>
        <cfparam name="objectParams.exampletext" default="">
        <cfparam name="objectParams.exampletextarea" default="">
        <cfparam name="objectParams.exampleselect" default="">
        <cfparam name="objectParams.exampleradio" default="false">
        <cfparam name="objectParams.examplecheckbox" default="">
        <cfparam name="objectParams.examplefile" default="">
      </cfsilent>
      
      <!--- cf_objectconfigurator tag adds default inputs --->
      <cf_objectconfigurator>
        <cfoutput>
      
          <!--- Text --->
          <div class="mura-control-group">
            <label>Text</label>
            <input type="text" name="exampletext" class="objectParam" value="#esapiEncode('html_attr',objectParams.exampletext)#"/>
          </div>
      
          <!--- TextArea --->
          <div class="mura-control-group">
            <label>Text Area</label>
            <textarea name="exampletextarea" class="objectParam">#objectParams.exampletextarea#</textarea>
          </div>
      
              <!--- Select --->
              <div class="mura-control-group">
               <label>Select</label>
               <select class="objectParam" name="exampleselect">
                <option value="">Choose a Value</option>
                <cfloop list="1,2,3" item="i">
                 <option <cfif objectParams.exampleselect eq i>selected </cfif>value="#i#">#i#</option>
                </cfloop>
               </select>
              </div>
      
          <!--- Checkboxes --->
          <div class="mura-control-group">
            <label>Checkboxes</label>
            <div class="checkbox-group">
            <cfloop list="Red,Green,Blue" item="i">
            <label class="checkbox"><input type="checkbox" class="objectParam" name="examplecheckbox" value="#lcase(i)#"<cfif listFindNoCase(objectParams.examplecheckbox,i)> checked</cfif>>#i#</label>
            </cfloop>
            </div>
          </div>
      
          <!--- Radio Group --->
          <div class="mura-control-group">
            <label>Radio Buttons</label>
            <div class="radio-group">
              <label class="radio"><input type="radio" class="objectParam" name="exampleradio" value="true" <cfif objectParams.exampleradio> checked</cfif>/>Yes</label>
              <label class="radio"><input type="radio" class="objectParam" name="exampleradio" value="false" <cfif not objectParams.exampleradio> checked</cfif>/>No</label>
            </div>
          </div>
      
              <!--- Custom file selector --->    
              <div class="mura-control-group">
                  <label>Select File</label>
                  <input name="myfile" class="objectParam" value="#esapiEncode('html_attr',objectparams.examplefile)#"><br/>
                  <button type="button" class="btn mura-finder" data-target="myfile" data-completepath=false>Select File</button><br/>
              </div>
      
              <!--- Custom modal window w/ control button --->
              <div class="mura-control-group">
                  <button type="button" class="btn" id="open-custom-modal">Open Modal</button>
              </div>
              <input name="trim-params" class="objectParam" type="hidden" value="true"/>
              <script>
                  $(function(){
                      $('##open-custom-modal').click(function(){
                          siteManager.openDisplayObjectModal('examples/modal.cfm');
                      });
                  });
              </script>
      
          </cfoutput>
      </cf_objectconfigurator>
      

      Note the additional "custom css classes" input. This is added automatically by Mura, with the supplied class names applied to the display object container. 

      Available Custom Tags

      In order make it so you don't really need to worry about the configurator markup in most cases the following custom tags are available.

      <cfimport prefix="ui" taglib="/core/mura/customtags/configurator/ui"/>
      <cfscript>
          param name='objectparams.myhtml' default='';
          param name="objectparams.myselect" default="test2";
          param name="objectparams.mytext" default="";
          param name="objectparams.mytextarea" default="";
          param name="objectparams.mycolor" default="";
          param name="objectparams.myimage" default="";
          param name="objectparams.myfile" default="";
          param name="objectparams.myradio" default="";
          param name="objectparams.mycheckbox" default="";
          param name="objectparams.mydispenser" default="";
      
      
          selectoptions=["test1","test2"];
      
          radiooptions=["test1","test2"];
      
          checkboxoptions=["test1","test2"];
      
          /*option attribute can be of objects to have different value and names*/
          dispenseroptions=[
              {
                  name="label 1",
                  value="value 1"
              },
              {
                  name="label 2",
                  value="value 2"
              }
          ];
      </cfscript>
      <cf_objectconfigurator>
          <ui:text 
              label="My Text"
              name="mytext"
              value="#objectparams.mytext#"/>
           <ui:textarea
              label="My Text Area"
              name="mytextarea"
              value="#objectparams.mytextarea#"/>
          <ui:color
              label="My Color"
              name="mycolor"
              value="#objectparams.mycolor#"/>
          <ui:image
              label="My image"
              name="myimage"
              value="#objectparams.myimage#"/>
          <ui:file
              label="My File"
              name="myfile"
              value="#objectparams.myfile#">
          <ui:html
              label="My HTML"
              name="myhtml"
              value="#objectparams.myhtml#"/>
          <ui:select
              label="My Select"
              name="myselect"
              options="#selectoptions#"
              value="#objectparams.myselect#"/>
          <ui:radio 
              label="My Radio Group"
              name="myradio"
              options="#radiooptions#"
              value="#objectparams.myradio#"/>
          <ui:checkbox 
              label="My Checkbox Group"
              name="mycheckbox"
              options="#checkboxoptions#"
              value="#objectparams.mycheckbox#"/>
          <ui:modal 
              label="My Modal"
              modalpath="mymodules/mymodal.cfm"/>
          <ui:dispenser 
              label="My Dispenser"
              name="mydispenser"
              options="#dispenseroptions#"
              value="#objectparams.mydispenser#"/>
      </cf_objectconfigurator>
      

      Defining Configurators in a mura.config.json File

      When creating decoupled frontends to Mura you can use the mura.config.json to define modules. Since you don't have access to created the Mura container you can set a configurator attribute of the module object. They can be defined in multiple ways.

      An important distinction in configurators defined and used in decoupled code is that you don't need to explicitly send a value attribute. The framework retrieves values from the the current module's configuration for you.

      Array of Objects

      The array of obects simply matches the attributes that you would set in a a custom tag invocation plus a "type" attribute that tells Mura what ui type to be used

      {
      	"global":{
      		"modules":{
      			"Examplejs":{
      				"name":"Examplejs",
      				"contenttypes":"*",
      				"configurator":[
      					{"type":"text","name":"mytitle","label":"My Label"}
      				]
      			}
              }
          }
      }
      
      URL to Static HTML or JS Object

      The return value of the http request will be rendered as the configurator. It can be static html or a valid JSON object

      {
      	"global":{
      		"modules":{
      			"Examplejs":{
      				"name":"Examplejs",
      				"contenttypes":"*",
      				"configurator":"https://localhost/app/configurator.html"
      			}
              }
          }
      }
      
      Static HTML
      {
      	"global":{
      		"modules":{
      			"Examplejs":{
      				"name":"Examplejs",
      				"contenttypes":"*",
      				"configurator":"escaped configurator markup"
      			}
              }
          }
      }
      

      Model

      The Model directory is where you will place any custom Mura ORM Objects or event handlers. These objects are instantiated by convention, meaning that any objects you place in here will be collected by Mura at start-up and integrated into the standard rendering Lifecycle.

      As always, you should practice thoughtful naming conventions when integrating custom Mura ORM Objects. Using a module-name prefix is good practice.

      Mura Plugins

      Mura plugins are an update-safe way to extend Mura and add new functionality. Plugins allow developers to distribute their code across multiple sites, and multiple Mura installations. They're also a great way to integrate existing and/or legacy applications into Mura.

      In addition, Mura is licensed under the GNU General Public License (GPL) version 2, and includes a special exception clause which allows developers to create independent software modules (plugins, themes, and bundles), and to distribute them without Mura under the license of their choice, provided they follow some specific guidelines such as not altering any default objects in Mura's database, not altering the Mura logo within Mura, and not altering some specific directories within Mura (such as the "admin" directory, etc.). Refer to the license itself for more details.

      Finally, Mura plugins allow for a custom administrator area, where you may also control which groups have access to the plugin itself, as well as the ability to enable the plugin on a site-by-site basis.

      In this section, you'll learn where to find open source plugins, how to install and manage plugins, and how to create your own Mura plugin. In addition, we'll compare Mura plugins to Mura's modules, and give you some tips on determining which option you might want to choose for extending Mura CMS and/or integrating your custom application. Finally, we'll cover some of the available "advanced" options for Mura plugin developers.

      Available Plugins

      At this time, there is not an "official" repository for Mura plugins. So, in the mean time, if developing open source plugins, we suggest developers post them to Github, and include "Mura plugin" in the description so that it's easy to discover via searching Github's repositories.

      You may also find some of the more popular Mura plugins at http://www.getmura.com/downloads/mura-cms-plugins/. If you would like to have your plugin considered for addition to the list, please don't hesitate to ask.

      Plugin Anatomy

      Mura plugins are distributed as ".zip" files. However, the .zip archive itself contains the underlying files which comprise the plugin. In this section, we'll cover the anatomy of a Mura plugin, and the directories and files Mura requires in order to function properly.

      When you begin creating a plugin, all of your plugin's directories and files must be packaged together under a common directory, and will ultimately be deployed under Mura as:

      {context}/plugins/{YourPluginDirectoryName}/

      Be careful when naming your plugin's containing directory, as some operating systems will not accept special characters in the name. This is also important, because Mura will automatically create a CFML Application variable using "this.mappings" to your plugin using the value of your directory name.

      A basic directory structure of a Mura plugin is detailed below, and assumes all listed directories/files are located under {context}/plugins/{YourPluginDirectoryName}/

      Directory or File Req/Opt Description
      /plugin/ Req This directory is required, and contains configuration files required for plugin registration and deployment.
      /plugin/config.xml.cfm Req Technically, this is the only required file, and contains the configuration settings and data required for your plugin.
      /plugin/plugin.cfc Opt If this file exists, Mura will attempt to invoke specific methods during various phases of the plugin deployment, update, and/or deletion lifecycles. This allows developers to do things such as create custom database tables, connect to your custom API for authentication, and more.
      /plugin/config.cfm Opt This file is completely optional, and merely contains some plugin configuration variables to be included in your application, if desired.
      index.cfm Opt While this file is technically optional, it is highly recommended you include it. When an administrator navigates to your plugin via the back-end administration area, this is the file that will be rendered. So, if it doesn't exist, administrators will be presented with an error.
      license.txt Opt If this file exists, the content will be used as an End Users License Agreement (EULA). When an administrator deploys the plugin, they will be presented with the EULA, and have to "accept" the terms before continuing to install and deploy the plugin.
      /content_types/ Opt If this directory exists, Mura will automatically and discover any custom content types found within it, and register them accordingly to any sites the plugin has been enabled for. See the Mura Content Types section for more information.
      /modules/ Opt If this directory exists, Mura will automatically discover any modules found within it, and register them accordingly to any sites the plugin has been enabled for. See the Mura Modules section for more information.

      Note: By default, Mura does not automatically scan for a "model" directory within plugins. This is intentional to avoid any collisions, as your plugin may have its own bean factory which scans for this directory as well.

      The files outlined above represent a very basic plugin. Your plugin may contain many more directories and files than those listed here. Be sure to review the links for each file noted above for more details on their importance, and why may wish to include them in your own plugin.

      Outside of the required and recommended directories and files, your plugin can be a completely separate, and independent application. If your application will merely be a portal of some kind, then maybe all you'll need is the "index.cfm" file and "config.xml.cfm" file.

      However, in most cases, you're going to want to include some functionality to either add to, or even replace, Mura's business logic. As such, you'll want to do things such as register a custom event handler, so you can also register a custom "model" directory, and more. Be sure to review The Plugin's "config.xml.cfm" File section for more details.

      The Plugin's "config.xml.cfm" File

      This file is where you create the required plugin setup variables. In addition to that, you can optionally register custom event handlers, class extensions, custom image sizes, custom tag paths, custom mappings, and more.

      If you haven't already done so, you should familiarize yourself with the Elements Of The "config.xml.cfm" File section. Each of the subordinate elements within the <mura> element apply to plugins. However, instead of a top level <mura> element, the document should contain the mandatory <plugin> element. There are no attributes available for this element.

      Plugin-Specific Subordinate Elements

      In addition to the elements available to the standard config.xml.cfm file, there are many other elements available for plugins. Please review the list below for details, and examples.

      Element Req/Opt Description
      name Req This is the default "Name (Alias)" of the plugin. When an administrator installs/deploys the plugin, they are given the opportunity to update this value.
      package Req This should be a unique, variable-safe string to use as way to reference the plugin. This value is used for the directory name when the directoryFormat element is set to the recommended value of 'packageOnly'.
      directoryFormat Req This setting controls the naming format for the plugin directory itself. Valid options are 'default' (names your plugins as {packageName}_{autoIncrement}) or 'packageOnly' (names your plugins using the value entered in package element. The recommended value to use is 'packageOnly'.
      loadPriority Req This setting determines the order plugins will be invoked during the onApplicationLoad event. This allows plugins to use other plugins as services or dependencies. However, this does not affect the order in which regular events are triggered. Valid options are 1 through 10.
      version Req May contain any versioning values you wish. This is merely meta data for users of your plugin to identify which version they are currently using.
      provider Req The name of the creator or organization who developed the plugin.
      providerURL Req The URL of the creator or organization who developed the plugin.
      category Req Most plugins fall under either 'Application' or 'Utility'. However, you may enter any string value desired. If anything other than 'Application' or 'Utility', a new tab will be displayed with the category name when viewing a site's applied plugins.
      eventHandlers Req See eventHandlers Element below.
      ormCFCLocation Opt May contain a list of paths where Mura should look for custom ColdFusion ORM components. This does not pertain to Mura ORM.
      customTagPaths Opt May contain a list of paths where Mura should look for ColdFusion custom tags.
      autoDeploy Opt This setting works with Mura's plugin auto-discovery feature. If true, whenever Mura reloads, it will auto-install the plugin, and apply it to any sites identified in the siteID element. If false, or if it's not defined, Mura will simply deploy the plugin with the default setting values, and a Super Admin will need to log in and manually complete the deployment process.
      siteID Opt This setting works in conjunction with the autoDeploy element. May contain a comma-delimited list of SiteIDs that you would like to assign the plugin to during the auto-deploy process.
      settings Opt See Settings Element below.
      mappings Opt See Mappings Element below.

      eventHandlers Element

      This is where you register your event handlers.

      <eventHandler> is a sub-element of <eventHandlers>

      The following table describes available attributes of the <eventHandler> element. Technically speaking, developers really only have to register one (1) event, onApplicationLoad. Then, within the onApplicationLoad event, you can register any other handlers you wish, including the .CFC that the onApplicationLoad event is in.

      Note: See the Example Plugin Event Handlers section for more information on plugin event handler files themselves.

      Attribute Req/Opt Description
      event Req The name of the Mura event you wish to register. For example, onApplicationLoad.
      component Req The dotted-notation path to where the .CFC file resides. The path is relative to the plugin directory. So, if the .CFC file is located under {context}/plugins/yourplugin/model/handlers/eventHandler.cfc, the value would be "model.handlers.eventHandler".

      Example <eventHandler> Element

      <eventHandlers>
      
        <eventHandler event="onApplicationLoad" 
                      component="model.handlers.eventHandler" />
      
      </eventHandlers>

      Settings Element

      Settings allows you to collect additional data for your plugin. For example, if you wish to collect an API key that you issued to the user, you can do that using a custom setting.

      <setting> is a sub-element of <settings>

      The following table describes available elements of the <setting> element.

      Element Req/Opt Description
      name Req This is the variable name of your attribute, which you will be able to then access using the pluginConfig.getSetting('{name}') syntax.
      label Req The text value entered here will be displayed as the label for the form field.
      hint Opt The string entered here will be used to display help text to end users when completing the form field.
      type Req Determines the type of form field to be displayed. Valid options are:
      • text
      • radioGroup
      • textArea
      • select
      • multiSelectBox
      required Req If true, the form field will be required.
      validation Req Determines the type of validation to apply to the form field. Valid options are:
      • none
      • email
      • date
      • numeric
      • regex
      regex Opt If the value for validation is regex, enter a JavaScript regular expression here.
      message Opt The string entered here will display if the form field fails validation or is required and no value has been entered.
      defaultvalue Opt The value entered here will be used as the default value for the form field.
      optionlist Opt If the form field type is a radioGroup, select, or multiSelectBox, you may enter a carat ("^") delimited list of values to use as the value to be stored in the database. For example: <optionlist>true^false</optionlist>
      optionalabellist Opt If the form field type is a radioGroup, select, or multiSelectBox, you may enter a carat ("^") delimited list of values to use as the labels to be used for display. These should coordinate with the values entered in optionlist. For example <optionlabellist>Yes^No</optionlabellist>

      Example <settings> Element

      <settings>
      
          <setting>
              <name>country</name>
              <label>Country</label>
              <hint>The country where you live</hint>
              <type>select</type>
              <required>true</required>
              <validation>none</validation>
              <regex></regex>
              <message>Please select a Country</message>
              <defaultvalue>US</defaultvalue>
              <optionlist>US^UK^FR</optionlist>
              <optionlabellist>United States^United Kingdom^France</optionlabellist>
          </setting>
      
      </settings>
      

      Mappings Element

      Mappings allow you to create custom ColdFusion mappings.

      <mapping> is a sub-element of <mappings>

      The following table describes available attributes of the <mapping> element.

      Attribute Req/Opt Description
      name Req The name to use for the CFML mapping.
      directory Req A plugin-relative path to your desired directory.

      Example <mappings> Element

      <mappings>
        <mapping name="yourmapping" directory="somedir/anotherdir" />
      </mappings>
      

      Mappings will automatically be bound to the directory your plugin is installed, so the above example would refer to: {context}/plugins/{package}/somedir/anotherdir/.

      Example config.xml.cfm

      <?xml version="1.0" encoding="UTF-8"?>
          <plugin>
              <name>Your Plugin</name>
              <package>yourplugin</package>
              <directoryFormat>packageOnly</directoryFormat>
              <loadPriority>5</loadPriority>
              <version>0.0.1</version>
              <provider>Blue River</provider>
              <providerURL>http://blueriver.com</providerURL>
              <category>Application</category>
      
              <!-- Event Handlers -->
              <eventHandlers>
                  <eventHandler event="onApplicationLoad"
                                component="model.handlers.muraplugin" />
              </eventHandlers>
      
              <!-- CFML Mappings -->
              <mappings>
                <mapping name="yourmapping" directory="somedir/anotherdir" />
              </mappings>
      
              <!-- Settings -->
              <settings>
      
                  <setting>
                      <name>country</name>
                      <label>Country</label>
                      <hint>The country where you live</hint>
                      <type>select</type>
                      <required>true</required>
                      <validation>none</validation>
                      <regex></regex>
                      <message>Please select a Country</message>
                      <defaultvalue>US</defaultvalue>
                      <optionlist>US^UK^FR</optionlist>
                      <optionlabellist>United States^United Kingdom^France</optionlabellist>
                  </setting>
      
              </settings>
      
              <!-- Image Sizes -->
              <imagesizes>
                <imagesize name="myimage"
                           width="600"
                           height="400" />
              </imagesizes>
      
              <!-- Class Extensions -->
              <extensions>
                  <!-- <extension> elements -->
              </extensions>
          </plugin>
      

      Example Plugin Event Handlers

      Plugin event handlers should "mura.plugin.pluginGenericEventHandler". The example below illustrates how to register the event handler using the onApplicationLoad event, so that any other Mura events may also be included in the file, and Mura will automatically invoke them when announced, using the pluginConfig.addEventHandler(this) method.

      component extends='mura.plugin.pluginGenericEventHandler' output=false {
      
          public any function onApplicationLoad(required struct m) {
              // Register all event handlers/listeners of this .cfc with Mura CMS
              variables.pluginConfig.addEventHandler(this);
          }
      
          // Add any event handlers here
      
      }

      If you wish to register another event handler .CFC file, you can follow the example below.

      component extends='mura.plugin.pluginGenericEventHandler' output=false {
      
          public any function onApplicationLoad(required struct m) {
              // Register all event handlers/listeners of this .cfc with Mura CMS
              variables.pluginConfig.addEventHandler(this);
      
              // Register another event handler
              // Assumes you have a customHandler.cfc located under the same directory.
              var anotherEventHandler = new customHandler();
              variables.pluginConfig.addEventHandler(anotherEventHandler);
          }
      
          // Add any event handlers here
      
      }

      Register a Custom Model Directory

      This example shows how to register a custom "model" directory.

      component extends='mura.plugin.pluginGenericEventHandler' output=false {
      
          public any function onApplicationLoad(required struct m) {
              // Register all event handlers/listeners of this .cfc with Mura CMS
              variables.pluginConfig.addEventHandler(this);
      
              // Register a custom 'model' directory
              variables.pluginConfig.registerModelDir(
                dir='path/from/plugin/to/model/dir'
              );
          }
      
          // Add any event handlers here
      
      }

      Register a Custom Modules Directory

      This example shows how to register a custom "modules" directory.

      component extends='mura.plugin.pluginGenericEventHandler' output=false {
      
          public any function onApplicationLoad(required struct m) {
              // Register all event handlers/listeners of this .cfc with Mura CMS
              variables.pluginConfig.addEventHandler(this);
      
              // Register a custom 'module' directory
              variables.pluginConfig.registerModuleDir(
                dir='path/from/plugin/to/modules/'
              );
          }
      
          // Add any event handlers here
      
      }

      Register a Custom Content Types Directory

      This example shows how to register a custom "content_types" directory.

      component extends='mura.plugin.pluginGenericEventHandler' output=false {
      
          public any function onApplicationLoad(required struct m) {
              // Register all event handlers/listeners of this .cfc with Mura CMS
              variables.pluginConfig.addEventHandler(this);
      
              // Register a custom 'content_types' directory
              variables.pluginConfig.registerContentTypeDir(
                dir='path/from/plugin/to/content_types/'
              );
          }
      
          // Add any event handlers here
      
      }

      The "plugin.cfc" File

      If this file exists, Mura will attempt to invoke specific methods during various phases of the plugin deployment, update, and/or deletion lifecycles. This allows developers to do things such as create custom database tables, connect to your custom API for authentication, and more.

      The example code below illustrates a basic plugin.cfc file.

      component extends='mura.plugin.plugincfc' output=false {
      
          // pluginConfig is automatically available as variables.pluginConfig
      
          public void function install() {
              // Do custom installation stuff
          }
      
          public void function update() {
              // Do custom update stuff
          }
      
          public void function delete() {
              // Do custom delete stuff
          }
      
          public void function toBundle(pluginConfig, bundle, siteid) output=false {
              // Do custom toBundle stuff
          }
      
          public void function fromBundle(bundle, keyFactory, siteid) output=false {
              // Do custom fromBundle stuff
          }
      
      }
      

      The "config.cfm" File

      This file is completely optional. However, if you have an ultra-simple plugin, this file can come in handy. In short, it simply contains a few variables you can share across the administrative pages of your plugin using the "cfinclude" method.

      The Mura Scope Variable

      As a Mura developer, you probably want a way to access the Mura Scope. Since your plugin is running under the Mura application itself, you will have access to its application scope variables and methods. However, when accessing the plugin directly, the Mura Scope itself will not be defined. So, using the information we learned from the Custom Instance of the Mura Scope section, we can easily create a variable to reference the Mura Scope using the code shown below to place in our config.cfm file.

      // ./plugin/config.cfm
      
          // Mura Scope
          if ( !IsDefined('m') ) {
              siteid = session.keyExists('siteid') ? session.siteid : 'default';
              m = application.serviceFactory.getBean('m').init(siteid);
          }
      

      The pluginConfig Variable

      Now that we have a reference to the Mura Scope, as a Mura plugin developer, we'll also want access to the pluginConfig. By having access to the pluginConfig, the following methods are available to you.

      Method Parameters Description
      getDirectory none Returns the directory name of the plugin.
      getPackage none Returns the value from the config.xml.cfm <package> node.
      getName none Returns the Name (Alias) data entered when the plugin was installed and deployed.
      getApplication none

      Plugins have access to their own, exclusive application scope. Useful for creating and setting custom application variables associated only with your plugin. For example:

      myPluginApplication = pluginConfig.getApplication();
      myPluginAppication.set('colors', ['Red','White','Blue']);
      pluginColors = myPluginApplication.get('colors');
      

      If you wish to purge your plugin's application variables, you may use the following syntax:

      // 'true' = purge the plugin's application variables
      myPluginApplication = pluginConfig.getApplication(true);
      getSession none

      Plugins have access to their own, exclusive session scope. Useful for creating and setting custom session variables associated only with your plugin. For example:

      myPluginSession = pluginConfig.getSession();
      myPluginSession.set('fruit', ['Grapes','Oranges']);
      pluginFruit = myPluginSession.get('fruit');

      If you wish to purge your plugin's session variables, you may use the following syntax:

      // 'true' = purge the plugin's session variables
      myPluginSession = pluginConfig.getSession(true);
      
      getAssignedSites none

      Returns a recordset of of SiteIDs the plugin has been enabled for.

      <cfset rsSites = pluginConfig.getAssignedSites()>
      
      <ul>
        <cfloop query="rsSites">
          <li>#siteid#</li>
        </cfloop>
      </ul>
      
      getSettings none Returns a struct of any plugin settings collected via the config.xml.cfm <settings> elements.
      getSetting property Provides a way to obtain a specific plugin setting collected via the config.xml.cfm <settings> elements. Returns the value for the property parameter which was passed in.

      Let's add some code to our config.cfm file to give us access to a variable named pluginConfig.

      // ./plugin/config.cfm
      
          // Mura Scope
          if ( !IsDefined('m') ) {
              siteid = session.keyExists('siteid') ? session.siteid : 'default';
              m = application.serviceFactory.getBean('m').init(siteid);
          }
      
          // Plugin Config
          if ( !IsDefined('pluginConfig') ) {
              pluginConfig = m.getBean('pluginManager').getConfig('{YourPluginPackagNameGoesHere}');
          }
      

      Restricting Access

      Finally, in most cases, you'll want to make sure that groups which do not have access to the plugin's administrator area will not be able to access it. The pluginConfig variable has a special method labeled currentUserAccess, which checks the currently logged in user to see if any of the groups they belong to have been granted access or not. If it returns true, then they have been granted access. If false, then they have not been granted permissions to the plugin itself.

      Using this method, we can easily add one more line of code to our config.cfm file, and redirect unauthorized users to the login screen. As a developer, you can probably come up with other options as well.

      // ./plugin/config.cfm
      
          // Mura Scope
          if ( !IsDefined('m') ) {
              siteid = session.keyExists('siteid') ? session.siteid : 'default';
              m = application.serviceFactory.getBean('m').init(siteid);
          }
      
          // Plugin Config
          if ( !IsDefined('pluginConfig') ) {
              pluginConfig = m.getBean('pluginManager').getConfig('{YourPluginPackagNameGoesHere}');
          }
      
          // Check User Access ~ Redirect if Not Authorized
          if ( !pluginconfig.currentUserAccess() ) {
              location(
                url=m.globalConfig('context') & '/admin/index.cfm?muraAction=clogin.main'
                , addtoken=false
              );
          }
      

      Summary

      If you have a fairly simple plugin, the config.cfm file is a pretty easy way to include common variables and logic throughout your plugin's administrative pages. For more complex plugins, you may wish to use an Application.cfc file instead. An example may be found at https://github.com/stevewithington/MuraPlugin/blob/develop/Application.cfc. If you would like to use a front-end controller framework, be sure to check out MuraFW1, which is a base Mura CMS plugin using Framework One (FW/1) as its application framework.

      The "index.cfm" File

      While technically optional, it is highly recommend to include the "index.cfm" file in the root of your plugin. The primary reason behind this is because when an administrator navigates to your plugin via the back-end administration area, this is the file that will be rendered. So, if it doesn't exist, administrators will be presented with an error.

      Also, the "index.cfm" file is the primary view for your plugin in general, not just for administrators. For example, if your plugin is installed under {context}/plugins/yourplugin/, when a user navigates directly to your plugin's directory, such as http://www.yourdomain.com/plugins/yourplugin/, the "index.cfm" file will be presented to the user.

      In other words, you may essentially put whatever you want in this file. For example:

      <!DOCTYPE html>
      <html>
          <head>
              <meta charset="utf-8">
              <title>YourPlugin</title>
          </head>
          <body>
              <h1>Hello from YourPlugin!</h1>
          </body>
      </html>

      In some rare cases, this may be exactly what you want to happen. Maybe you merely want to host your application alongside Mura on the same server, and if it's a completely separate and independent site, then this might be as far as you take it. However, this is not the typical scenario.

      Your Application Entry Point

      Often, developers will want to use the plugin's "index.cfm" as the entry page to their application, while also being able to work with Mura CMS functionality, or extending its capabilities.

      You may also use the "index.cfm" file as part of your front-end controller application. For example, maybe you're using a CFML framework such as Framework One (FW/1). If you're interested in doing this, please check out MuraFW1, a base Mura CMS plugin using FW/1 as its application framework.

      While you can easily add display objects and custom content types, you may also want to register a custom event handler, and then register a custom "model" directory. To do this, please see The "config.xml.cfm" File section.

      Administration Area Layout Template

      If you would like your plugin's administration area to have the same look and feel of Mura's back-end administration area, you can do that fairly easily using the pluginManager bean's helper method labeled "renderAdminTemplate".

      The example below illustrates how to use renderAdminTemplate in your "index.cfm" file. See The "config.cfm" File section for information about the first line of code.

      <cfinclude template="plugin/config.cfm" />
      
      <cfsavecontent variable="pluginBody">
          <cfoutput>
              <h1>#esapiEncode('html', pluginConfig.getName())#</h1>
              <p>Hello from YourPlugin!</p>
          </cfoutput>
      </cfsavecontent>
      
      <cfoutput>
          #m.getBean('pluginManager').renderAdminTemplate(pluginBody)#
      </cfoutput>

      Using the above code, if an administrator views your plugin, they would see something similar to the following image.

      However, using the renderAdminTemplate method does not "protect" your plugin from being accessed by people who are not authorized to do so. In other words, your plugin is still publicly accessible. In fact, if you're attempting to follow along, and wish to test it out, log out of the administration area, then navigate to your plugin's URL. For example, http://www.yourdmain.com/plugins/yourplugin/. You should see something similar to the following image.

      Restricting Access to Your Plugin

      If you wish to restrict access to your plugin's entry page, you'll want to include a custom Application.cfc file, and then include your desired logic. A sample Application.cfc with example code may be found at https://github.com/stevewithington/MuraPlugin/blob/develop/Application.cfc. In the example file, you'll note how some of Mura's application methods have been included into the standard CFML lifecycle events, as briefly mentioned in The "appcfc" Directory section.

      In the end, as a Mura plugin developer, it's your responsibility to come up with a solution to restrict access to the plugin.

      The "license.txt" File

      If this file exists in the root of the plugin, or under {context}/plugins/{YourPlugin}/plugin/license.txt, the content of the text file will be used as an End-User License Agreement (EULA) or software license agreement. When an administrator deploys the plugin, they will be presented with the EULA, and have to "accept" the terms before continuing to install and deploy the plugin.

      Installing Plugins

      Mura plugins are distributed as ".zip" files. Plugins are managed in the back-end administrator area of Mura. Administrators may install plugins via the back-end user interface (UI). There are a few different methods for installing/deploying plugins. The method you select will often depend on whether you are a developer or not.

      That said, here's some important tips before you install any plugins:

      • Never install a plugin in a production environment without testing it elsewhere first.
      • Plugins have access to all areas of Mura, and as such, be wary of plugins from sources you are unfamiliar with.
      • Review the code of any plugin you intend to install first, if you are not the author of it.
      • You may want to make a backup of your filesystem and database before deploying a plugin, in case something goes wrong during the deployment process.
      • Finally, be sure the plugin you are deploying is compatible with your version of Mura.

       

      Via Back-End UI

      To install a plugin via the back-end UI, follow the steps below.

      1. From the back-end administration area, select Global Settings, then click Plugins.
      2. You should see the Global Settings screen, with the Plugins tab enabled.
      3. Under "Install Plugin", you have two (2) options for deploying a plugin:
        • Via Upload
          • Select this option if you have the .zip file on your local machine.
          • Select the "Choose File" or "Browse" button, and locate your desired plugin file.
          • When ready to upload and deploy the plugin, select the "Deploy" button, then continue to the next step.
        • Via URL
          • Select this option if you have a URL to the .zip file. For example, if your desired plugin is hosted on Github, you may select to copy the link to the .zip file, and use that as the URL.
          • Click the Via URL button.
          • Then, enter the full link to the plugin. For example, https://github.com/stevewithington/MuraPlugin/archive/develop.zip
          • When ready to deploy the plugin, select the "Deploy" button, then continue to the next step.
      4. After you select "Deploy", if your plugin includes an End User License Agreement (EULA), the license will be displayed. In order to use the plugin, you will need to select "I Accept" in order to see the rest of the plugin installation and configuration settings.
      5. You may need to scroll down to see the rest of the installation/configuration form. The main form fields are outlined below. However, plugins may include a number of additional configuration settings, and vary by plugin.
        • Plugin Name (Alias)
          • The default value entered by the developer will be used here. However, administrators also have the ability to rename the plugin to something more meaningful, if desired.
        • Load Priority
          • This setting is used only during Mura's "onApplicationLoad" event. This is useful for plugin dependency issues that may arise. For example, Plugin2 might be dependent upon Plugin1 functionality, and so Plugin1 should have a higher Load Priority than Plugin2.
        • Site Assignment
          • Select which site(s) you wish to enable the plugin for.
        • Event Handlers
          • In most cases, only one Event Handler is listed, "onApplicationLoad". However, all of the plugin's registered event handler methods will be listed here.
      6. When finished completing the plugin's configuration settings information, simply click "Update" to save the changes.
      7. The plugin should now be listed under "Current Plugins".

      Via Auto-Discovery

      Most Mura plugin developers select deploying their plugins via auto-discovery, especially if the plugin is being created by the developer conducting the installation. To deploy your plugin via auto-discovery, follow the steps outlined below.

      1. First, you need to make sure auto-discovery of plugins is enabled for your Mura installation. Open the file located under {context}/config/settings.ini.cfm. Locate the setting for "autodiscoverplugins" and make sure it is set to "true". If not, set it to "true", then reload Mura.
      2. The next step in the auto-discovery process is to either create a simple plugin, as described in the Plugin Anatomy section, or download a starter plugin from Github, such as MuraPlugin, or MuraFW1.
      3. Then, instead of using a ".zip" file, you would simply place your plugin under the plugins directory. For example: {context}/plugins/yourPlugin/
      4. Next, reload Mura, and it should now discover your new plugin. Mura then automatically deploys the plugin.
      5. To continue the installation process, select Global Settings, then click Plugins.
      6. You should now see your new plugin under the "Current Plugins" listing.
      7. Select the three-dot menu to the left of the plugin's name.
      8. Then, click "Edit" from the options menu.
      9. You will then be taken to the Plugin Settings screen.
      10. Select your desired options, as described in the Installing Plugins Via Back-End UI section.
      11. When finished, click Update to save your new settings.

      Setting Permissions For Plugins

      Plugins may be restricted to specific groups, allowing administrators the ability to control which groups may access each plugin. Permissions may be set only for plugins enabled for the specific site you are currently working on. To set permissions, follow the steps outlined below.

      1. From the back-end administration area, select Plugins, and click Site Plugins
      2. You will then be directed to the Site Plugins screen
      3. At a minimum, there are two (2) tabs: Application and Utility. However, there may be additional tabs, depending on the plugins enabled for your site. Locate the plugin you wish to manage permissions on, then click the three-dot menu to the left of the plugin name.From the menu of options, click Permissions
      4. The Permissions screen should appear.
      5. Select the group(s) you wish to enable access for
      6. When finished, click Update to save the new permissions

      Deleting Plugins

      To delete a Mura CMS plugin, follow the steps outlined below.

      1. From the back-end administration area, select Global Settings, then click Plugins
      2. Select the three-dot menu to the left of the plugin's name.
      3. Then, click "Delete" from the options menu
      4. An Alert dialog window should appear
      5. Select "OK" to continue the delete process, or select "Cancel" to abort and close the dialog window.

      Advanced Options

      When it comes to plugins, the possibilities are truly endless. Throughout the documentation, we've shown you a plethora of ways to interact with Mura. In other words, we've given you all of the tools you'll need to do pretty much anything you want. Just in case you've forgotten, here's a quick reminder of some of the most powerful tools Mura has to offer:

      • Mura Scope
        • When you need to communicate with Mura, use the Mura Scope.
      • Mura Events
        • When you need to replace, or add to Mura's default functionality, use Mura's Events. You may also create your own, custom events too.
      • Mura Beans & Objects
        • When you need to work with Mura beans or objects, or create your own custom objects using class extensions and/or Mura ORM, don't hesitate to do so directly from your plugin.
      • Mura Rendering
        • When you need to alter Mura's rendering, remember you have access to tools such as Mura's modules, custom content types, Mura.js, and localization features.

      In addition to all of Mura's baked-in developer tools, feel free to use any CFML framework you wish. For example, are you a fan of Framework One? Great! We have a starter plugin using FW/1 as its application framework called MuraFW1. Using a different one? Go ahead. Remember, your plugin can be completely isolated from Mura, if you want it to be. You, as the plugin developer, choose which pieces of Mura you wish to include in your application.

      Plugins vs. Modules

      As you've seen in the Mura Modules section, modules are a powerful tool, allowing Mura developers to integrate not only custom displays and applications, but also a new world of possibilities for injecting custom processing logic and more. 

      The decision to use a Plugin instead of a Module comes down one question:

      • Do you need a custom administration area?
        • Yes
          • Use a plugin. Each plugin can be restricted to specific sites, and the plugin's administrator area can be restricted to specific user groups.
        • No
          • Use a module.

      Mura Rendering

      While there have been some bits and pieces of rendering in other areas of the guide, throughout this section, you'll build upon the knowledge you've picked up so far, and learn how to override Mura's default rendering methods, as well as how to create some sophisticated display objects, and how to target specific content types to provide custom body rendering. In addition, you'll learn about Mura.js, and how to use it in your development efforts, as well as other important information such as internationalization/localization, and how to leverage caching to improve your site's performance.

       

      The Mura contentRenderer.cfc

      The majority of Mura's rendering behavior occurs in a special file labeled the "contentRenderer.cfc". Mura's primary, or "Core" contentRenderer is located under:

       {context}/core/mura/content/contentRenderer.cfc

      As covered in the Theme Developer's Guide, the "Core" contentRenderer also contains a plethora of helper methods, as well as settings used by Mura's display objects, all of which are frequently used by Mura developers.

      Since the "Core" contentRenderer is a "core" file, it shouldn't be modified directly. Instead, if you wish you to override any of the default settings or methods found in the "core" contentRenderer, Mura will first check the theme, and then the site for a contentRenderer.cfc. If the matching setting or method is found, it will use the custom setting or method in lieu of Mura's default "core" setting or method. You only need to define the setting(s) or method(s) you wish to override in your "Site" or "Theme" contentRenderer.

      Custom Rendering Methods

      In addition to overriding any of the default settings and methods found in the "Core" contentRenderer, any custom rendering methods defined in the "Site" or "Theme" contentRenderer are available via the Mura Scope. For example, if you have the following method defined in your "Site" or "Theme" contentRenderer:

      public string function dspHello() {
        return "<p>Hello from dspHello!</p>";
      }
      

      You may invoke the method by simply using the following syntax:

      <cfoutput>
        #m.dspHello()#
      </cfoutput>
      

      The above code would output:

      <p>Hello from dspHello!</p>

      Lookup Hierarchy

      Mura uses the following lookup hierarchy for settings or methods found in a contentRenderer.cfc. If the setting or method is defined in the first file, Mura will use it and ignore any other identically named methods found in the remaining files.

      1. "Theme" contentRenderer
        • ../themes/{ThemeName}/contentRenderer.cfc
      2. "Site" contentRenderer
        • {context}/sites/{SiteID}/includes/contentRenderer.cfc
      3. "Core" contentRenderer
        • {context}/core/mura/content/contentRenderer.cfc

      contentRenderer Settings

      The vast majority of the contentRenderer settings allow developers to apply custom classes to Mura's modules. This allows developers to use CSS to control the style and display of Mura's modules, without having to override the rendering methods or modules themselves to achieve the desired result.

      Mura allows you to override its default contentRenderer settings on a site-by-site basis. To override a setting, simply place a reference to the setting and your desired value in your "Site" or "Theme" contentRenderer.cfc.

      Example

      this.validateCSRFTokens = true;
      this.navOffset = 0;
      this.navDepthLimit = 1000;
      this.navWrapperClass = 'sidebar-nav well';
      

      Reference

      A reference for Mura's "Core" contentRenderer.cfc settings is outlined below. Keep in mind the vast majority of the contentRenderer settings are specific to Mura's modules, and as such, most of these settings are merely the CSS classes that will be applied to them.

      Note: The settings listed below are the "default" settings, and many of them may be overwritten in Mura's default theme. Please refer to the theme's contentRenderer.cfc file to review the theme's settings.

      Setting Type Default Description
      validateCSRFTokens boolean false If true, Mura will check for cross-site request forgery (CSRF) tokens to help prevent CSRF attacks.
      navOffset int 0 This allows you to start standard navigation behavior at lower navigation levels. For example, this would be useful if you have a section of your site that should have its own primary navigation, such as a Member's only area with a custom layout template.
      navDepthLimit int 1000 This sets the maximum depth that standard navigation will follow.
      navParentIdx int 2  
      navGrandParentIdx int 3  
      navDepthAdjust int 0  
      navSelfIdx int 1  
      deferMuraJS boolean false If true, Mura will add a "defer" attribute to the script reference for mura.min.js so that the script will be executed when the page has finished parsing.
      jsLib string jquery

      This determines what JavaScript library Mura should use with its built-in display objects. Valid options are:

      • jquery
      • prototype
      jsLibLoaded boolean false This allows developers to not rely on Mura to load the default JavaScript framework and simply add it to their theme's HTML head area.
      suppressWhitespace boolean true If true, Mura will remove excess whitespace characters from CFML generated content.
      longDateFormat string long This is the default long date format for the site.
      shortDateFormat string short This is the default short date format for the site.
      showMetaList list jpg,jpeg,png,gif,svg This is a list of file extensions that will not directly download, but instead, will render in a Mura CMS page with the "Summary" being displayed in the body area.
      imageInList list jpg,jpeg,png,gif,svg This is a list of what image extensions should be shown in built in content listing display objects.
      directImages boolean true This tells Mura whether to serve images indirectly through fileManager.renderImage() or create direct links.
      personalization string user This allow developers to choose whether site personalizations such as ratings and favorites are attached to simple cookies or an actual Mura user. Users do not need to login in order to save cookie-based personalizations. Valid options:
      • user
      • cookie
      hasEditableObjects boolean false If true, enables editable objects.
      asyncObjects boolean true If true, Mura will load display objects asynchronously.
      asyncRender boolean false  
      defaultInheritedRegions string list, empty by default An indexed list of the display regions to be inherited by default. Regions are identified in Site Settings, i.e. Left Column^Body^Footer, and if the desired default regions to be inherited was Body and Footer, then the value for this setting would be "2,3".
      queueObjects boolean true  
      layoutManager boolean false If true, enables the front-end Mura Layout Manager. 
      legacyObjects boolean true If true, allows for management of legacy display objects used in pror Mura versions
      siteIDInURLs boolean   If a boolean value exists, it will override the settings.ini.cfm value
      indexFileInURLs boolean   If a boolean value exists, it will override the settings.ini.cfm value
      hashURLs boolean   If a boolean value exists, it will override the settings.ini.cfm value
      enableMuraTag boolean   If a boolean value exists, it will override the settings.ini.cfm value
      showAdminToolbar boolean   If true, the front end toolbar will be rendered for administrative users.
      showMemberToolbar boolean   If true, the front end toolbar will be rendered for site members
      showEditableObjects boolean   If true, editable display objects like components and content collections will be rendered. 
      showInlineEditor boolean   If true, will display the Inline Edit button on the front end toolbar for administrative users. 
      renderHTMLQueues boolean true If true, Mura will render the request's HTMLHeadQueue and HTMLFootQueue.
      preloaderMarkup string empty string Allows developers to include a custom preloader.
      bodyMetaImageSizeArgs struct {size="medium"}  
      bodyMetaImageClass string thumbnail A CSS class applied to the primary associated image.
      navsize int 50 An integer value to control the maximum number of navigational items to output for Mura generated navigation.
      imageClass string img-thumbnail A CSS class applied to the primary associated image when displayed in a list such as a collection.
      categoriesNestCheckboxClass string checkbox A CSS class applied to category checkboxes.
      generalWrapperClass string well A CSS class applied to general wrapper HTML elements.

      Heading Markup Settings

      When Mura outputs headings, the following settings may be used. The following settings are also used to control the heading tags used for the HTML Editor's "Format" select menu options. The defaults listed below are based on the concept that <h1> tags are reserved for the "Site Name" or logo area of the layout templates, and page titles begin with <h2> tags.

      Setting Type Default
      headline string h2
      subHead1 string h3
      subHead2 string h4
      subHead3 string h5
      subHead4 string h6
      subHead5 string h6

      Alert Markup Settings

      When Mura outputs an alert, the following settings may be used.

      Setting Type Default
      alertSuccessClass string alert alert-success
      alertInfoClass string alert alert-info
      alertWarningClass string alert
      alertDangerClass string alert alert-error

      Table Markup Settings

      When Mura outputs a table, the following settings may be used.

      Setting Type Default
      tableClass string table table-bordered table-striped
      tableHeadClass string empty string
      tableHeaderClass string empty string
      tableBodyClass string empty string
      tableRowClass string empty string
      tableCellClass string empty string
      tableFooterClass string empty string

      Module (Display Object) Settings

      The vast majority of contentRenderer settings are used to control the CSS classes for HTML markup of Mura's baked-in modules. Mura's modules and their associated settings are described below.

      Navigation

      The markup for the various types of navigational modules can be found in the files located under:

      {context}/core/modules/v1/nav/
      
      Setting Type Default
      navWrapperClass string sidebar-nav well
      navLIClass string empty string
      liHasKidsClass string empty string
      liHasKidsAttributes string empty string
      liCurrentClass string current
      liCurrentAttributes string empty string
      liHasKidsNestedClass string empty string
      aHasKidsClass string empty string
      aHasKidsAttributes string empty string
      aCurrentClass string current
      aCurrentAttributes string empty string
      ulNestedClass string empty string
      ulNestedAttributes string empty string
      ulTopClass string navSecondary
      ulPaginationClass string navSequential
      ulPaginationWrapperClass string pagination
      aNotCurrentClass string empty string
      navCalendarWrapperClass string svCalendar
      navCalendarTableClass string table table-bordered
      navSequentialWrapperClass string pagination
      navSequentialULClass string empty string
      tagCloudWrapperClass string svTagCloud
      navArchiveWrapperClass string empty string
      navArchiveListClass string empty string
      navBreadcrumbULClass string breadcrumb

      Form

      The Form module markup can be found in the files located under:

      {context}/core/modules/v1/form/
      Setting Type Default
      formWrapperClass string well
      formFieldWrapperClass string control-group
      formFieldLabelClass string control-label
      formInputWrapperClass string input-addon
      formInputClass string form-control
      formCheckboxClass string empty string
      formButtonWrapperClass string btn-group
      formButtonInnerClass string empty string
      formButtonClass string btn btn-default
      formRequiredWrapperClass string empty string

      Form Builder

      The Form Builder module markup can be found in the files located under:

      {context}/core/modules/v1/formbuilder/
      
      Setting Type Default
      formBuilderFieldWrapperClass string empty string
      formBuilderButtonWrapperClass string form-actions
      formBuilderSubmitClass string btn btn-default
      formBuilderFormFieldsClass string control-group
      formBuilderTabHeaderClass string dropdown
      formBuilderDisabledInputClass string disabled
      formBuilderCheckboxClass string checkbox

      Calendar

      The Calendar module markup can be found in the files located under:

      {context}/core/modules/v1/calendar/
      Setting Type Default
      calendarWrapperClass string svCalendar
      calendarTableClass string table table-bordered
      calendarTableHeaderClass string empty string
      calendarTitleInDesc boolean true
      calendarListWrapperClass string svCalendar
      calendarcolors array of structs [
      {background='##3a87ad', text='white'},
      {background='blue', text='white'}
      ]

      Comments

      The Comments module markup can be found in the files located under:

      {context}/core/modules/v1/comments/
      Setting Type Default
      commentsWrapperClass string empty string
      commentSortContainerClass string empty string
      commentSortWrapperClass string empty string
      commentSortSelectClass string empty string
      commentFormWrapperClass string empty string
      commentFormClass string well
      commentNewClass string btn
      commentFieldWrapperClass string empty string
      commentFieldLabelClass string empty string
      commentInputWrapperClass string empty string
      commentInputClass string empty string
      commentCheckboxClass string checkbox
      commentPrefsInputWrapperClass string empty string
      commentSubmitButtonWrapperClass string empty string
      commentSubmitButtonClass string btn
      commentMoreCommentsUpClass string btn btn-default icon-arrow-up
      commentMoreCommentsDownClass string btn btn-default icon-arrow-down
      commentRequiredWrapperClass string empty string
      commentAdminButtonWrapperClass string empty string
      commentUserEmailClass string btn
      commentDeleteButtonClass string btn
      commentEditButtonClass string btn
      commentApproveButtonClass string btn
      commentThumbClass string img-polaroid
      commentSpamClass string btn
      commentSpamLinkClass string btn
      commentClass string empty string
      commentDateTimeClass string empty string
      commentReplyClass string empty string
      commentAwaitingApproval string empty string
      commentAdminButtonWrapperClass string btn-group pull-right
      commentUserEmailClass string btn btn-default btn-sm
      commentDeleteButtonClass string btn btn-default btn-sm
      commentEditButtonClass string btn btn-default btn-sm
      commentApproveButtonClass string btn btn-default btn-sm
      commentMoreCommentsContainer string well
      emailLinkClass string btn
      commentsLinkClass string btn
      approveCommentLinkClass string btn
      deleteCommentLinkClass string btn

      Collections, Folders, and List/Grid Output

      Collections & List/Grid module markup can be found in the files located under:

      {context}/core/modules/v1/collection/
      Setting Type Default
      contentListImageStyles boolean true
      contentListImagePadding int 20
      contentListPropertyMap struct {
      containerEl={tag="div"},
      itemEl={tag="dl",class="clearfix"},
      labelEl={tag="span"},
      title={tag="dt"},
      date={tag="dt"},
      credits={tag="dd",showLabel=true,rbkey="list.by"},
      tags={tag="dd",showLabel=true,labelDelim=":",rbkey="tagcloud.tags"},
      rating={tag="dd",showLabel=true,labelDelim=":",rbkey="list.rating"},
      default={tag="dd"}
      }
      contentGridStyleMap struct {
      '1 Column'='mura-grid-one',
      '2 Column'='mura-grid-two',
      '3 Column'='mura-grid-three',
      '4 Column'='mura-grid-four',
      '5 Column'='mura-grid-five',
      '6 Column'='mura-grid-six',
      '7 Column'='mura-grid-seven',
      '8 Column'='mura-grid-eight',
      '9 Column'='mura-grid-nine'
      }
      contentGridPropertyMap struct {
      itemEl={tag="div",class="mura-item-meta"},
      labelEl={tag="span"},
      title={tag="div"},
      date={tag="div"},
      credits={tag="div",showLabel=true,labelDelim=":",rbkey="list.by"},
      tags={tag="div",showLabel=true,labelDelim=":",rbkey="tagcloud.tags"},
      rating={tag="div",showLabel=true,labelDelim=":",rbkey="list.rating"},
      'default'={tag="div"}
      }
      contentListWrapperDivClass string empty string
      contentListItemImageLinkClass string thumbnail
      folderWrapperClass string svIndex
      nextNWrapperClass string empty string

      Edit Profile

      The Edit Profile module markup can be found in the files located under:

      {context}/core/modules/v1/editprofile/
      Setting Type Default
      editProfileWrapperClass string empty string
      editProfileFormClass string form-horizontal
      editProfileFormGroupWrapperClass string control-group
      editProfileFieldLabelClass string control-label
      editProfileFormFieldsWrapperClass string empty string
      editProfileFormFieldsClass string empty string
      editProfileHelpBlockClass string help-block
      editProfileExtAttributeFileWrapperClass string empty string
      editProfileExtAttributeFileCheckboxClass string checkbox
      editProfileExtAttributeDownloadClass string empty string
      editProfileExtAttributeDownloadButtonClass string btn btn-default
      editProfileSubmitButtonWrapperClass string empty string
      editProfileSubmitButtonClass string btn btn-primary
      editProfileSuccessMessageClass string alert alert-success

      Event Reminder Form

      The Event Reminder Form module markup can be found in the files located under:

      {context}/core/modules/v1/event_reminder_form/
      Setting Type Default
      eventReminderFormWrapperClass string empty string
      eventReminderFormClass string well
      eventReminderFieldWrapperClass string control-group
      eventReminderFormLabelsClass string control-label
      eventReminderSubmitClass string btn btn-default

      Feed

      The Feed module markup can be found in the files located under:

      {context}/core/modules/v1/feed/

      You should also review the Collections, Folders, and List/Grid Output settings above for markup information specific to the feed output itself. These settings are merely "wrapper" classes.

      Setting Type Default
      localIndexWrapperClass string svSyndLocal svFeed svIndex clearfix
      remoteFeedWrapperClass string svSyndRemote svIndex svFeed clearfix

      Login Form

      The Login Form module markup can be found in the files located under:

      {context}/core/modules/v1/login/
      Setting Type Default
      loginWrapperClass string container
      loginWrapperInnerClass string row
      loginFormClass string form-horizontal
      forgotPasswordFormClass string form-horizontal
      loginFormGroupWrapperClass string empty string
      loginFormFieldLabelClass string control-label
      loginFormFieldWrapperClass string empty string
      loginFormFieldClass string empty string
      loginFormPrefsClass string empty string
      loginFormCheckboxClass string checkbox
      loginFormSubmitWrapperClass string empty string
      loginFormSubmitClass string btn btn-default
      notRegisteredLinkClass string btn btn-primary

      Search Form & Search Results

      The Search Form module markup can be found in the files located under:

      {context}/core/modules/v1/search/
      
      Setting Type Default
      searchFormClass string empty string
      searchFormInputWrapperClass string empty string
      searchFormInputClass string empty string
      searchFormSubmitWrapperClass string empty string
      searchFormSubmitClass string btn btn-default
      searchShowNumbers int 1
      searchResultWrapperClass string container
      searchResultInnerClass string row
      searchResultsRowClass string row
      searchResultsMoreResultsRowClass string row
      searchResultsListClass string svIndex
      searchResultsPagerClass string pager
      searchAgainRowClass string row
      searchAgainInnerClass string empty string
      searchAgainFormClass string empty string
      searchAgainInputWrapperClass string empty string
      searchAgainFormInputClass string empty string
      searchAgainButtonWrapperClass string empty string
      searchAgainSubmitClass string btn btn-default

      User Tools

      The User Tools module markup can be found in the files located under:

      {context}/core/modules/v1/user_tools/
      
      Setting Type Default
      userToolsLoginWrapperClass string well clearfix
      userToolsLoginFormClass string form-horizontal
      userToolsFormGroupWrapperClass string empty string
      userToolsLoginFormLabelClass string control-label
      userToolsLoginFormInputWrapperClass string empty string
      userToolsLoginFormInputClass string empty string
      userToolsLoginFormFieldInnerClass string empty string
      userToolsLoginFormCheckboxClass string checkbox
      userToolsLoginFormSubmitClass string btn btn-default
      userToolsNotRegisteredLinkClass string btn btn-primary
      userToolsWrapperClass string clearfix
      userToolsEditProfileLinkClass string btn btn-default
      userToolsLogoutLinkClass string btn btn-default

      Content Rater

      The Content Rater module markup can be found in the files located under:

      {context}/core/modules/v1/rater/
      
      Setting Type Default
      raterObjectWrapperClass string row clearfix
      raterWrapperClass string empty string
      avgRatingWrapperClass string empty string

       

      The Mura [m] Tag

      As covered in the Theme Developer's course, you may use "Mura Tags" when you want to output dynamic code within the HTML Editor.

      Developers need to use Mura Tags because hash tags (#) are ignored when entered as text in the HTML Editor. Hence, you can only render a CFML variable, or function call.

      Globally Enable/Disable the Mura Tag

      To globally enable or disable the Mura Tag, open the file located under {context}/config/settings.ini.cfm. Locate the enablemuratag key, and set the value to true to enable the Mura Tag, or false to disable the Mura Tag.

      If enablemuratag is set to false, the Mura tag button will not appear on any HTML Editors. In addition, the Mura Tags will not be parsed as CFML, and will only display as a string of text, as it was entered into the HTML Editor.

      Enable/Disable the Mura Tag on a Site-by-Site Basis

      If you wish to enable or disable the Mura Tag on a site-by-site basis, you may edit the Site or Theme contentRenderer.cfc file located under:

      • Site contentRenderer.cfc
        • {context}/sites/{SiteID}/contentRenderer.cfc
      • Theme contentRenderer.cfc
        • ../themes/{ThemeName}/contentRenderer.cfc

      Then, add this.enablemuratag=true to enable, or this.enablemuratag=false to disable. This setting overrides the global setting described above.

      Rendering Mura Tags

      When you expect Mura Tags in the output, you'll want to use the m.SetDynamicContent(string) function. This function is covered under the setDynamicContent section of the Theme Developer's Guide.

      Class Extensions

      Mura's Class Extensions allow you to customize some of its common bean objects such as content beans, user beans, group beans, and site beans. The term "class extension" itself is borrowed from the world of object-oriented programming (OOP). These "objects" are designed in hierarchies, commonly referred to as a "class" hierarchy. Hence, "class extensions" is a term Mura uses to define custom subclasses.

      Class extensions allow you to create Extended Attribute Sets (think fieldsets, or groups of related form elements) to provide additional "attributes" (think form fields) to collect and store data. In addition, you can create Related Content Sets to allow content managers the ability to associate content to something other than the default catch-all "Related Content" field, allowing you to create fields such as Related Videos, Related Files, Related Authors, and so on.

      Also, class extensions provide an excellent way to allow developers the ability to target a certain Type and/or Subtype of a content item's body area, so they can do things such as automatically alter the layout of the body area, and/or include additional data, etc. Using this method means you wouldn't necessarily have to create any Extended Attribute Sets or Related Content Sets either, unless that's something you desire.

      Types & Subtypes

      Each of the bean objects Mura exposes for customization (see table below) include two very important attributes: Type, and Subtype. While you can change the Subtype attribute of a bean object, you cannot alter the Type attribute itself.

      When working with class extensions, you first target the specific Type for customization. Then, you target the "default" Subtype, or create a new Subtype altogether. These are often represented as {Type}/{Subtype} when creating new objects. For example, "Folder/Locations", "Page/Location", "User/Employee", etc.

      While the options for customization are essentially limitless, here are a few examples to help illustrate how you might use class extensions.

      • User
        • You could create a new "User" subtype, such as "Employee". Then, you might want to collect additional data about the employee such as "Emergency Contact Name", "Emergency Contact Mobile", etc.
      • Folder
        • You could create a new "Folder" subtype, such as "Locations". Then, you could target any content items using "Folder/Locations" as the Type and Subtype in order to loop over any child content items and display them on a map. You wouldn't necessarily have to add any Extended Attribute Sets or Related Content Sets either. As you'll see in the Define with the Admin UI section, you can also restrict the {Type}/{Subtype} of content that can be added as children.
      • Page
        • Along the same lines of the previous Folder example, you could create a new "Page" subtype, such as "Location". Then, you could collect data about the location such as the Street, City, State, Phone Number, etc. and display the information entered into these fields on the page when it's being rendered. In addition, you could use this data to geocode the location for use on map.

      The "default" Subtype

      By targeting "default" as the Subtype, any Extended Attribute Sets or Related Content Sets associated with the class extension will apply to all objects sharing the same Type attribute.

      For example, if you create a new class extension and the Type equals "Page" and Subtype equals "Default", then anything you create will apply to all "Page/Default" content items, as well as any other "Page/{Anything}" content items.

      Extendable Bean Objects

      The table below itemizes each of the "Base Types" of bean objects available for customization.

      Type Bean Description
      Page content This is the standard Page content type.
      Folder content This is the standard Folder content type.
      File content This is the standard File content type.
      Calendar content This is the standard Calendar content type.
      Link content This is the standard Link content type.
      Component content This is a standard Component, which is essentially a special type of content itself. Using Class Extensions with Components opens several possibilities such as creating a Component with tabbed content, slideshows, and more.
      Form content This is a standard Form, also a special type of content.
      Base content Extending the Base/Default object allows you to create additional Extended Attribute Sets and Related Content Sets to appear on all standard content types (Page, Folder, File, Calendar, and Link), excluding Components and Forms. If you specify a different Subtype, then any Extended Attribute Sets or Related Content Sets will only appear on content types that share the specified Subtype.
      User (2) user Since the group bean is essentially the user bean, the type attribute for users is technically stored as a "2" to indicate the record reflects a "user".
      Group (1) group Since the group bean is essentially the user bean, the type attribute for groups is technically stored as a "1" to indicate the record reflects a "group".
      Address address Address bean objects are related to user beans. A user may have one or more addresses. By extending this class, you can create additional attributes to store new types of data that Mura doesn't already collect.
      Site site Extending the Site object allows you to create additional Extended Attribute Sets via the Site Settings area of the back-end administration area of Mura.

       

      Define with Admin UI

      Follow the steps below to create a custom class extension. For example purposes, we'll create a Page/Location, as mentioned in the introduction to Class Extensions.

      1. From Mura's back-end administration area, select Site Settings, click Class Extensions, and select Add Class Extension.
      2. You should be taken to the Add Class Extension screen.
        • Base Type
          • Please refer to the Extendable Bean Objects section of the Class Extensions page for details on each option.
        • Description
          • You may enter your desired description of the class extension here. This is merely information for anyone else who may edit or manage the class extension in the future, and is not displayed anywhere else by default.
        • Status
          • Active
            • If selected, the defined Class Extension will appear as an option for content managers when creating new content items.
          • Inactive
            • If selected, the defined Class Extension will not appear as an option for content managers when creating new content items.
      3. From the Add Class Extension screen, click the Base Type menu, and choose your desired option, (e.g., Page).
      4. Once you've made a selection, additional form fields may appear. If you select one of the content types, (e.g., Page, Folder, etc.), additional form fields will appear.
        • Sub Type
          • You may choose to leave the initial setting of "Default" so that any Extended Attribute Sets and/or Related Content Sets will apply to all content items sharing the same Type. Or, you may enter your own, unique Subtype.
        • Icon
          • The icon you select here, will appear in the Select Content Type dialog window, when content managers create new content items.
        • Show "Summary" field when editing?
          • Select whether you wish to display the "Summary" field.
        • Show "Body" field when editing?
          • Select whether you wish to display the "Body" field.
        • Show "Associated Image" field when editing?
          • Select whether you wish to display the "Associated Image" field.
        • Allow user to add only specific Subtypes?
          • If Yes, you may select one, or more options from the available list. This will restrict what appears in the Select Content Type dialog window, when content managers add new content items as children on this specific type of content.
      5. When finished, click the Add button, so save your new Class Extension.
      6. You should now be taken to the Class Extension screen, where you may view/create Extended Attribute Sets, and Related Content Sets.

      Extended Attribute Sets

      Extended Attribute Sets are essentially <fieldset> elements. In other words, they're a set of form controls, grouped under a common name. To create an Extended Attribute Set, follow the steps outlined below.

      1. From the back-end administration area of Mura, select Site Settings, click Class Extensions, and select Class Extensions.
      2. You should be directed to the Class Extensions screen.
      3. Select the Class Extension you wish to add an Extended Attribute Set to, from the listing of available Class Extensions.
      4. You should be directed to the Class Extension screen. Here, you can view any available Extended Attribute Sets or Related Content Sets.
      5. To add an Extended Attribute Set, click the Add button, then select Add Attribute Set.
      6. You should now see the Add Attribute Set screen.
        • Attribute Set Name
          • The text entered here will be used as the <legend> for the generated <fieldset>.
        • Container (Tab)
          • You may select the tab you wish to include your Extended Attribute Set on, from the listing of available tabs. The listing will differ, depending on the Base Type you selected when creating the Class Extension itself.
          • If you select the Custom UI option, you're essentially telling Mura that you'll take care of collecting any attributes you're going to define. If you wish to add a new tab, and include a custom form for collecting the data, you may find this Gist useful: https://gist.github.com/stevewithington/f3dd405d5a188fb594a1
      7. Once you've entered an Attribute Set Name, and selected your desired Container (Tab), click the Add button to create your new attribute set.
      8. You should be directed to the Manage Extended Attribute Set screen. This is where you'll add, and manage your new attributes, or form fields.
      9. To create a new attribute, click the Add New Attribute button.
      10. The screen should now reveal a form to add a new attribute.
        • Name (No spaces)
          • Text entered here will be used as the name attribute of the form field. In addition, the text entered here is how you will reference any data entered for this attribute, as you'll see in the Displaying Extended Attributes section. It is important to use a proper naming convention, as if you were manually creating the input using HTML. Keep in mind that this form field will be included on a much larger form, and as such, you'll want to avoid using any of the bean object's attribute names so you don't run into any naming collisions. For example, you don't want to use the Name "title", since Content Beans already have an attribute labeled "title".
          • The recommended best practice is to prefix the Name with the Subtype of your class extension. For example, if your Subtype equals "Location", instead of using "title" for the Name, you might use "locationtitle" for the Name.
        • Label
          • Text entered here will be used as the <label> element.
        • Input Type
          • Select your desired input type from the option list.
        • Default Value
          • Optionally, enter a default value here. To include a dynamic value, you may use Mura [m] tags.
        • Tooltip
          • Optionally, enter text to be used as a tooltip.
        • Required
          • Whether the field is required. If True, Mura will use the option selected in the Validate field when the user submits the form.
        • Validate
          • If you desire the input to be validated before the form is submitted, select one of the following options.
            • None
              • Do not validate input.
            • Date
              • Validates data entered is a valid date. Mura will display a calendar UI widget for content managers to use when completing this form field.
            • DateTime
              • Validates data entered is a valid date and time. Mura will display a calendar and time UI widget for content managers to use when completing this form field.
            • Numeric
              • Validates data entered is an integer.
            • Email
              • Validates data entered matches standard email address formats.
            • Regex
              • Validates data entered against the JavaScript regular expression entered in the Regex field.
            • Color
              • If the Input Type is set to TextBox, and this option is selected, Mura will display a "color picker" UI widget for content managers to use when completing this form field.
            • URL
              • Validates data entered matches standard URL formats.
        • Regex
          • If Validate is set to Regex, Mura will use the JavaScript regular expression entered here to validate data entered in this form field.
        • Validation Message
          • If Required is set to True, text entered here will be displayed to content managers if the data entered in this form field does not pass validation rules.
        • Option List ("^" Delimiter)
          • This carat ("^") delimited list of "option values" will be used if Input Type is set to SelectBox, MultiSelectBox, or RadioGroup. For example, "option1^option2^option3". To include a dynamic value, you may use Mura [m] tags.
        • Option Label List (Optional, "^" Delimiter)
          • This carat ("^") delimited list of "option labels" will be used if Input Type is set to SelectBox, MultiSelectBox, or RadioGroup. For example, "Option 1^Option 2^Option 3". To include a dynamic value, you may use Mura [m] tags.
        • For Administrative Use Only?
          • If Yes, the field will not display for content editing purposes, and will be a hidden form field.
      11. After you've completed the form, click the Add button to create your new attribute.
      12. Repeat these steps to create additional attributes for the Extended Attribute Set.
      13. The attributes sort order is the order in which they will be displayed to content managers. If you wish to reorder any of them, click the Reorder button.
      14. Then, hover over the attribute you wish to move, and then click, drag, and drop to your desired location.
      15. When finished reordering, click the Save Order button.

      Define with XML

      In the Define with Admin UI section, we covered creating Class Extensions, Extended Attribute Sets, and Related Content Sets using Mura's back-end administrator user interface. In addition to using the administrator's UI, Mura can parse a special XML file to create Class Extensions, as well as create custom image sizes, and more.

      config.xml.cfm

      Whenever Mura experiences an application reload, it scans for the config.xml.cfm file, and if found, will attempt to parse the file to create any pre-defined Class Extensions and custom image sizes. As you'll see in the Mura Modules section and Plugins section, if the file is located under a modules or plugin directory, Mura uses the file to obtain various settings and information about the module or plugin too.

      Convention-Based Lookup

      The config.xml.cfm file will be discovered in any theme directory, module directory, content type directory, or plugin directory.

      For example:

      • {context}/themes/{ThemeName}/
      • {context}/themes/{ThemeName}/content_types/{Type}/
      • {context}/themes/{ThemeName}/content_types/{Type}_{Subtype}/
      • {context}/themes/{ThemeName}/modules/{Module}/
      • {context}/sites/{SiteID}/themes/{ThemeName}/
      • {context}/sites/{SiteID}/themes/{ThemeName}/content_types/{Type}/
      • {context}/sites/{SiteID}/themes/{ThemeName}/content_types/{Type}_{Subtype}/
      • {context}/sites/{SiteID}/themes/{ThemeName}/modules/{Module}/
      • {context}/plugins/{PluginName}/plugin/
      • {context}/plugins/{PluginName}/modules/{Module}/
      • {context}/plugins/{PluginName}/content_types/{Type}_{Subtype}/

      As covered in the Mura Rendering section, you can nest additional module directories and content type directories within each other, and Mura will automatically search for the config.xml.cfm file in those too.

      Please refer to the Elements of the "config.xml.cfm" File section for details about the file itself.

      Elements Of The "config.xml.cfm" File

      At the top level, a config.xml.cfm document should contain the mandatory <mura> element. There are no attributes available for this element, unless being used with a module/display object.

      The following table describes available attributes, only if used with a module/display object.

      Attribute Req/Opt Default Description
      name Required This is the actual name or title of the module/display object.
      contenttypes Optional empty string If blank, it will never display as a draggable option. You may use the asterisk (*) to indicate the display object may be used on all content types. Or, you may pass in a comma-delimited list of content types the display object may be used on. For example, contenttypes="{Type | Type/Subtype},Folder/Blog,Folder/News".
      omitcontenttypes Optional empty string You may use the asterisk (*) to indicate the display object should not be used on any content types. Or, you may pass in a comma-delimited list of content types the display object should not be used on. For example, contenttypes="{Type | Type/Subtype},Folder/Blog,Folder/News".
      condition Optional true You may pass in a condition to be evaluated at runtime as to whether the display object should be available. For example, condition="(m.content('title') == 'News')".
      iconclass Optional mi-cog You may use a Fontawesome icon class, prefixed with "mi-" instead of "fa-" to avoid potential conflicts. For example, iconclass="mi-book". The icon specified here will be used to identify the display object.
      cacheoutput Optional true If false, output will not be cached, when Site Caching is enabled.

      Example <mura>

      This attributes will only be used with a display object.

      <mura name="My Display Object" contenttypes="*" iconclass="mi-book">
        ...
      </mura>
      

      Subordinate to the <mura> element is an <imagesizes> element, which may contain one or more custom image sizes, and an <extensions> element, which may contain one or more custom class extension definitions.

      <mura>
          <imagesizes>
          </imagsizes>
          <extensions>
          </extensions>
      </mura>

      Note: Plugins have additional elements, and will be covered in the Plugin Development section.

      <imagesize> is a sub-element of <imagesizes>

      The following table describes the available attributes.

      Attribute Req/Opt Description
      name Required Treat the value of this attribute as a variable name. In other words, do not include any spaces or special characters.
      width Required Specify the numeric width in pixels.
      height Required Specify the numeric height in pixels.

      Example <imagesize>

      <imagesize name="yourimagesizename" width="600" height="100"/>
      

      <extension> is a sub-element of <extensions>

      The following table describes the available attributes.

      Attribute Req/Opt Description
      type Required

      The Base Type to apply the class extension to. Valid options are:

      • 1 (to indicate User Group)
      • 2 (to indicate User)
      • Address
      • Page
      • Folder
      • File
      • Calendar
      • Link
      • Component
      • Site
      • Base
      subtype Required Use "default" to apply the class extension to all, or enter a custom subtype. Does not apply to the Site type.
      availablesubtypes Optional Answers the question; "Allow users to add only specific subtypes?". Leave blank to allow all content types. Or, you may pass in a comma-delimited list of content types. For example, availablesubtypes="{Type | Type/Subtype},Folder/Blog,Folder/News".
      description Optional May enter a description for the class extension.
      hasassocfile Optional Answers the question; "Show 'Associated Image' field when editing?". Valid options are: 1 (yes/true), 0 (no/false). Default is "1".
      hasbody Optional Answers the question; "Show 'Body' field when editing?". Valid options are: 1 (yes/true), 0 (no/false). Default is "1". Applies to Page, Folder, and Calendar.
      hasconfigurator Optional Answers the question; "Show 'List Display Options' field when editing?". Valid options are: 1 (yes/true), 0 (no/false). Default is "1". Applies to Folder only.
      hassummary Optional Answers the question; "Show 'Summary' field when editing?". Valid options are: 1 (yes/true), 0 (no/false). Default is "1". Applies to Page, Folder, File, Calendar, and Link.
      iconclass Optional You may use a Fontawesome icon class, prefixed with "mi-" instead of "fa-" to avoid potential conflicts. For example, iconclass="mi-book". The icon specified here will be used to identify the display object.
      isactive Optional Answers the question; "Status". Valid options are: 1 (yes/true), 0 (no/false). Default is "1".

      Example <extension>

      <extension type="Page" subType="Product" hasSummary="1" hasBody="1" isActive="1">
      	...
      </extension>

      <attributeset> is a sub-element of <extension>

      An attribute set is similar to creating a <fieldset> element in HTML.

      The following table describes the available attributes.

      Attribute Req/Opt Description
      name Required This will display on the content edit screen as the title or label above the grouping of associated attributes, similar to a fieldset's legend.
      container Required This is the "tab" container which the grouping of associated attributes will be displayed on. Valid options depend on the content Type. See the Available Container Options table.
      orderno Optional Specify the numeric

      Available Container/Tab Options

      Option Description
      Basic Displays the attribute set on the Basic tab.
      Default Displays the attribute set on the Extended Attributes tab. This is the default setting.
      Custom Will not display the attribute set by default. This allows for a developer to create a custom user interface to collect the data.
      List Display Options DIsplays the attribute set on the List Display Options tab.
      Layout & Objects Displays the attribute set on the Layout & Objects tab.
      Categorization Displays the attribute set on the Categorization tab.
      Tags Displays the attribute set on the Tags tab.
      Related Content Displays the attribute set on the Related Content tab.
      Advanced Displays the attribute set on the Advanced tab.
      Publishing Displays the attribute set on the Publishing tab.
      Usage Report Displays the attribute set on the Usage Report tab.

      Valid Container/Tab Options

      The following table describes available options to use as the value for each Base Type.

      Base Type Container/Tab Options
      1 (User Groups), 2 (Users), & Site
      • Default
      • Custom
      Pages, Folders, Links, Files, and Calendars
      • Basic
      • Default
      • List Display Options
      • Layout & Objects
      • Categorization
      • Tags
      • Related Content
      • Advanced
      • Publishing
      • Usage Report
      • Custom
      Address, Custom, and Base
      • Basic
      • Default
      • Custom
      Component
      • Basic
      • Default
      • Categorization
      • Usage Report
      • Custom

      Example <attributeset>

      <attributeset name="Mobile Options" container="Basic" orderno="1">
          ...
      </attributeset>

      <attribute> is a sub-element of <attributeset>

      An attribute describes a data container (form field) and its validation rules.

      The following table describes required and optional attributes of the <attribute> element:

      Attribute Description
      name The variable name. This will also become the form fields "name" attribute.
      label The label that will be displayed on the form
      hint A tooltip hint for the form field
      type Valid options: TextBox (default), TextArea, HTMLEditor, SelectBox, MultiSelectBox, RadioGroup, File, Hidden
      required Valid options: true, false (default)
      validation Leave blank if not using validation. Otherwise, valid options are: Date, DateTime, Numeric, Email, Regex, Color, and URL.
      regex If validation is set to Regex, then you can enter a JavaScript regular expression to execute when the form is submitted.
      message The error message that displays when validation fails.
      defaultvalue Optional. The default value for the form field.
      optionlist A carat "^" delimited list of option values. Used when type is set to SelectBox, MultiSelectBox or RadioGroup.
      optionlabellist A carat "^" delimited list of option labels. Used when type is set to SelectBox, MultiSelectBox or RadioGroup.
      adminonly Valid options: 0 for false (default), 1 for true. Whether the attribute is for administrator use only, and will not be displayed.

      Note: To enable a color picker for a form field, set type="TextBox" and validation="Color"

      Example config.xml.cfm

      <?xml version="1.0" encoding="UTF-8"?>
          <mura>
              <!-- Image Sizes -->
              <imagesizes>
                <imagesize
                  name="locationimage"
                  width="600"
                  height="400"/>
              </imagesizes>
      
              <!-- Class Extensions -->
              <extensions>
                  <extension 
                      adminonly="0" 
                      availablesubtypes="Page/Location" 
                      description="" 
                      hasassocfile="0" 
                      hasbody="0" 
                      hasconfigurator="0" 
                      hassummary="0" 
                      iconclass="mi-map-o" 
                      subtype="Locations" 
                      type="Folder"/>
      
                  <extension 
                      availablesubtypes="" 
                      description="" 
                      hasassocfile="1" 
                      hasbody="0" 
                      hasconfigurator="0" 
                      hassummary="1" 
                      iconclass="mi-map-pin" 
                      subtype="Location" 
                      type="Page">
      
                      <attributeset 
                          container="Basic" 
                          name="Location Options" 
                          orderno="1">
      
                          <attribute 
                              adminonly="0" 
                              defaultvalue="" 
                              hint="" 
                              label="Location Street" 
                              message="Please enter a Location Street" 
                              name="locationstreet" 
                              optionlabellist="" 
                              optionlist="" 
                              orderno="1" 
                              regex="" 
                              required="true" 
                              type="TextBox" 
                              validation=""/>
      
                          <attribute 
                              adminonly="0" 
                              defaultvalue="" 
                              hint="" 
                              label="Location City" 
                              message="Please enter a Location City" 
                              name="locationcity" 
                              optionlabellist="" 
                              optionlist="" 
                              orderno="2" 
                              regex="" 
                              required="true" 
                              type="TextBox" 
                              validation=""/>
      
                          <attribute 
                              adminonly="0" 
                              defaultvalue="" 
                              hint="" 
                              label="Location State" 
                              message="Please enter a Location State" 
                              name="locationstate" 
                              optionlabellist="" 
                              optionlist="" 
                              orderno="3" 
                              regex="" 
                              required="true" 
                              type="TextBox" 
                              validation=""/>
      
                          <attribute 
                              adminonly="0" 
                              defaultvalue="" 
                              hint="" 
                              label="Location Postal Code" 
                              message="" 
                              name="locationpostalcode" 
                              optionlabellist="" 
                              optionlist="" 
                              orderno="4" 
                              regex="" 
                              required="false" 
                              type="TextBox" 
                              validation=""/>
      
                      </attributeset>
                  </extension>
              </extensions>
          </mura>
      

      Export Definitions via Admin UI

      Mura is able to export Class Extensions as an XML file via the back-end administration UI. This is the recommended method for creating XML Class Extension definitions, as opposed to writing the XML by hand. By using this method, you're able to reduce the likelihood of typos or introducing errors when Mura attempts to parse your XML file(s).

      There are two methods for exporting Class Extensions. You can export them individually, or select multiple extensions to export as a single XML file.

      Export Single Class Extension

      To export a single Class Extension, follow the steps outlined below.

      1. From the back-end administration area of Mura, select Site Settings, click Class Extensions, and select Class Extensions.
      2. You should be directed to the Class Extensions screen.
      3. Select the Class Extension you wish to export, from the listing of available Class Extensions.
      4. You should be directed to the Class Extension screen.
      5. Click the Export Class Extension button.
      6. You should directed to the Export Class Extensions screen.
      7. You can hover over the code, and press <CTRL>+C on a PC, or <CMD>+C on a Mac to copy it to your clipboard, then paste the code into your own XML file.
      8. Or, you can simply click the Download button at the bottom of the screen, to download the code as a XML file.
      9. You now have a file that can be used to import the Class Extension into another instance of Mura, or package it with a display object's config.xml.cfm file or a plugin's config.xml.cfm file for future distribution.

      Export Multiple Class Extensions

      To export multiple Class Extensions at the same time, so they can be packaged together in a single XML file, follow the steps outlined below.

      1. From the back-end administration area of Mura, select Site Settings, then click Class Extensions, and select Export Extensions.
      2. Or, from the Class Extension screen, select the Actions button, then click Export.
      3. You should be directed to the Export Class Extensions screen.
      4. Select the Class Extensions you wish to export, or simply click Select All to check all available Class Extensions if you desire to do so.
      5. Click the Export button.
      6. You should now be directed to the Export Class Extensions screen.
      7. You can hover over the code, and press <CTRL>+C on a PC, or <CMD>+C on a Mac to copy it to your clipboard, then paste the code into your own XML file.
      8. Or, you can simply click the Download button at the bottom of the screen, to download the code as a XML file.
      9. You now have a file that can be used to import the Class Extension into another instance of Mura, or package it with a display object's config.xml.cfm file or a plugin's config.xml.cfm file for future distribution.

      Import Definitions via Admin UI

      To import Class Extensions, you will first need a properly formatted XML file, preferably exported via Mura's admin UI. Once you have a properly formatted XML file, follow the steps below to import the Class Extensions.

      1. From the back-end administration area of Mura, select Site Settings, click Class Extensions, then select Import Extensions.
      2. Or, from the Class Extensions screen, select the Actions button, and click Import.
      3. You should be directed to the Import Class Extensions screen.
      4. Click the Browse or Choose File button, then select navigate to the exported XML file.
      5. When ready, click the Import button.
      6. Mura will then upload, and attempt to parse the submitted XML file. When the import has completed, your new Class Extensions should appear in the list on the Class Extensions screen.

      Displaying Extended Attributes

      There are a few ways to display the attributes of Class Extensions, and it really depends on where you're attempting to display them. That said, the most important thing to keep in mind is that you'll need to know your attribute's name property, in order to display it.

      In short, a Class Extension's attribute is treated as a property of the bean it's associated with. So, for example, if a Class Extension's Base Type is Content, then you could use the same getter and setter methods when working with any other attribute of a Content Bean.

      Basic Syntax

      The basic syntax is shown in the example below.

      someBean.get('attributeName')
      someBean.get{AttributeName}()
      someBean.getValue('attributeName')

      Examples

      The examples listed below demonstrate the most common method for accessing attributes of a Class Extension. In addition, the recommended best practice is to use esapiEncode when outputting attributes.

      Via Layout Template

      This example assumes the Base Type is "Content", and you merely want to display the attribute on a layout template.

      <cfoutput>
        <h2>
          #esapiEncode('html', m.content('attributeName'))#
        </h2>
      </cfoutput>
      

      Via HTML Editor

      This example assumes you wish to output an attribute in the body of a HTML Editor.

      [m]esapiEncode('html', m.content('attributeName'))[/m]

      Via Content Bean

      This example assumes you load a Content Bean, and then want to retrieve the data for whatever reason.

      contentBean = m.getBean('content').loadBy(title='Home');
      // assumes we have a custom attribute labeled `locationstreet`
      street = contentBean.get('locationstreet');
      

      Via Iterator

      You can easily pull out attributes of Class Extensions while looping inside of a Mura Iterator.

      <!--- get an iterator of the content item's children --->
      <cfset it = m.content().getKidsIterator()>
      <ul>
      <cfloop condition="it.hasNext()">
        <cfset item = it.next()>
        <li>
          #esapiEncode('html', item.get('attributeName'))#
        </li>
      </cfloop>
      </ul>
      

      Making Attributes Editable

      When displaying an extended attribute in a layout template, you may want to make the attribute editable by content managers when they're using front-end editing features. This is accomplished by using m.renderEditableAttribute.

      Note: This feature only works on extended attributes with its Input Type set to TextBox or HTMLEditor.

      Function Syntax

      m.renderEditableAttribute(
        attribute
        , type
        , required
        , validation
        , message
        , label
        , value
        , enableMuraTag
      )
      

      Parameters

      Parameter Type Req/Opt Default Description
      attribute string required   The attribute's name
      type string opt text

      Valid options are:

      • text
        • Treats the attribute as text being entered into a TextBox
      • HTMLEditor
        • When editing content via inline edit mode, and a content manager double clicks into the text, a HTML Editor toolbar will appear.
      required boolean opt false If true, Mura will require a value to be entered before saving successfully.
      validation regex opt blank Leave blank, or enter a JavaScript regular expression to use for validation purposes. Will trigger if a value is entered and a save is attempted.
      message string opt empty string The message to display when the submitted value does not pass validation.
      label string opt attribute name Text to display above the field when edited.
      value string opt the attribute's stored value Optionally pass in a string to preset the value.
      enableMuraTag boolean opt false If true, the value will be parsed by Mura's setDynamicContent to render any Mura [m] tags.

      Example

      The following example demonstrates how to use m.renderEditableAttribute for an attribute with its Input Type set to HTMLEditor.

      <cfoutput>
        <div>
          #m.renderEditableAttribute(
            attribute='attributeName'
            type='HTMLEditor'
            label='Attribute Label'
          )#
        </div>
      </cfoutput>

      Mura Modules & Display Objects

      Mura's baked-in modules are managed via the front-end user interface. Modules are sometimes referred to as "display objects" because often times, that's how the module itself is used. However, a Mura module does not have to contain a "display" of any kind. Modules can be complete applications, or simply contain some custom logic that is triggered based on a desired event.

      Note: If you aren't familiar with Mura's default Modules, you should review the Layout Manager Modules section of the Using Mura Guide before continuing in this section.

      Mura includes a variety of baked-in modules (display objects) by default while offering content managers the ability to quickly and easily add modules such as Collections, Components, Containers, Forms, Navigation, and many more.

      Each of Mura's modules contain some server-side logic, basic markup, styling, and sometimes include JavaScript. In this section, you'll learn how to safely modify this logic, markup, styling, and/or JavaScript, and do so without worrying about your changes getting overwritten whenever Mura's files are updated. In addition, you'll learn how to create your own, custom modules too.

      Modifying Mura's Modules

      Mura's base modules (display objects) are located under the following directory:

      {context}/core/modules/v1/

      As illustrated in the image below, there are more modules than the ones listed.

      You shouldn't modify any of the files in these directories directly. If you choose to do so, you run the risk of your changes being overwritten whenever Mura is updated to the latest version.

      Instead, copy the desired directory and all of its files, then paste it under one of the locations listed in the "Lookup Hierarchy" section below. Then, you can safely make your modifications without fear of losing any of your changes the next time Mura is updated.

      Registering a Custom "modules" Directory

      While Mura automatically scans for modules in the directories specified in the "Lookup Hierarchy" section below, you may register a custom "modules" directory using the code example below.

      m.siteConfig().registerModuleDir(
        dir='/path/to/your/modules/'
      );

      The directory path should be a logical path to a CFML directory, and is usually registered in the onApplicationLoad() event.

      Lookup Hierarchy

      As mentioned under "The 'Modules' Directory" section, Mura uses the following lookup hierarchy when searching for modules, and aborts the lookup process once the target module has been located:

      • Registered Module Directory
        • {RegisteredModuleDirectory}/
        • A pre-registered module directory path. See "Registering a Custom 'Modules' Directory" section above.
      • Module
        • ../{module}/modules/
        • A nested "modules" directory nested within a known module.
      • Theme
        • ../themes/{ThemeName}/modules/
        • Theme modules are only used when the specified theme is actively assigned to a site. Keep in mind themes may be located under either the global themes directory ({context}/themes/{ThemeName}), or under a site ({context}/sites/{SiteName}/themes/{ThemeName}).
      • Site
        • {context}/sites/{SiteName}/modules/
        • Site modules are shared across all themes within the specific site.
      • Global
        • {context}/modules/
        • Global modules are shared across all sites under a single Mura instance.
      • Core
        • {context}/core/modules/v1/core_assets/modules/
        • These are the "core" modules or display objects which may be copied and placed into any of the directories above to be safely modified.

      Note: You should not edit the "core" modules directly. If you do, you run the risk of losing your edits or changes whenever you update Mura to the latest version.

      Anatomy of a Module

      Mura's default modules follow a specific directory structure and file naming convention. These conventions also apply to the development of custom modules (including those known as Mura "display objects" in prior versions).

      Directory Structure

      First, create a subdirectory using the name of the module, under any standard or registered "modules" directory within the Mura file structure. For example, ../sites/mysite/modules/mymodule/ or  ../themes/mytheme/modules/{module}/. 

      Inside of the{module} directory, you may use the following files and directories as applicable:

      File or Directory Req/Opt Description
      index.cfm Optional If your module will be used as as part of the Layout Manager, this file will manage and/or contain the view that is rendered to the browser. See example "index.cfm" file section below.
      config.xml.cfm Optional This is the configuration file for the module itself. This file also allows you to include custom image sizes, class extensions, and more. See example "config.xml.cfm" file section below. Also see Elements Of The "config.xml.cfm" File for more about available elements and attributes.
      configurator.cfm Optional This file allows developers to include a form with configuration options which appear on the module configuration panel when using the Inline Edit feature. See example "configurator.cfm" file section below.
      /model/ Optional In order to leverage Mura ORM, the module can contain its own "model" directory, which in turn may also include  "beans" and/or "handlers" directories. See Mura ORM Configuration for more about the "model" directory.
      /content_types/ Optional You may also include custom content types. This is useful for keeping content types directly related to specific module(s) together, in a unified directory structure. See Content Type for more information.
      /modules/ Optional You may also nest custom modules. This is useful for keeping related modules together, under one, unified module itself. One caveat is that if you choose to display the nested object on the parent object, content managers will not be able to access the nested object's configurator, if it has one. Because of this, you may wish to either omit the "contenttypes" attribute from your config.xml.cfm's <mura ...> node, or explicitly add contenttypes="" so that the nested object will not appear in the module panel's UI. If you need configurable options for the nested object, you should include the options on the parent object's configurator. To include the nested object in the view of the parent object, use the following syntax: #m.dspObject(object='{objectName}', objectparams=objectparams)#.

      Example "index.cfm" File

      The example below shows the contents of a basic "index.cfm" file for a module.
      For custom modules, you may include your own code or markup as needed.

      <cfparam name="objectparams.mytext" default="" />
      
      <cfoutput>
        <h2>My Object</h2>
      
        <cfif Len(objectparams.mytext)>
          <p>
            This object has a configurator, and the value of "objectparams.mytext" is:<br>
            <strong>#esapiEncode('html', objectparams.mytext)#</strong>
          </p>
        <cfelse>
          <!--- No value entered for "objectparams.mytext" --->
        </cfif>
      </cfoutput>

      Use MuraJS to add any CSS or JavaScript to your module. The example below leverages the loader() method to load both CSS and JavaScript. Simply add this code to your "index.cfm" file, changing file paths to match your specific directory structure and filenames as needed.

      <!--- Add the following to your "index.cfm" file --->
      <script>
        Mura(function(m) {
          m.loader()
            .loadcss(m.themepath + '/modules/myobject/my.css')
            .loadjs(
              m.themepath + '/modules/myobject/my.js',
              m.themepath + '/modules/myobject/other.js',
              function() {
                // Do something with the loaded JS, if desired
              }
            );
        });
      </script>
      

      Visit the MuraJS section to learn more about Mura.loader() capabilities.

      Rendering Via JavaScript

      If your module will be rendered using only JavaScript, your index.cfm file should only contain the following code:

      <cfset objectparams.render="client">

      Choosing this option means you'll have to load your JavaScript by using a custom event handler method, as shown below.

      // ../yourmodule/model/handlers/yourhandler.cfc
      
      component extends='mura.cfobject' {
      
        function onRenderStart(m) {
          // if script should be included in the <head>
          arguments.m.addToHTMLHeadQueue('<script src="/path/to/script.js"></script>');
      
          // OR
      
          // if script should be included before closing </body> tag
          arguments.m.addToHTMLFootQueue('<script src="/path/to/script.js"></script>');
        }
      
      }
      

      Please also see the Modules and Display Objects and Mura JS sections for more information on rendering modules via JavaScript.

      Example "config.xml.cfm" File

      This is one example of a standard "config.xml.cfm" file. Visit the Elements Of The "config.xml.cfm" File section for more details on available elements and attributes.

      <?xml version="1.0" encoding="UTF-8"?>
        <mura name="My Module" contenttypes="*" iconclass="mi-rebel">
          <!-- May also include other elements here -->
        </mura>

      Example "configurator.cfm" File

      This is very basic example of a module's "configurator.cfm" file.

      <cfparam name="objectparams.mytext" default="" />
      
      <cfoutput>
          <div class="mura-control-group">
              <label class="mura-control-label">My Text</label>
              <input  type="text"
                      name="mytext"
                      class="objectParam"
                      value="#esapiEncode('html_attr', objectparams.mytext)#" />
          </div>
      </cfoutput>
      

      See the Module Configurators for more on proper markup conventions.

      Content Types

      In addition to using some of Mura's content-related event hooks, developers can control the rendering of the body area by targeting a content's Type and Subtype. This feature also applies to any custom Class Extensions. For example, "Folder/Contacts", "Page/Contact", etc.

      Mura automatically scans the following directories for custom content types:

      • {context}/themes/{ThemeName}/content_types/
      • {context}/sites/{SiteID}/themes/{ThemeName}/content_types/
      • {context}/sites/{SiteID}/content_types/
      • ../content_types/{type}/content_types/
      • ../content_types/{type}_{subtype}/content_types/

      Or, you can register any directory you want by using the following syntax:

      m.siteConfig().registerContentTypeDir(
        '/path/to/your/content_types/'
      );
      
      // Path is a logical path to a CFML directory
      // Usually registered in onApplicationLoad();
      

      Note: This feature only works if the layout template is using m.dspBody() to render the body area.

      Control Body By Type/Subtype

      You target the body's Type/Subtype by a conventional directory structure.

      For example, you could target content by only its Type:

      ../content_types/{type}/
      
      // If you wish to target 'Page'
      ../content_types/page/index.cfm
      

      Or, you can target by both the Type, and its Subtype:

      ../content_types/{type}_{subtype}/
      
      // If you wish to target 'Page/Contact'
      ../content_types/page_contact/index.cfm
      

      Anatomy of a {type}_{subtype} Directory

      As previously mentioned, you create a directory under a known or registered "content_types" directory by using {type}_{subtype} (e.g., ../content_types/page_contact/ or ../.content_types/component_default/, etc.). Within the {type}_{subtype} directory, you may have the following files and/or directories:

      File or Directory Req/Opt Description
      index.cfm Required This is the body or view used by Mura for display file itself. At a minimum, this file should contain <cfoutput>#m.renderEditableAttribute(attribute="body",type="htmlEditor")#</cfoutput>, if you wish to see any text entered into the "Content" area. See example "index.cfm" file below.
      config.xml.cfm Optional This is the configuration file. This allows you to include custom image sizes, class extensions, and more. See Elements Of the "config.xml.cfm" File section for more information.
      /model/ Optional If you wish to leverage Mura ORM, the content type could have its own "model" directory, which in turn could also include a "beans" and/or "handlers" directory too. See Mura ORM Configuration section for more information about the "model" directory.
      /modules/ Optional You may also include custom modules. This is useful for keeping modules and/or display objects directly related to specific content types together, in a unified directory structure. See Modules section for more information. See below for information on how to output modules in your layout template.
      /content_types/ Optional You may include a nested "content_types" directory. This is a great way to keep related code items together.

      Example "index.cfm" File

      The example below illustrates what an "index.cfm" file could contain. However, you may include your own custom markup, code, and more.

      <cfoutput>
        <article class="article">
          <!--- Page Title --->
          <h2 class="article__title">#m.renderEditableAttribute(attribute='title')#</h2>
      
          <!--- Primary Image --->
          <cfif m.content().hasImage(usePlaceholder=false)>
            <figure class="article__image">
              <img src="#m.getURLForImage(fileid=m.content('fileid'), size='medium')#" 
                   alt="#esapiEncode('html_attr', m.content('title'))#" />
            </figure>
          </cfif>
      
          <!--- Content --->
          <div class="article__body">
            #m.renderEditableAttribute(attribute='body', type='htmlEditor')#
          </div>
        </article>
      </cfoutput>

      How To Output Modules

      To output a module (display object) in your custom body layout, use the following syntax:

      <cfoutput>
        #m.dspObject(object='{objectName}')#
      </cfoutput>

      When hardcoding a module in your layout, the "Delete" button will not appear for content managers when using the "Inline Edit" mode.

      Including Configurable Modules

      The first thing to understand about including configurable modules in your custom body layout is there can only be one (1) module that is configurable. Whichever module you wish to be configurable should include a "objectparams" parameter, and include the "objectparams" as the value, as shown below:

      <cfoutput>
        #m.dspObject(object='{objectName}', objectparams=objectparams)#
      </cfoutput>

      If you attempt to include multiple configurable modules using the code example above, the first module will be configurable, and all others will not. This means, when you select "Inline Edit" mode, only the first configurable module will include the pencil icon and actually be configurable.

      To include additional modules, you may pass in any "objectparams" parameters as an object, as show in the example below:

      <cfoutput>
        #m.dspObject(
          object='object1', 
          objectparams={param1=objectparams.param1, param2=objectparams.param2}
        )#
      
        #m.dspObject(object='object2', objectparams=objectparams)#
      </cfoutput>

      In the example above, "object2" will be the only configurable module.

      Internationalization & Localization

      Mura utilizes resource bundles to internationalize various areas of the user interface, making the code locale-independent.

      Resource bundles are .properties files, located under specified directories in Mura. These .properties files, or resource bundles, are named to indicate the language code, and country code. For example, the language code for English (en) and the country code for United States (US) would be represented as en_US.properties. If Mura cannot find a direct language and region match, it will search for a language match. If a locale's language cannot be found, Mura will fall back to its default of English, or en_US.properties.

      The file itself is comprised of key-value pairs. The keys remain the same throughout each of the .properties files, and the value is translated into the file's designated language. If Mura is searching for a specific key-value pair within a translation, and cannot locate it, Mura will fall back to the English translation.

      The image below illustrates the en_US.properties file side-by-side with the es_ES.properties file:

      Lookup Hierarchy

      Mura automatically searches for resource bundles under specific directories, and uses the key-value pair(s) found in the order outlined below. If a resource_bundles directory does not exist in the following locations, you may safely create one, and place your resource bundle files there.

      • Module
        • ../{module}/resource_bundles/
        • Module (aka "Display Object") resource bundles are used for the specific module itself.
      • Content Types
        • ../content_types/{type}_{subtype}/resource_bundles/
        • Content Types resource bundles are used for the specified content type.
      • Theme
        • ../{ThemeName}/resource_bundles/
        • Theme resource bundles are only used when the specified theme is actively assigned to a site.
      • Site
        • {context}/sites/{SiteID}/resource_bundles/
        • Site resource bundles are shared across all themes within the specified site.
      • Global
        • {context}/resource_bundles/
        • Global resource bundles are shared across all sites under a single Mura instance.
      • Core
        • {context}/core/modules/v1/core_assets/resource_bundles/
        • If the requested key-value pair is not defined in any of the locations above, Mura will use the "core" resource bundles located here for Mura's modules.

      Note: Admin-area resource bundles are located under {context}/core/mura/resourceBundle/resources/. However, many key-value pairs are not able to be overwritten using the technique described above.

      Contribute

      If you would like to contribute to the translations project, please visit https://crowdin.com/project/muracms. Your help will be greatly appreciated!

      Custom Keys

      As previously documented, Mura searches for custom ".properties" files, under the directories listed in the "Lookup Hierarchy" section.

      You don't have to include all of the key-value pairs from the original files. You only have to include the key-value pairs you wish to override. In addition, if you have any custom keys you would like to use in your site, you can simply include them in your custom ".properties" files.

      Displaying Custom Keys

      To output a custom key-value from your ".properties" file, simply use m.rbKey({keyName}).

      So, for example, to output a key of "myCustomString", use the following syntax.

      <cfoutput>
        #m.rbKey('myCustomString')#
      </cfoutput>

      Custom Factories

      Mura allows you to create your own resource bundle factories, and fall back to Mura's original lookup hierarchy when a requested key-value is not found. This is especially useful when creating plugins, or integrating a custom application.

      The code example below demonstrates how you could go about creating a custom resource bundle factory.

      customResourceBundleDirectory = ExpandPath('#m.globalConfig('context')#/path/to/your/RBFactory/');
      
      customFactory = new mura.resourceBundle.resourceBundleFactory(
        parentFactory = m.siteConfig('rbFactory')
        , resourceDirectory = customResourceBundleDirectory
        , locale = m.siteConfig('JavaLocale')
      );
      

      You could wrap this up into a custom method to obtain a reference to your custom factory using something similar to the following example.

      public any function getResourceBundleFactory(required struct m) {
        var resourceDirectory = ExpandPath('#arguments.m.globalConfig('context')#/plugins/MyPlugin/rb/');
        return new mura.resourceBundle.resourceBundleFactory(
          parentFactory = arguments.m.siteConfig('rbFactory')
          , resourceDirectory = resourceDirectory
          , locale = arguments.m.siteConfig('JavaLocale')
        );
      }

      Once you've created your custom resource bundle factory, simply use {yourCustomFactoryVariableName}.getKey({yourKey})

      So, for example, to output a key of "myCustomString", with a custom factory variable named "customFactory", use the following syntax.

      <cfoutput>
        #customFactory.getKey('myCustomString')#
      </cfoutput>
      

      Caching

      Mura has a built-in caching mechanism that, when enabled, will store data in memory for reuse. The purpose for this is to improve overall site performance, by decreasing the amount of reads to the database. Mura's caching uses a "lazy load" approach, which means data won't be cached until it's called upon the first time.

      How to Enable/Disable Site Caching

      Follow the steps below to enable and/or disable site caching.

      1. From the back-end administrator, on the main navigation, click Site Settings, then select Edit Settings
      2. From the Site Settings screen, select the Basic tab.
      3. Scroll down to Site Caching.
        • Select On to enable site caching.
        • Select Off to disable site caching.
      4. After making your selection, click Save Settings.

      Cache Free Memory Threshold

      By default, when Site Caching is enabled, Mura will only store data into memory when the Java virtual machine (JVM) has at least sixty percent (60%) available memory. You may choose to set an alternate minimum desired percentage by following the steps outlined below.

      1. From the back-end administrator, on the main navigation, click Site Settings, then select Edit Settings.
      2. From the Site Settings screen, select the Basic tab.
      3. Scroll down to Cache Free Memory Threshold (Defaults to 60%), and enter in your desired percentage amount. If you enter zero (0), Mura will revert to using its default setting of sixty percent (60%).
      4. After entering your desired amount, click Save Settings.

      cf_CacheOMatic Tags

      One of the easiest ways to improve the performance of your site, is to use proven caching techniques. In addition to the native caching strategies for CFML, Mura also offers a special, custom tag, <cf_CacheOMatic>, to use for expensive database transaction code, or other time-consuming CFML code that doesn't need to be executed during each request.

      Using Mura's <cf_CacheOMatic> tag allows you to add data to Mura's cache. The tag only works when Site Caching is enabled, which is a great reason to use it since you can easily disable it, if necessary.

      The custom tag is located under {context}/core/mura/customtags/CacheOMatic.cfm if you would like to inspect the file to see how it works under the hood.

      Tag Syntax

      <cf_CacheOMatic>
        Code or data to cache.
      </cf_CacheOMatic>
      

      Attributes

      Attribute Type Default Description
      key string CGI.scriptname & CGI.query_string A unique string to associate with the specified data. Used to retrieve the cached data, when Site Caching is enabled.
      timespan datetime CreateTimeSpan(0,0,30,0) The desired time period the data should be cached for before it expires. (Defaults to 30 minutes).
      scope string application The desired CFML scope in which to store the data. Valid options are application, session, and server.
      nocache boolean false If true, Mura will not cache the code/data contained within the tag. Useful for explicitly omitting code from being cached.
      purgecache boolean request.purgecache If true, Mura will empty its cache.
      siteid string request.siteid You may choose to specify an alternate SiteID to associate the cache to.

      Usage

      Use this custom tag to cache inline content or data within layout templates.

      Examples

      Using the "key" Attribute

      This example uses the ContentID of the content item as the key with prefix of "page-".

      <cf_CacheOMatic key="page-#m.content('contentid')#">
        <h2 class="page-title">#esapiEncode('html', m.content('pagetitle'))#</h2>
      </cf_CacheOMatic>

      Using the "nocache" Attribute

      The following example demonstrates how you could use the nocache attribute to see an "un-cached" version of the output by simply appending ?nocache=true to the URL.

      <cf_CacheOMatic 
           key="example-timestamp1" 
           nocache="#m.event('nocache')#">
        <p>The time is now: #LSTimeFormat(Now())#</p>
      </cf_CacheOMatic>

      Using the "timespan" Attribute

      In this example, we're setting the cache to expire in five (5) hours.

      <cf_CacheOMatic
           key="example-timestamp2"
           nocache="#m.event('nocache')#"
           timespan="#CreateTimeSpan(0,5,0,0)#">
        <p>The time is now: #LSTimeFormat(Now())#</p>
      </cf_CacheOMatic>
      

      Programmatic Caching

      As covered in the cf_CacheOMatic Tags section, Mura allows developers to add data to Mura's cache. One thing to keep in mind is your data or code may be either intentionally or even inadvertently wrapped by cf_CacheOMatic tags, subjecting your output to the settings of the tag.

      However, as a developer, you may have data or code you wish to either explicitly exclude from being added to Mura's cache. Or, you may wish to set a specific duration of time the data or code may be cached for. Mura also allows developers to achieve this using the code snippets described below.

      request.cacheItem

      This code snippet controls whether the code or data will be cached if wrapped by cf_CacheOMatic tags. If true, the code or data will be cached according to the settings of the containing cf_CacheOMatic tags. If false, the code or data will not be cached.

      Note: A side-effect of setting request.cacheItem to false is none of the code or data within the containing cf_CacheOMatic tags will be cached at all.

      Syntax

      request.cacheItem = {true || false}
      

      Example

      <cfset request.cacheItem = false />
      <!--- The following timestamp will NOT be cached --->
      <cfoutput>
        <p>The date/time is #Now()#</p>
      </cfoutput>
      

      request.cacheItemTimeSpan

      This code snippet controls the duration of time the code or data will be cached for.

      Syntax

      request.cacheItemTimeSpan = {TimeSpan Object}

      Example

      <cfset request.cacheItemTimeSpan = CreateTimeSpan(0, 0, 5, 0) />
      <!--- The following timestamp will be cached for 5 minutes, if wrapped by cf_CacheOMatic tags --->
      <cfoutput>
        <p>The date/time is #Now()#.</p>
      </cfoutput>
      

      Mura Scope

      Similar to how CFML includes various scope types, Mura also includes its very own "Mura Scope" for accessing, and manipulating, Mura CMS variables, data, and objects. In a nutshell, the Mura Scope allows you to "talk" with Mura.

      The Mura Scope is available via mura, $, or m. As covered in the Template Variables & Helper Methods section of Theme Developer's Guide, the Mura Scope has many helper methods. We'll cover more helper methods throughout this guide. You'll also learn how to create a custom instance of the Mura Scope, for those rare times it may not be defined for you.

      In addition to the helper methods, the Mura Scope has a number of subscopes that will be covered throughout this guide as well.

      Examples

      The Mura Scope is used extensively by Mura developers, and examples of its use are scattered throughout this guide. A few simple examples demonstrating use of the Mura Scope are also noted below.

      <!--- Example from within .cfm templates --->
      #m.content('title')#
      
      <!--- Example when using the [m] or [mura] tag --->
      [m]m.content('title')[/m]
      

      Using CFML's <cfdump> tag, you can easily inspect the Mura Scope yourself to see all of the data and helper methods included for you.

      <!--- Inspecting the Mura Scope --->
      <cfdump var="#m#">
      

      Custom Instance of the Mura Scope

      As a Mura developer, you are going to run into situations from time-to-time where the Mura Scope is not accessible to you. This most often occurs when you're processing logic falls outside of the normal Mura event model (e.g., Ajax requests, accessing your application directly under a non-Mura directory, etc.).

      As long as you're working within the realm of the Mura application itself, you can easily create a custom instance of the Mura Scope. One important thing to keep in mind is that you'll most likely want to initialize the Mura Scope with a SiteID.  The reason for this is that most often, you're probably going to to be working with site-specific information, and unless the Mura Scope has been initialized with a SiteID, you won't have access to any site-specific data.

      Also, unless a user is accessing your logic directly, and outside of the normal Mura event model, the session scope typically contains a SiteID key. However, there may be rare instances when this does not occur, and when it does, you can always fall back to using the one site that each and every Mura installation includes, which is the "default" site, and its SiteID is always default

      Using this knowledge, the examples highlighted below should hopefully make a bit more sense.

      Examples

      Below is an example of how to create a custom instance of the Mura Scope, checking for an instance of session.siteid, and using that to initialize the Mura Scope, or using the default SiteID, if, for whatever reason, it has not been defined yet.

      if ( !IsDefined('m') ) {
        siteid = session.keyExists('siteid') ? session.siteid : 'default';
        m = application.serviceFactory.getBean('m').init(siteid);
      }
      

      While the most common method for initializing the Mura Scope is with a SiteID string, it can also be initialized with an event object or a struct that contains a siteid key. 

      if ( !IsDefined('m') ) {
      
        // Assumes you have a pre-defined event object named `yourEventObject`
        m = application.serviceFactory.getBean('m').init(yourEventObject);
      
        // OR
      
        yourStruct = {
          siteID = 'SomeSiteID'
          , someKey = 'Some Value'
        };
      
        m = application.serviceFactory.getBean('m').init(yourStruct);
      }
      

       

      setCustomMuraScopeKey

      As you can see, the Mura Scope is a great way to access Mura CMS variables, data, and objects. There may be times when you want to add your own variables, data, and/or objects to the Mura Scope as well, and as you'll see, it's very easy to do just that.

      In the Mura Events section, the various event/request lifecycles and contextual events that occur within Mura are covered. At any point within the lifecycle, or contextual event, you can choose to register your own custom Mura Scope key. Once it has been registered, the key will continue to be available for the remainder of the subsequent lifecycle and/or contextual event.

      Function Syntax

      m.setCustomMuraScopeKey(
        name
        , value
      )

      Parameters

      Parameter Type Req/Opt Default Description
      name string Req This is the name of the custom Mura Scope key.
      value any Req This is the "value" returned when referencing the Mura Scope key.

      Examples

      To register your custom Mura Scope key, you first select the point in the lifecycle, or contextual event, you wish to create your custom key, and then choose your desired event handler to place your custom logic (e.g., the Site eventHandler.cfc, Theme eventHandler.cfc, or any other registered event handler).

      Setting a Custom Key to a Simple String

      The example below is using the onSiteRequestStart event to register a simple string to be stored in the Mura Scope.

      public function onSiteRequestStart(m) {
        m.setCustomMuraScopeKey('myCustomKey', 'This is my custom Mura Scope Key value!');
      }

      Accessing the Custom Key

      <cfoutput>
        <p>#m.myCustomKey#</p>
      </cfoutput>
      

      The above example should output the following:

      <p>This is my custom Mura Scope Key value!</p>

      Setting a Custom Key to an Object

      First, we'll need an example object. Let's assume you have the following code in a file labeled exampleObject.cfc.

      component {
      
        public function sayHello() {
          return 'Hello from exampleObject.cfc!';
        }
      
      }

      Next, we'll select an event to add our logic to, and set a custom key that references the example object.

      public function onBeforeUserSave(m) {
        var myObject = new exampleObject();
      
        m.setCustomMuraScopeKey('myObject', myObject);
      }

      Because we've specified the onBeforeUserSave event in the example above, our new Mura Scope Key will be available in the onAfterUserSave event, if needed.

      Accessing the Custom Key's Object

      <cfoutput>
        #m.myObject.sayHello()#
      </cfoutput>

      The above example should output the following:

      Hello from exampleObject.cfc!