52
Programmers Training Mura CMS Version 5.3 Version: 1.5 Published: October 20, 2010 1

Programmers Guide MuraCMS

  • Upload
    mao1312

  • View
    932

  • Download
    9

Embed Size (px)

Citation preview

Page 1: Programmers Guide MuraCMS

Programmers TrainingMura CMS Version 5.3

Version: 1.5Published: October 20, 2010

1

Page 2: Programmers Guide MuraCMS

FILE STRUCTURE! 4......................................................................................................................Core Directories and Files! 4

Site Directories! 4.......................................................................................................................................Basic Structure! 4

The Mura Tag! 6.........................................................................................................Embedding Java, CFML or cfscript! 6

The Mura Event Model! 7.........................................................................................................Life-Cycle of a Front End Request! 8

...................................................................................................................................Contextual Events! 9

The Mura Scope ! 12.....................................................................................................................................Content Scope ! 12

.........................................................................................................................................Event Scope! 13

................................................................................................................................Component Scope! 13

.............................................................................................................................Current User Scope! 14

.................................................................................................................................Site Config Scope! 14

............................................................................................................................Global Config Scope! 14

.............................................................................Helper Methods and Template Rendering Methods! 15

...........................................................................................................................Session User Facade! 15

Understanding Base Mura Objects! 17..........................................................................................................................................Mura Beans! 17

........................................................................................................................................Content Bean! 18

.......................................................................................................................Content Comment Bean! 19

.............................................................................................................................................User Bean! 19

......................................................................................................................................Category Bean! 20

..............................................................................................................................................Site Bean! 21

.......................................................................................................................................Mura Iterators! 22

..........................................................................................................................................Mura Feeds! 24

...................................................................................................................Local ContentRenderer.cfc! 25

.........................................................................................................................Local EventHandler.cfc! 27

.................................................................................................................Theme ContentRenderer.cfc! 28

...................................................................................................................................Method Injection! 28

................................................................................................................................Resource Bundles! 28

Mapping Events in Mura! 30......................................................................................Using addEventHandler() Outside of a Plugin! 30

2

Page 3: Programmers Guide MuraCMS

....................................................................................................Using addEventHandler() in a Plugin! 31

Custom Types and Output! 33....................................................................................................Class Extension Manager: Overview! 33

Creating Plugins in Mura CMS ! 42.............................................................................................................................................Config.xml! 42

..............................................................................................................................................Plugin.cfc! 44

.............................................................................................................................................license.txt! 44

.............................................................................................................................................config.cfm! 45

..............................................................................................................................................index.cfm! 45

.........................................................................................................................................PluginConfig! 46

Custom Caching! 51.........................................................................................................................................How to use it! 51

....................................................................................................................Additional Cache Settings! 51

......................................................................................................................The cf_cacheomatic tag:! 52

3

Page 4: Programmers Guide MuraCMS

FILE STRUCTURECore Directories and FilesWhen you first install Mura CMS, you'll see a standard set of files and folders:  /admin - This contains the main Mura content administration code.

/config - This contains the configuration files - including the settings.ini.cfm where Mura stores itʼs core settings. Attribute values can contain dynamic expression denoted inside ${expression}.

/config/mappings.cfm -Mura will create this file on the first request after installation. This file provides a place to add and edit CF mappings that Mura and any sub-applications use

/config/cfapplication.cfm -Similar to the mappings.cfm, this file provides an update safe place to add and edit CF Application.cfc variables.

/config/appcfc - This directory contains individual Application.cfc elements, which can be used to assemble plugin Application.cfcs.

/config/coldspring.custom.xml.cfm - By default this does not exist.  However, you can create it and it will be included into the main coldspring.xml.cfm

/default - This directory contains all the site-specific files and will be duplicated and renamed for each site you create within Mura. If you have multiple sites within your Mura instance, you will see each site's folder added at the same level as /default (each site folder will be named based on the siteID you enter in the site settings when adding that site)

/plugins - This contains deployed plugins. At initial Mura installation, this will be empty.

/requirements - This contains the core Mura CMS code, as well as any CFML. frameworks that Mura requires

/tasks - This directory contains various utilities including ckeditor and ckfinder.

Site DirectoriesBasic Structure

/{siteID}/includes/display_objects - This directory contains default display object code.

/{siteID}/includes/display_objects/custom - This directory is where developers can put custom display object code.

/{siteID}/includes/email    - This directory contains the email template(s) used in the email broadcaster.

/{siteID}/includes/plugins - This directory is where plugins that are configured to run their display objects locally are deployed (In addition to  /plugins).

4

Page 5: Programmers Guide MuraCMS

/{siteID}/includes/resourceBundles - This directory contains the resource bundles for the built-in display objects.

/{siteID}/includes/templates - In this directory are the templates available for any given site when no theme is applied. This directory has been deprecated in favor of moving toward requiring themes.

/{siteID}/includes/themes -  This directory contains the the site's available themes.

/{siteID}/includes/themes/{theme}/templates -  This directory contains the templates that are available for any given site that is using this theme.

/{siteID}/includes/themes/{theme}/templates/components -  This directory contains the templates designed for “Components” that are available for any given site that is using this theme.

/{siteID}/includes/themes/{theme}/display_objects - This directory contains display object files that have been packaged with this theme.

/{siteID}/includes/themes/{theme}/resourceBundles - This directory contains resource bundle files that have been packaged with this theme. You only need to add keys that are unique to this theme.

5

Page 6: Programmers Guide MuraCMS

The Mura TagMura provides a markup syntax that allows users to insert dynamic expressions into the Mura admin UI. 

Embedding Java, CFML or cfscriptTo embed Java, ColdFusion or cfscript into a page, create a separate file with your CFML, Java or CFScript and place it in /{siteID}/includes/display_objects/custom/ and use the dspInclude() method to include it in your page or component as shown below:

[mura]dspInclude({path to cfm file})[/mura]

Examples:dspIncludeDisplays the given cfm file in the context of your page:

[mura]$.dspInclude('templates/inc/theFile.cfm')[/mura][mura]$.dspInclude('display_objects/custom/theFile.cfm')[/mura]

dspObject

Displaying a Mura Display Object

[mura]$.dspObject({object type} ,{objectID} [,{siteId}])[/mura]

Rendering a feed:

[mura]$.dspObject('feed' ,{feedId})[/mura]

Rendering a feed without summaries:

[mura]$.dspObject('feed_no_summary' ,{feedId})[/mura]

Rendering a Component:

[mura]$.dspObject('component', {contentId}[/mura]

createObject

Directly invoke methods on CFCs:

[mura]createObject('component', {path.to.component} ).init()[/mura]

Access a Java object:

[mura]createObject('java', {java.lang.System} ).getProperty('java.version')[/mura]

ColdFusion Functions

Access built-in ColdFusion functions:

[mura]now()[/mura]

OTHER USAGES:• Feed advanced param critera values.

• Extended attribute default values, option list and option list label.

6

Page 7: Programmers Guide MuraCMS

The Mura Event ModelThe Mura application flow is a chain of events that fire in sequence.  Each link in the chain can be intercepted to either (1) provide additional or (2) replace existing business logic. This allows for extreme ease of customization.

The most common objects used during a front end page request are:

7

Page 8: Programmers Guide MuraCMS

Life-Cycle of a Front End RequestThis is the general life-cycle of events that get fired when a Front-End Request is made (page render, etc.). Note that Events that begin with "onGlobal" are defined on a per-Mura instance basis.Events that start with “standard” are actually units of implemented logic that you can replace. As a references you can find them in the following directories:

• /requirments/mura/handler

• /requirments/mura/validator

• /requirments/mura/transaltor

Events that start with “on” are places where you can add additional logic.

onGlobalRequestStartonSiteRequestInitonSiteRequestStartstandardSetContentRendererstandardSetContentHandler! standardSetPreviewHandler    ! standardSetAdTrackingHandler! standard404Validator  ! standard404Handler! ! onSite404standardWrongDomainValidatorstandardWrongDomainHandlerstandardTrackSessionValidator! standardTrackSessionHandlerstandardSetPermissionHandlerstandardSetIsOnDisplayHandlerstandardDoActionsHandlerstandardRequireLoginValidator! standardRequireLoginHandlerstandardSetLocaleHandlerstandardDoResponseHandler! onRenderStart        ! ! standardLinkTranslator        ! ! standardFileTranslator            !! onBeforeFileRender            !! onAfterFileRender        ! ! standardTranslationHandler            !! standardHTMLTranslator                ! ! ! onSiteEditProfileRender             ! ! ! onSiteSearchRender                ! ! ! onSiteLoginPromptRender                ! ! ! onContentOffLineRender                ! ! ! onContentDenialRender                ! ! ! on{type}{subType}BodyRender

8

Page 9: Programmers Guide MuraCMS

                ! ! ! on{type}BodyRender                ! ! ! onBeforeFormSubmitRedirect

                ! ! ! onFormSubmitPollRender! ! ! ! onFormSubmitResponseRender                ! ! ! onBeforeFormSubmitSave! ! ! ! onAfterFormSubmitSave! ! ! standardForceSSLValidator! ! ! standardForceSSLHandler    ! onRenderEndonSiteRequestEndonGlobalRequestEnd

Contextual EventsContextual events are fired only in response to other events - they are only fired when needed. It is imported to note that contextual events only contain data that is directly supplied to it by the Mura core. If you need to access to main Mura front end or Admin event you can access it with the Mura Scope object:

<cfset globalEvent =$.getGlobalEvent() >

Application EventsonApplicationLoad! ! ! onSiteSessionStartonGlobalSessionStart! ! ! onSiteSessionEndonSiteMissingTemplate! ! ! onSiteErroronGlobalError! ! ! ! onBeforeAutoUpdateonAfterAutoUpdate! ! ! onGlobalThreatDetect

Admin Rendering EventsonDashboardPrimaryToponDashboardPrimaryBottomonDashboardSidebarToponDashboardSidebarBottomonContentEditonGroupEditonUserEditonFEToolbarAdd (renders in front end toolbar add list)onGroupEdit (renders as a tab when editing a group)onUserEdit (renders as a tab when editing a User)onContentEdit (Node Level Only) (renders as a tab when editing a User)onAfterSiteDeployRender (renders above list of sites after manually deploying a site)onAdminModuleNav (renders inside admin left nav)

Staging to Production EventsonSiteDeploy! ! ! onBeforeSiteDeployonAfterSiteDeploy! ! onAfterSiteDeployRender       

9

Page 10: Programmers Guide MuraCMS

Login EventsonSiteLogin! ! ! onGlobalLoginonSiteLoginSuccess! ! onGlobalLoginSuccessonSiteLoginBlocked! ! onGlobalLoginBlocked

Content EventsNode-Level events only fire for node-level content types (ie. Page, Portal, Gallery, File, Calendar, Gallery)

onBeforeContentSave (Node Level Only)onBefore{type}SaveonBefore{type}{subType}Save

onAfter{type}SaveonAfter{type}{subType}SaveonAfterContentSave (Node Level Only)

onBeforeContentDelete (Node Level Only)onBefore{type}deleteonBefore{type}{subType}delete

onAfterContentDelete (Node Level Only)onAfter{type}deleteonAfter{type}{subType}delete

onBeforeContentSortonAfterContentSort

onBeforeContentDeleteVersionHistory (Node Level Only)onBefore{type}DeleteVersionHistoryonBefore{type}{subType}DeleteVersionHistory

onAfterDeleteVersionHistory (Node Level Only)onAfter{type}DeleteVersionHistoryonAfter{type}{subType}DeleteVersionHistory

onBeforeContentDeleteVersion (Node Level Only)onBefore{type}ContentDeleteVersiononBefore{type}{subType}ContentDeleteVersion

onAfterContentDeleteVersion (Node Level Only)onAfter{type}ContentDeleteVersion (Node Level Only)onAfter{type}{subType}ContentDeleteVersion

Content Comment EventsonBeforeCommentUpdate! ! onBeforeCommentCreateonBeforeCommentSave!! ! onBeforeCommentDelete

onAfterCommentUpdate!! ! onAfterCommentCreateonAfterCommentSave! ! ! onAfterCommentDelete

10

Page 11: Programmers Guide MuraCMS

Category EventsonBeforeCategoryUpdate! ! onBeforeCategoryCreateonBeforeCategorySave! ! ! onBeforeCategoryDelete

onAfterCategoryUpdate !! ! onAfterCategoryCreateonAfterCategorySave! ! ! onAfterCategoryDelete

Feed EventsonBeforeFeedUpdate! ! ! onBeforeFeedCreateonBeforeFeedSave! ! ! onBeforeFeedDelete

onAfterFeedUpdate! ! ! onAfterFeedCreateonAfterFeedSave! ! ! onAfterFeedDelete

User EventsonBeforeUserUpdate! ! ! onBeforeUserCreateonBeforeUserSave! ! ! onBeforeUserDeleteonBeforeUser{subType}Update! ! onBeforeUser{subType}CreateonBeforeUser{subType}Save! ! onBeforeUser{subType}Delete

onAfterUserUpdate! ! ! onAfterUserCreateonAfterUserSave ! ! ! onAfterUserDeleteonAfterUser{subType}Update ! ! onAfterUser{subType}CreateonAfterUser{subType}Save! ! onAfterUser{subType}Delete

11

Page 12: Programmers Guide MuraCMS

The Mura ScopeThe Mura Scope provides a standard, concise syntax for interfacing with Mura objects (and their properties and events).             It is available as either "$" or "mura".

<cffunction name="onRenderStart" output="false"><cfargument name="$"><cfset property=$.event({property})><!--- do stuff---></cffunction>

<cffunction name="onRenderStart" output="false"><cfargument name="mura"><cfset property=mura.event({property})><!--- do stuff ---></cffunction>

If you are writing code that will run outside of the normal Mura event model you can obtain an istance it from the application.serviceFactory object.

<cfset $=application.serviceFactory.getBean(“muraScope”)>

You then can init it with either an instance of an event object, a struct containing value pairs or simply a siteID string value.

<cfset $.init(event)><cfset $.init(myStruct)><cfset $.init(session.siteID)>

If you init your MuraScope instance with an event object or struct be sure to include a siteID attribute if it is available.

<cfset event.setValue({siteID})><cfset $.init(event)><cfset myStruct.siteID={siteid}><cfset $.init(myStruct)>

Content ScopeThe Content scope wraps the current front end request's contentBean.  All values that you would previously get with request.contentBean.get{Attribute} or event.getContentBean().get{Attribute} are now accessible through the Content Scope. 

It is important to understand that the content scope is only available during the front-end request lifecycle.

Returns value:

$.content({property});

Sets and returns the value:

$.content({property},{property Value});

12

Page 13: Programmers Guide MuraCMS

Returns full contentBean:

$.content();

Returns curresnt content scope parent as contentNavBean based on crumbdata:

$.getParent();

Returns current content crumbdata inside of a content iterator:

$.getCrumbIterator();

Return current content scope parent as contentBean:

$.content().getParent();

You can also access all built-in contentBean methods:

$.content().getKidsIterator();$.content().getCategoriesIterator();$.content().getRelatedContentIterator();$.content().getCommentsIterator();$.content().getVersionHistoryIterator();$.content().getStats().getComments();$.content().getCrumbIterator();$.content().getURL({querystring});$.content().save();

Event ScopeThe Event scope simply wraps the current request's event object which contains merged data from both the CFML FORM and URL scopes.Returns value:

$.event('property');

Sets and return the value:

$.event({property},{property value});

Returns full event object:

$.event();

In contextual events you are able to access the outer global event object with $.getGlobalEvent().

 

Component ScopeThe Component scope is only available from within Mura Components and templates are assigned to Mura Components.Returns value:

$.component({property});

Sets and returns the value:

$.component({property},{property value});

Returns full component contentBean;

$.component();

13

Page 14: Programmers Guide MuraCMS

A simple example of using the Component Scope in a template:

<cfoutput><div>#$.setDynamicContent($.component('body'))#</div></cfoutput>

 

Current User ScopeThe Current User scope wraps the new Session User Facade object, which in turn wraps the session.mura struct. The main benefit of the new Session Facade is that it knows how and when to retrieve data that is not normally stored in the session.mura struct, including extended attributes.Returns value:

$.currentUser({property});

Sets and returns the value:

$.currentUser({property},{propertyValue});

Returns sessionUserFacade:

$.currentUser();

Returns the current user's group memberships:

$.currentUser().getMembershipsIterator();

You can also save any changes that may be made to user during a session:

$.currentUser(‘fname’,‘Robert’);$.currentUser().save();

 Site Config ScopeSite Config scope is specific to the site in which you're working at the moment. Returns value:

$.siteConfig({property});

Sets and returns the value:

$.siteConfig({property},{propertyValue});

Returns full site settingsBean;

$.siteConfig();

Global Config ScopeThe Global Config scope wraps the global configuration for your Mura instance, which is configured in your Mura intance's /config/settings.ini.cfm.Returns value:

$.globalConfig({property});

14

Page 15: Programmers Guide MuraCMS

Sets and returns the value:

$.globalConfig({property},{property value});

Returns full configBean:

$.globalGlobal();

Any custom attributes set in the Mura instances /config/settings.ini.cfm are available:

$.siteConfig(‘customVar’);

Helper Methods and Template Rendering MethodsAlong with these scopes, Mura has additional helper methods and template rendering methods.

Helper MethodsFor example, value content, feed, user, category, userManager:

$.getBean({bean type});$.announceEvent({EventToAnnounce});$.renderEvent({EventToRender});$.getPlugin({package|pluginID|moduleID});

Template Rendering Methods All contentRender methods are available:

$.dspInclude({path_to_file});$.dspObjects({displayRegion});$.dspObject({type}, {objectID});$.createHREF(filename={filename} [, type={type}, contentID= {contentID}, complete={complete}, showMeta={showMeta}, queryString={queryString}] );$.getTopID();$.getTopVar({varName}};...

Session User Facade This new object is available to all objects through the base mura.cfobject by getCurrentUser(). You have access to all built-in userBean methods as well as some extra helpers.

Built-In Methods$.currentUser().setFName({value});$.currentUser().setValue({property},{property value});$.currentUser().getMembershipsIterator();$.currentUser().getAddressesIterator();$.currentUser().readAddress(addressName={addressName});$.currentUser().getInterestGroupsIterator();$.currentUser().save();....

Helper Methods  15

Page 16: Programmers Guide MuraCMS

$.currentUser().isSuperUser();$.currentUser().isPrivateUser();$.currentUser().isInGroup({groupname} [,{isPublic}] );$.currentUser().isLoggedIn();$.currentUser().logout();

16

Page 17: Programmers Guide MuraCMS

Understanding Base Mura ObjectsMura BeansBeans provide you with the ability to directly access and manipulate their associated records in the database. This can be accomplished by using the beans' helper methods (see below for examples).

Common helper methods:

BEAN.LOADBY({PROPERTIES...}):

This new method gives you the ability to load persistent data from the database based on the properties passed.

Beans and how you can load them up:• Load a content bean by (optional properties: contentID, contenthistID, fileName, remoteID)

• Load a feed bean by (optional properties: feedID, name, remoteID)

• Load a user bean by (optional properties: userID, username, groupName, remoteID)

• Load a category bean by (optional properties: categoryID, name, remoteID)

EXAMPLE:NOTE: The second argument of "siteID" is only required when there is no siteID defined or is not the same as the siteID value set in the current Mura Scope's event (ie. $.event("siteID”).

bean=$.getBean({beantype} ).loadBy({property}={propertyValue} [,siteID={siteID}] );

What happens if a loadBy parameter has multiple matches:If the parameters sent to a loadBy() request have more than one match then the method will return an array of beans.

For example, if the following request had more that one matches:

content = $.getBean( "content" );loadByResponse = content.loadBy( remoteID={remoteID});

The “loadByResponse” variable would be an array of contentBean objects and the “content” variable would the contentBean in the first position of the array.

BEAN.GETVALUE({PROPERTY});This existing method has been expanded to provide an easy hook to extended attributes. Simply pass in the variable name for the attribute and your value will be retrieved. No more needing to use getExtendedAttribute (it's still in there though, in case you rely on it).

Examples:Get some extended data:

attributeValue = bean.getValue( 'attributeA' );

17

Page 18: Programmers Guide MuraCMS

Get a common field:

value = bean.getMenuTitle();value = bean.getValue(‘MenuTitle’);

BEAN.SETVALUE({PROPERTY}, {PROPERTY VALUE});This existing method has been expanded to allow the setting of extended attributes; no more needing to figure out extend set id's, etc.

Example:Set extended information:

bean.setValue( 'menuTitle', 'value' );

BEAN.SAVE()The save method is quite simple. If you want to save the data in the bean to the database (add or update), just call this method.

Example:Set extended information:

bean.setValue( 'menuTitle', 'value' );

Save the record:

bean.save();

DELETE()As long as the data is persistent, when you call this method, it will delete the associated record from the database.

Example:Let's delete the record:

bean.delete();

Content BeanLoad by contentID:

content = $.getBean( "content" ).loadBy( contentID={contentID} [, siteID={siteID}] );

Load by contentHistID:

content = $.getBean( "content" ).loadBy( contentHistID={contentHistID} [, siteID={siteID}] );

Load by remoteID:

content = $.getBean( "content" ).loadBy( remoteID={remoteID} [, siteID={siteID}] );

Load by filename (index.cfm/filename/):

content = $.getBean( "content" ).loadBy( filename={filename} [, siteID={siteID}] );

18

Page 19: Programmers Guide MuraCMS

Key methods:content.set({args});content.save();content.delete();

content.getParent()content.getIsNew();content.getURL({queryString});content.hasDrafts();content.deleteVersion();content.deleteVersionHistory();content.getKidsIterator([{liveOnly},{aggregation}]);content.getKidsQuery([{liveOnly},{aggregation}]);content.getCrumbIterator();content.getCrumbArray();content.getCommentsIterator();content.getCommentsQuery();content.getCategoriesIterator();content.getCategoriesQuery();content.getVersionHistoryIterator();content.getVersionHistoryQuery();content.getRelatedContentIterator();content.getRelatedContentQuery();content.setNewFile({filePath/URL});

Content Comment BeanLoad by commentID:

comment = $.getBean( "comment" ).loadBy(commentID={commentID});

Key methods:comment.set({args});comment.save();comment.delete();comment.getUser();comment.getParent();comment.getIsNew();comment.getKidsIterator([{boolean:isEditor}]);comment.getKidsQuery([{boolean:isEditor}]);comment.getCrumbIterator();comment.getCrumbQuery();

User BeanLoad by userID:

user = $.getBean( "user" ).loadBy( userID={userID} );

Load by username:

19

Page 20: Programmers Guide MuraCMS

user = $.getBean( "user" ).loadBy( username={username} [, siteID={siteID}] );

Load by remoteID:

user = $.getBean( "user" ).loadBy( remoteID={remoteID} [, siteID={siteID}] );

Load by groupname:

group = $.getBean( "user" ).loadBy( groupname={groupname}, siteID={siteID} [, {isPublic}] );

Read address Bean by Name:

user.readAddress(name={name});

Read address by addressID:

user.readAddress(addressID={addressID});

Read empty address:

user.readAddress();

Key methods:user.set({args});user.save();user.delete();user.getIsnew();user.addAddress({address});user.getAddressssIterator();user.getAddressesQuery();user.getMembershipsIterator();user.getMembershipsQuery();user.getMembersIterator();user.getMembersQuery();user.getInterestGroupsIterator();user.getInterestGroupsQuery();

Category BeanLoad by categoryID:

category = $.getBean( "category" ).loadBy( categoryID={categoryID} [, siteID={siteID}] );

Load by name:

category = $.getBean( "category" ).loadBy( name={name} [, siteID={siteID}] );

Load by remoteID:

category = $.getBean( "category" ).loadBy( remoteID={remoteID} [, siteID={siteID}] );

20

Page 21: Programmers Guide MuraCMS

Key methods:category.set({args});category.save();category.delete();category.getParent();category.getIsNew();category.getKidsIterator();category.getKidsQuery();category.getCrumbIterator();category.getCrumbQuery();

Site BeanThe siteBean can be accessed from the Mura Scope as well as directly from the settingsManager.  It contains key information methods to reference a specific site configuration.

Accessing it from the Mura Scope:

site=$.siteConfig();

Accessing it from the settingsManager:

site=getBean("settingsManager").getSite({siteID});

Key Methods:These methods return the starting URL path to be used with things like JS and CSS file referencing:

site.getAssetPath();site.getThemeAssetPath();

These methods return the starting file path to be used for <cfinclude>:

site.getIncludePath();site.getThemeIncludePath();

These methods return the starting component path for object instatiation:

site.getAssetMap();site.getThemeAssetMap();

This returns an instance of the site's contentRenderer for use with static methods:

site.getContentRenderer();

This returns an instance of the themeʼs contentRenderer for use with static methods:

site.getThemeRenderer();

21

Page 22: Programmers Guide MuraCMS

Mura IteratorsMura iterators make managing content and users simpler by providing direct access to objects.  A Mura Iterator wraps a traditional CFML query object. It is most often used in conditional loop statements:

<cfloop condition=”Iterator.hasNext()”>

And the first line inside of the condition loop is obtaining a reference to the actual next value object:

<cfset item=Iterator.next()>

And you then can use it for whatever the purpose of the iteration is for:

<cfoutput>#HTMLEditFormat(item.getValue(‘myVar’))#</cfoutput>

It is very important to note that the the Iterator.hasNext() is page aware. The Mura Iterator was built with pagination in mind. This means that it will hasNext() with only return true if it was iterated through less than the value of it Iterator.getNexttN() value.

For example, if an iterator has 20 items inside of it the following code would output the first 10:

<cfset iterator.setNextN(10)><cfloop condition=”Iterator.hasNext()”><cfset item=Iterator.next()><cfoutput>#HTMLEditFormat(item.getValue(‘myVar’))#</cfoutput></cfloop>

If you set the value of the Iteratorʼs nextN to 0 it will set the next to the recordcount of the current query.

<cfset iterator.setNextN(0)>

You can change the page that is iterated through by settings the iteratorʼs current page:

<cfset iterator.setPage(2)>

You can also iterate backwards as well:

<cfset Iterator.end()><cfloop condition=”Iterator.hasPrevious()><cfset item=iterator.previous()”><cfoutput>#HTMLEditFormat(item.getValue(‘myVar’))#</cfoutput></cfloop>

Key MethodsIterator.setQuery();Iterator.recordCount();Iterator.setPage({pageNum});Iterator.pageCount();Iterator.setNextN({nextN});Iterator.reset(); (Moves the current index to the beginning of the query)Iterator.hasNext();Iterator.next(); (Returns the next value)Iterator.end(); (Moves the current index to the end of the query)Iterator.hasPrevious();

22

Page 23: Programmers Guide MuraCMS

Iterator.previous(); (Returns the previous value)

Using a Iterator Returned from a Method<!---Read out an existing node from the default site.---><cfset content=$.getBean('content').loadBy(filename='blog',siteID='default')><!--- Pull out a content iterator of the node's child content.---><cfset it=content.getKidsIterator()><!--- The number of items to be listed is determined by the content.getNextN() value. Its default is 10. ---><cfset it.setPage(1)><!--- You can also set the start row instead of setting a page number. ---><cfset it.setStartRow(1)><cfloop from="1" to="#it.pageCount()#" index="p"><cfset it.setPage(p)><cfoutput><h2>Page #p#</h2></cfoutput><!---Iterate throught the child content.The it.hasNext() will return true until the page lengthas determined by the content.getNextN() has been reached --><cfloop condition="it.hasNext()"> <!--- The it.next() method returns a new contentNavBean. It acts as a facade to data into the wrapped query while digging into the full bean when needed. ---><cfset sub1=it.next()><cfoutput><a href="#sub1.getURL()#">#it.currentIndex()#:#sub1.getMenuTitle()#</a></br></cfoutput></cfloop></cfloop>

Using an Iterator with a Custom QueryThe Mura contentIterator can decorate any query that contains the columns “SiteID” and “ContentID”.

<cfquery name="rs" datasource="#$.globalConfig('datasource')#">select contentID, siteid from tcontent where parentID=<cfqueryparam cfsqltype="cf_sql_varchar" value="{contentID}">and siteID=<cfqueryparam cfsqltype="cf_sql_varchar" value="{siteID}">and active=1</cfquery><cfset it=$.getBean("contentIterator")><cfset it.setQuery(rs)><cfloop condition="it.hasNext()"><cfset item=it.next()><a href="#item.getURL()#">#item.getTitle()#</a><br/></cfloop>

23

Page 24: Programmers Guide MuraCMS

Mura FeedsFeeds allow you to create custom Mura queries.

Content FeedLoad by feedID:

feed = $.getBean( "feed" ).loadBy( feedID={feedID} [, siteID={siteID}] );

Load by name:

feed = $.getBean( "feed" ).loadBy( name={name} [, siteID={siteID}] );

Load by remoteID:

feed = $.getBean( "feed" ).loadBy( remoteID={remoteID} [, siteID={siteID}] );

EXAMPLE:<!--- Create a feed bean ---><cfset feed=$.getBean('feed')><cfset feed.setSiteID({siteID})><!--- Set the number of records to return, enter 0 for all---><cfset feed.setMaxItems(100)><!--- other sort by colums (menuTitle, title, lastUpdate, releaseDate, orderNo, displayStart, created, rating, comments, credits, type, subType, {extendedAttribute}) ---><cfset feed.setSortBy('created')><cfset feed.setSortDirection('desc')><!--- filter off of contentID ---><!--- NOTE: the second argument (if true) appends to already existing value of contentID ---><cfset feed.setContentID('11223344', {append:false})><!--- filter category id ---><cfset feed.setCategoryID('1122', {append:false})><!--- advanced filter ---><cfset feed.addAdvancedParam(field='{table}.{field}', relationship='{AND|OR}' condition='{EQUALS|EQ|IS|GT|LT|NEQ|GTE|LTE|BEGINS|CONTAINS}', criteria='{value}', dataType='{varchar|numeric|timestamp|...}')><!--- get the iterator from the feed ---><cfset feedIterator = feed.getIterator()><!--- or ---><cfset feedQuery = feed.getQuery()>

User Feed<!--- Create a feed bean ---><cfset userFeed=$.getBean('userFeed')><cfset userFeed.setSiteID({siteID})><!--- filter off of groupID ---><cfset userFeed.setGroupID('1122', false)><!--- advanced filter ---><cfset userFeed.addAdvancedParam(field='tusers.lastname', relationship='AND' condition='EQUALS', criteria='Smith', dataType='varchar')><!--- get the iterator from the feed ---><cfset userIterator = userFeed.getIterator()>

24

Page 25: Programmers Guide MuraCMS

Local ContentRenderer.cfcEvery site in your Mura instance has a local contentRenderer.cfc located at {siteID}/includes/contentRenderer.cfc.  It simply extends the base mura.content.contentRenderer component.  It is provided to allow you to override or add new rendering behaviours on a per-site basis. In addition, all local contentRenderer methods are available from the Mura Scope site-specific events.

For example, if you were to add the following method to your site's local contentRenderer:

<cffunction name="dspDateTime" output="false"><cfrreturn now()></cffunction>

You would then be able to refer to that method in any site specific eventHandler method as:

<cfoutput>#$.dspDateTime()#</cfoutput>

Or in the body of a content node as:

[mura]$.dspDateTime()[/mura]

Available Renderer SettingsIn addition to being able to redefine or add new methods, the local contentRenderer.cfc allows you to adjust various rendering settings on a per-site basis.

This determines what JavaScript library Mura should use with its built-in display objects. The options are Prototype and jQuery.

<cfset this.jslib="jquery"/>

This allows you set 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.

<!---<cfset this.navOffSet=0/>--->

This sets the maximum depth that standard navigation will follow.

<!---<cfset this.navDepthLimit=1000/>--->

This allows developers to not rely on Mura to load the default JS framework and simply add it to their theme's HTMLhead.

<!---<cfset this.jsLibLoaded=false>--->

This is the default long date format for the site.

<!---<cfset this.longDateFormat="long"/>--->

This is the default short date format for the site.

<!---<cfset this.shortDateFormat="short"/>--->

This is a list of file extensions and content types that will not directly download, but instead will render in an Mura CMS page with summary information.

<!---<cfset this.showMetaList="jpg,jpeg,png,gif">--->

This is a list of what image extensions should be shown in built in content listing display objects.

25

Page 26: Programmers Guide MuraCMS

<!---<cfset this.imageInList="jpg,jpeg,png,gif">--->

This tells Mura whether to serve images indirectly through fileManager.renderImage() or create direct links.

<!---<cfset this.directImages=true/>--->

This allow developers to choose whether site personalizations such as ratings and favorites are attached to simple cookies or an actual Mura user.  Options are user or cookie.  Users do not need to login in order to save cookie-based personalizations.

<!---<cfset this.personalization="user">--->

This toggles whether the Mura tag call are actually processed by the setDynamicContent() method.

<!---<cfset this.enableMuraTag=true/>--->

This toggles whether the front end toolbar should be rendered for administrative users.

<!---<cfset this.showAdminToolBar=true/>--->

This toggles whether the front end toolbar should be rendered for site members.

<!---<cfset this.showMemberToolBar=false/>--->

This toggles whether editable display objects like components and content collections should be rendered.

<!---<cfset this.showEditableObjects=false/>--->

This toggle whether to render the request's HTMLHeadQueue.

<!---<cfset this.renderHTMLHead=true/>--->

The following settings allow developers to change how Mura handles content hierarchy within its display objects. For example, some developers prefer H1 tags for their page titles, some prefer H2 tags - you can adjust this here. 

<!---<cfset this.headline="h2"/>---><!---<cfset this.subHead1="h3"/>---><!---<cfset this.subHead2="h4"/>---><!---<cfset this.subHead3="h5"/>---><!---<cfset this.subHead4="h6"/>--->

ShowMeta for Link and File NodesThe current request's showMeta value is used to determine how Link and File requests are handled. 

It can be retrieved from the current event scope.

<cfset showMeta = $.event("showMeta")>

Options:0 - Files and Links requests are either rendered as an HTML summary page or served directly according to the local contentRenderer's showMetaList value.

1 - Files and Links are always render as HTML summary page.

2 - File and Links requests are always served directly.

26

Page 27: Programmers Guide MuraCMS

HTMLHeadQueueMura provides an HTMLHeadQueue where you can add files to be rendered inside of the current request's HTMLhead.  Its main purpose is to reduce duplicate HTMLhead entries.

Tell Mura to add its main JS library to the HTMLHeadQueue.  The queue will render in the same order that items are added to it.

<cfset $.loadJSLib() />

Add your custom file to the HTMLHeadQueue.  The pathing starts at the {siteid}/includes/display_objects directory.

<cfset $.addToHTMLHeadQueue("custom/htmlhead/slideshow.jquery.cfm")>

Or, if this is something that you would like to include in a theme you can also add your custom file there:

/{siteID}/includes/themes/{theme}/display_objects/{targetFile}

Look up hierarchy:• /{siteID}/includes/themes/{theme}/display_objects/htmlhead/{targetFile}

• /{siteID}/includes/display_objects/htmlhead/{targetFile}

• /{siteID}/includes/themes/{theme}/display_objects/{targetFile}

• /{siteID}/includes/display_objects/{targetFile}

THE CONTENTS OF A TYPICAL FILE IN THE HTMLHEADQUEUEInside of an file that has been added to the HTMLHeadQueue you output the code that you want in the HTML Head.

<cfoutput><script src="#$.siteConfig(‘themeAssetPath’)#/display_objects/gallery/js/gallery.js" type="text/javascript"></script><cfoutput>

Local EventHandler.cfcEvery site in your Mura instance also has a local eventHandler.cfc located {siteID}/includes/eventHandler.cfc.  It is provided to allow developers to define listening methods on a per-site basis.  You can either directly define your method or instantiate and register entire custom handlers.

A simple listening method:

<cffunction name="onRenderStart" output="false"><cfargument name="$"><!--- do custom stuff ---></cffunction<cffunction name="onApplicationLoad" output="false><cfargument name="$"><cfset $.getBean("userManager").injectMethod("customMethod", customMethod)></cffunction>

27

Page 28: Programmers Guide MuraCMS

Theme ContentRenderer.cfcNew as of Mura core versin 5.2.2439 you are now able to provide a theme specific contentRenderer.cfc. Simply place the custom component into the root of your theme and all of itʼs methods will be available view the Mura Scope as:

<cfoutput>#$.dspThemeRendererMethod()#</cfoutput>

You can also directly reference the theme contentRenderer.cfc with:

<cfset themeRenderer=$.getThemeRenderer()

Method InjectionThe base mura.cfobject now has injectMethod and deleteMethod functions that allow you to add or replace methods to existing Mura objects. This would most often be used during the onApplicationLoad or onRenderStart events. In most cases this eliminates the need to create a /config/coldspring.custom.xml.cfm.

<cffunction name="customMethod" output="false"><!-- Do custom stuff ---></cffunction><cffunction name="onRenderStart" output="false><cfargument name="$"><cfset $.getContentRenderer().injectMethod("customMethod", customMethod)></cffunction>

Resource BundlesMura has a built in way to chain resource bundle factories so that you can easily deploy your own.

Built-In FactoriesGaining access to your siteʼs default resource bundle factory instance:

<cfset rbFactory=$.siteConfig(‘RBFactory’)>

By default a siteʼs resource bundles are located at {siteid}/includes/resourceBundles/. They follow a naming convention that matched the languages language and region values.

Syntax:

{language}_{region}.properties

Example:

en_us.properties

If Mura cannot find a direct language and region match it will look for just a language match.

Example:

{language}.properties

Example:

en.properties

28

Page 29: Programmers Guide MuraCMS

If you have custom keys that you would like to deploy as part of a theme you can deploy them in a resource bundle file within your siteʼs theme directory ({siteid}/includes/themes/{theme}/resourceBundles). You only need to add keys that are unique to your theme. All other key values will come from the default resource bundles ({siteid}/includes/resourceBundles).

For example, if your theme required a key named myCustomString and your site was using the default English resource bundle you would create a file name en.properties in your siteʼs theme resourceBundles directory with the following contents.

myCustomString=This is my custom resource bundle key.

You could then reload Mura and pull that custom key out of the siteʼs default resource bundle and have access to that key.

<cfset rbFactory=$.siteConfig(‘RBFactory’)>

<cfoutput>#rbFactory.key(‘myCustomString’)#<br/>#rbFactory.key(‘comments.comments’)#</cfoutput>

Look up hierarchy:• /{siteID}/includes/themes/{theme}/resourceBundles/{targetResourceBundle}

• /{siteID}/includes//resourceBundles/{targetResourceBundle}

• /requirements/mura/resourceBundles/resources/{targetResourceBundle}

Custom FactoriesYou can also easily create custom resource bundle factories.

<cfset customFactory=createObject("component","mura.resourceBundle.resourceBundleFactory").init(ParentFactory=$.siteConfig(‘rbFactory’), ResourceBundle=‘Path/to/resoureBundleDirectory’ , Locale= $.siteConfig(‘JavaLocale’) )>

At this point the custom factory would have the following look up hierarchy.• {customResourceBundlerDirectory}/{targetResourceBundle}

• /{siteID}/includes/themes/{theme}/resourceBundles/{targetResourceBundle}

• /{siteID}/includes//resourceBundles/{targetResourceBundle}

• /requirements/mura/resourceBundles/resources/{targetResourceBundle}

29

Page 30: Programmers Guide MuraCMS

Mapping Events in MuraIn addition to defined methods in your sites local contentRenderer.cfc, you can directly add objects with the new pluginManager.addEventHandler() method.  This can be very useful within your local contentRenderer.cfc as well as within custom Mura plugins.

pluginManager.addEventHandler([component intance],'{siteID}')pluginManager.addEventHandler('[component path]','{siteID}',[persist])

Using addEventHandler() Outside of a Plugin

Site Local Event HandlerEvery site in Mura has its own eventHandler.cfc where you can define event handling methods without needing to bind it to an actual plugin. 

• You can find this file at: /{siteID}/includes/eventHandler.cfc

Since the local eventHandler.cfc is a single file and is instantiated on every request if you need to define a large number of events or if you need your handler to persist between requests you can run into problems.  The pluginManager.addEventHandler() method solves this issue.

Theme Local Event HandlerYou can also place an eventHandler.cfc into the root your theme directory. 

• You can find this file at: /{siteID}/includes/themes/{theme}/eventHandler.cfc

The major difference is that this and a site local eventHandler.cfc is that this component will only be intantiated and registered during Mura's onApplicationLoad event.  So any changes made to it will not take until the application has been reloaded.

Creating an Custom Event HandlerHere's an example of a simple exampleHandler.

• /{siteID}/includes/exampleHandler.cfc

<cfcomponent extends="mura.cfobject"><cfset variables.hits=0><!--- Mura uses on{name}, onGlobal{name} and standard{name} for binding events ---><cffunction name="onAddHit" output="true" returntype="any"><cfargument name="$"><cfset variables.hits=variables.hits+1><cfset $,event("hits","<strong>Number of Hits:" & hits & "</strong>")></cffunction></cfcomponent>

30

Page 31: Programmers Guide MuraCMS

Loading a Custom EventHandlerHere's how to use the local eventHandler onApplicationLoad event to instantiate the custom handler and register it with the pluginManager.

<cfcomponent extends="mura.cfobject"><cffunction name="onApplicationLoad" output="true" returntype="any"><cfargument name="$"><cfset var exampleHandler=createObject("component","exampleHandler")><!--- Notice that the second argument of "siteID" is hard coded.  This is because the onApplicationLoad event is not site bound<cfset $.getPluginManager().addEventHandler(exampleHandler,"default")></cffunction></cfcomponent>Announcing an Event

Since the example handler that we created doesn't have any official Mura events you can use the Mura tag in a test page to see it in action.

[mura]$.announceEvent('addHit',$)[/mura][mura]$.event('hits')[/mura]

Now reload your Mura instance and view the page and you should see the number of times that the onExample event has been fired since the application was last loaded.

Using addEventHandler() in a PluginYou can also map events with plugins with only slight variation to using the local eventHandler.cfc

Simple plugin CONFIG.XMLNotice that there's only one handler for onApplicationLoad defined in the XML.

<plugin><name>Hit Counter</name><package>HitCounter</package><loadPriority>5</loadPriority><version>0.1</version><provider>Blue River</provider><providerURL>http://blueriver.com</providerURL><category>Example</category><settings/><eventHandlers><eventHandler event="onApplicationLoad" component="cfcs.eventHandler" persist="true"/></eventHandlers><displayobjects location="global"/></plugin>

31

Page 32: Programmers Guide MuraCMS

Simple plugin eventHandler.cfcHere you can see that the onApplicationLoad() uses the variables.pluginConfig.addEventHandler() to register the entire component and all of its defined events for all of the plugin's assigned sites.

<cfcomponent extends="mura.plugin.pluginGenericEventHandler"><cfset variables.hits=0><cffunction name="onApplicationLoad">    <cfargument name="$">    <!--- Notice that there is no "siteID" argument.  This is because the pluginConfig is already aware of which sites it has been assigned to. --->   <cfset variables.pluginConfig.addEventHandler(this)></cffunction><cffunction name="onAddHit" output="true" returntype="any"><cfargument name="$"><cfset variables.hits=variables.hits+1><cfset $.event("hits","<strong>Number of Hits:" & variables.hits & "</strong>")></cffunction></cfcomponent>

The main thing here is that you can just add an event handler without explicitly mapping events that it's listening for.

32

Page 33: Programmers Guide MuraCMS

Custom Types and OutputClass Extension Manager: OverviewThe Class Extension Manager allows you to create subTypes which extend the base types of content (users, pages, portals, etc.) found in Mura. This allows you to create highly structured data by adding new fields/attributes to existing Mura CMS data types. Here are a few examples of situations when creating a new subType in Mura CMS would be useful:

• Products with specific attributes (cost, weight, version number, etc.)

• Site Members with specific attributes (gender, age, eye color, favorite ice cream flavor, etc.)

• Events with specific attributes (date, location, topic, time, etc.)

The Class Extension Manager is one of the more complex parts of Mura CMS, and it's important to understand how each aspect of the Class Extension Manager relates to the others. It's also important to mention that your naming convention needs to be strict - you must treat each named attribute as a variable, with no spaces and no special characters.

• Types: As mentioned above, types are the default data types in Mura CMS such as users, pages and portals. Each data type serves a unique purpose in Mura, both on a data and a rendering level.

• SubTypes: SubTypes allow you extend a base Mura type. You can have as many subTypes as you'd like, each defining the associated type further. For example, you could create a "Resume" subType. This would be a Mura page but with a "Resume" subType, with the attributes we've assigned to that subType. Remember, keep your naming convention strict (like a variable). It's important to note that if you build a subType labeled "Default" this will cause EVERY instance of that Type (user, page, etc.) to automatically have its associated attributes added to it. It's a way of giving you the ability to add global fields to use in Mura CMS.

• Attribute Sets are groups of attributes that associate to a given subType. Each group can be uniquely named to help define its purpose. For instance, you could have a "Resume" subType. Most resumes contain a section for listing out skills a user possesses. So we'll add an Attribute Set labeled "Skills" to store special attributes for skill-related information.

It's also important to note that when you create a new subType Mura automatically creates a data set for that subType labeled "Default". This is not a required data set in this case. It's simply a data set you can just start using.

For this example, we'll extend the base Page type by adding a new subType with new attributes. Our example will include the creation of a "Resume" subType. 

33

Page 34: Programmers Guide MuraCMS

Access your site's Class Extension Manager:• Log into your Mura Admin screens, and select the site you want to work with

• From the Site Settings menu in the upper right of the yellow bar in your Mura CMS admin screens, click Edit Current Site

• On the Site Settings page, click the link to Class Extension Manager (directly under the title)

Click Add Class Extension - this will give you the ability to create a new subType.

Select "Page" from the base type options - we're going to call our example subType "Resume". Click the "Add" button.

Now you'll see the Class Extension Attribute Sets page. You'll see a few options: 

• List All Class Extensions: This will take you back to the list of subTypes.

• Edit Class Extension: This will let you change the subType name. Here you will also be able to delete the subType itself.

• Add/Edit Attribute Set: This will let you add/edit the Attribute Set.

Select the Add Attribute Set link. Name your Attribute Set "Skills". If you have any categories or interests setup for your site you will have the option to associate them to this attribute set here if you so desire. You can also assign the extend set to render its attributes either in the extended attributes tab, the basic tab or with custom UI.

34

Page 35: Programmers Guide MuraCMS

Now you'll see your Skills Attribute Set details - but notice that it has no attributes yet. Select the "Add New Attributes" link.

Here you can add the details of your Attributes. Attributes are the new "fields" you create per Attribute Set. An example of a new Attribute would be a "Common skills" field (MutliSelect box) for the above mentioned "Skills" Attribute set. These attributes sets along with their associated attributes would appear in all Extended Attribute tabs that have the subType.

35

Page 36: Programmers Guide MuraCMS

Here are the fields explained:

• Name: This is the "variable" name for the attribute. This is another case where you need to keep your naming convention strict. It is also important to namespace the variable based on its subType and Attribute Set's name. This isn't shown in this example, but if you have many attributes in your site you could easily build two (or more) attributes with the same name - don't do this! Mura always renders the first one found based on the attribute name itself.

• Label: This is the friendly label that you would want everyone to see. If one is not supplied then the name field is used in its place.

36

Page 37: Programmers Guide MuraCMS

• Hint: Next to each field within the "Extended Attributes" tab for each type you have the option to provide a hint "pop-up" that will help the user understand what that field is meant for in the admin view of that subType.

• Input Type: This field dictates how the field will be rendered (Selectbox, MultiSelect, HTMLEditor [FCKEditor], etc.).

• Default Value: This field holds a default value if one is not selected by the user.

• Required: This field ensures that a user supplies an entry for the attribute. If an entry is not supplied then the "Validation Message" error pop-up message appears for them and halts the process from going any further.

• Validate: Date, numeric, email, regex.

• Regex: If Regex is chosen then its associated validation regex goes here.

• Validation Message: This field holds the error message you would like to have displayed if you have validation turned to "true".

• Option List: The is a "^" delimited list of options "values" that would be used if you choose a input type of selectBox, multiSelect or radioButton

• Option Label List: This is a "^" delimited list of labels for each option you would provide to the above option list. The positioning of the entry within the list correlates to the option list.

Note: The Mura tag can be used in the "Default Value", "Option List" and "Option Label List" attribute values.

You should now see your attribute within the Attribute Set. Here you will find a "Reorder" link that will give you the ability to reorder the attributes if you like - this is primarily for display purposes.

37

Page 38: Programmers Guide MuraCMS

Now that the new subType is fully setup we can now use it within a page. To do this, simply go to your site manager,

choose any page that you would like to have the subType assigned to and select it.Once the subType is selected it will automatically update the Extended Attributes tab with that subType's associated Attribute Sets/Attributes.

Displaying Extended AttributesThere are many ways you can display extended attributes. The most common way to display the value of an attribute is to use the content bean from the event upon rendering and ask for the attribute. This can be done by using the below code:

<cfoutput>#$.content( {attributeName} )#</cfoutput>

For the above example it would be:

<cfoutput>#$.content( "CommonSkills" )#</cfoutput>

38

Page 39: Programmers Guide MuraCMS

It's important to know that each Mura type displays differently from the other. In the above we focused on how a page type would be set up, but a user type will be a little different (showing extended attributes on the edit profile screen, etc).

Also, you can also use similar code within the body section of the page via Mura tags [ mura]{code}[/mura ] (remember to remove spaces around the Mura tag).

How to programmatically create Extended Attributes/SubTypesWhile it's easy to create Extended Attributes and SubTypes manually, creating them programmatically provides even more functionality and flexibility.

Throughout the system these are referred to as subTypes (example: "Page / SubType"). These enable you to create custom data structures within Mura CMS.

CREATE A NEW SUBTYPE<cfset subType = application.classExtensionManager.getSubTypeBean() /><cfset subType.setType( "Portal" ) /><cfset subType.setSubType( "newPortalType" ) /><cfset subType.setSiteID( "your siteID" ) />

Load the subType in case it already exists:

<cfset subType.load() />

<cfset subType.setBaseTable( "tcontent" ) /><cfset subType.setBaseKeyField( "contentHistID" ) />

Save the subType:

<cfset subType.save() />

HOW TO ASSIGN ATTRIBUTES TO YOUR "DEFAULT" SUBTYPE'S EXTEND SETGet the default extend set. this is automatically created for every subType:

<cfset extendSet = subType.getExtendSetByName( "Default" ) />

Create a new attribute for the "default" extend set:

Note that AutoApprove does not exist yet, but getAttributeBy Name will look for it and if not found give me a new bean to use.

<cfset attribute = extendSet.getAttributeByName( "AutoApprove" ) /><cfset attribute.setLabel( "Auto Approve Topics" ) /><cfset attribute.setType( "RadioGroup" ) /><cfset attribute.setOptionList( "Yes^No" ) /><cfset attribute.setOptionLabelList( "Yes^No" ) /><cfset attribute.setDefaultValue( "Yes" ) />

Save the attribute:

<cfset attribute.save() />

39

Page 40: Programmers Guide MuraCMS

Customizing Default OutputIt's possible to customize standard Mura output without editing the main display files. This allows developers to upgrade their Mura instances without worrying about having their customizations overwritten, as well as opens the door for more structured content.There are three ways to change a portal's rendering without touching the main code.

GLOBAL RESESTThe first way can be done for any display object file in your site's  /{siteID}/includes/display_objects/. If you want to customize the rendering for all portals you can do a global reset by copying:

• /{siteID}/includes/display_objects/dsp_portal.cfm

to

• /{siteID}/includes/display_objects/custom/dsp_portal.cfm

and changing it.

Or, if this is something that you would like include in to a theme you can also add your custom file there:

• /{siteID}/includes/themes/{theme}/display_objects/dsp_portal.cfm

Look up hierarchy:• /{siteID}/includes/themes/{theme}/display_objects/{targetFile}

• /{siteID}/includes/display_objects/custom/{targetFile}

• /{siteID}/includes/display_objects/{targetFile}

TARGETING A SUBTYPE WITH AN INCLUDEThis way you can create files that only target a specific type and subType. You can modify the output of any base type in Mura (page, gallery, portal, etc.)

In this example we're going to modify the output of the Portal type. First, extend the base "Portal" type in your site's class extension manager by creating a subType named "News" .

Next you need to copy

• /{siteID}/includes/display_objects/dsp_portal.cfm

to

• /{siteID}/includes/display_objects/custom/extensions/dsp_Portal_News.cfm

There you will be able to redefine the markup.  Notice the the naming convention for the file.

• dsp_{Type}_{SubType}.cfm

Or, if this is something that you would like include in to a theme you can also add your custom file there:

• /{siteID}/includes/themes/{theme}/display_objects/custom/extensions/dsp_{Type}_{SubType}.cfm

Look up hierarchy:• /{siteID}/includes/themes/{theme}/display_objects/custom/extensions/dsp_{Type}_{SubType}.cfm

• /{siteID}/includes/display_objects/custom/extensions/dsp_{Type}_{SubType}.cfm

40

Page 41: Programmers Guide MuraCMS

USING THE SITE LOCAL EVENTHANDLER.CFCThe way we tend do it is by creating the same class extensions as in step two but using the local eventHandler.cfc (/{siteID}/includes/eventHandler.cfc). There you can create a method that follows a specific naming convention that will trigger Mura to use it instead of the default code.

The naming conventions are:

• on[Type]BodyRender

• on[Type][SubType]BodyRender

The methods can directory output the content or return a string:

Direct Output<cffunction name="onPortalNewsBodyRender" output="true" returntype="void"><cfargument name="$"><cfoutput><!--- The setDynamicContent() method is what executes the [mura] tag --->#$.setDynamicContent($.content("body"))#<!---You can create structured output with custom attributes--->#$.content('customVar')#</cfoutput></cffunction>

Returning a String<cffunction name="onPortalNewsBodyRender" output="false" returntype="String"><cfargument name="$"><cfreturn $.setDynamicContent($.content("body")) /></cffunction>

The benefit of this approach is that you can later take this method and register it as a plugin if you want to redistribute it.

USING A THEME LOCAL EVENTHANDLER.CFCIn the exact same way that you are able to register events in your site local eventHandler ({siteid}/includes/eventHandler.cfc) you are also able to distribute an eventHandler.cfc within themes ({siteid/includes/themes/{theme}/eventHandler.cfc).  The main difference between using the site's local eventHandler.cfc and a theme local eventHandler.cfc is that a theme local eventHandler.cfc is only read in  during the onApplicationLoad event and the site local eventHandler.cfc is instantiated on a per request basis.

41

Page 42: Programmers Guide MuraCMS

Creating Plugins in Mura CMSPlugins are the primary way to create custom extensions for Mura.Key components of a plugin are:

• /plugin/config.xml{|.cfm}

• /plugin/plugin.cfc

• /plugin/license.txt

• /plugin/config.cfm

• index.cfm

• PluginConfig

Config.xmlThe config.xml is a configuration file that tells Mura what it needs to know about the custom extension.  It can be either /plugin/config.xml or /plugin/config.xml.cfm.

<plugin>

The name element :

<name>Hello World Plugin</name>

The package value becomes a part of the directory name where the plugin is installed. It becomes available as a CF mapping as well a value that can be use to reference the plugin's config object from the base mura.cfobject.getPlugin("[package|pluginID|moduleID]") .

<package>HelloWorldPlugin</package>

With the directoryFormat attribute you can tell Mura to use only the package attribute value for the plugin install directory instead of adding a _{pluginID} to the end of it:

<directoryFormat>packageOnly</directoryFormat>

The version attribute is simple meta data for the developer to store the plugins specific version:

<version>1.1</version>

The loadPriority determines the order that the plugins will file the onApplicationLoad event. This allows plugins to use other plugins as services.

<loadPriority>8</loadPriority>

The provider attribute is simple meta data informing end users what person or organization provided the plugin.

<provider>Blue River</provider>

The providerURL is simple meta data to provide end users with a link to your plugin's resource website.

<providerURL>http://blueriver.com</providerURL>

The category attribute is simple meta data to inform end users about the render purpose of the plugin.

<category>Application</category>

42

Page 43: Programmers Guide MuraCMS

The settings element contains the individual settings that the plugin requires in or to function. They are basically the same as the attributes in the Mura Class Extension Manager.

<settings><setting>

The Name attribute is what you will pull the settings value from the pluginConfig.getSettings('[setting name]').

<name>YourName</name>

The Label element acts a reader-friendly alias for the settings element

<label>Enter Your Name</label>

The contents of the Hint element show up as a tool tip for the user installing the plugin.

<hint>Your will be displayed on the 'Hello World' display object.</hint>

The Type element determines the type of input that will be used for this setting.

<type>text|radioGroup|textArea|select|multiSelectBox</type>

The Required element tells Mura whether this setting is required.

<required>true</required>    

The Validation element sets the type of validation to use.

<validation>email|date|numeric|regex</validation>

The Regex element is used when the Validation element is set to regex.

<regex></regex>

The Message element is used to prompt the user when the value submitted does not pass validation.

<message></message>

The Default values element acts as the settings' default values when the user is first installing the plugin.

<defaultvalue></defaultvalue>

These next two elements are for select boxes and radio groups.  They should contain a karat-delimited (^) list .

<optionlist></optionlist><optionlabellist></optionlabellist></setting></settings>

EventHandlers enable developers to explicitly map either a script file or a component method to Mura events.  The event that are mapped can be either core Mura events or custom events that are announced within your plugin with:

<cfset $.announceEvent("myCustomEvent")>

The component pathing starts from the plugin root. So your eventHandler is located at {pluginRoot}/eventHandlers/myHandler.cfc  it's component element value would be "eventHandlers.myHandler".

The "persist" attribute for component based eventHandlers determine whether they are cached or instantiated on a per-request basis.

<eventHandlers><eventHandler event="onApplicationLoad" component="eventHandlers.myHandler" persist="false"/></eventHandlers>

43

Page 44: Programmers Guide MuraCMS

Display Objects allow developers to provide widgets that end users can apply to a content node's display regions when editing a page (using the Content Objects tab).  Mappings to both the displayObject component and displayObjectFile are done the same way as when using eventHandlers.

The "persist" attribute for component based displayObjects determine whether they are cached or instantiated on a per-request basis.

<displayobjects location="global">    <displayobject name="Say Hello (cfc)" displaymethod="sayHello" component="displayObjects.sayHello" persist="false"/>    <displayobject name="Say Hello (cfm)" displaymethod="sayHello" displayobjectfile="displayObjects/sayHello.cfm"/></displayobjects></plugin>

Plugin.cfcThe /plugin/plugin.cfc allows developers to run code that is need to properly install, update or delete their plugin.  The plugin's config is automatically available as variables.pluginConfig. 

<cfcomponent extends="mura.plugin.plugincfc" output="false"><cffunction name="install"><!--- Do custom installation stuff---><cfset super.install()></cffunction><cffunction name="update"><!--- Do custom update stuff---><cfset super.update()></cffunction><cffunction name="delete"><!--- Do custom installation stuff---><cfset super.delete()></cffunction></cfcomponent>

license.txtIf you place a file named license.txt in your /plugin directory it will be presented to the end user while both updating and initially installing the plugin. The end user will be required to accept the license conditions in order to proceed.

44

Page 45: Programmers Guide MuraCMS

config.cfmThe config.cfm file is not required - it is a helper file that you can include into your plugin's root index.cfm (and other files that are directly access via the Mura admin).  It provides the request with the plugin's pluginConfig. The main concept is that the request.pluginConfig is already available when the plugin code is executing as part of a front end request. It is only when the plugin is being called as a top level-application that it needs to know how to obtain its own configuration information.

<cfsilent><cfif not isDefined("pluginConfig")><cfset package=listFirst(listGetat(getDirectoryFromPath(getCurrentTemplatePath()),listLen(getDirectoryFromPath(getCurrentTemplatePath()),application.configBean.getFileDelim())-1,application.configBean.getFileDelim()),"_")><cfset pluginConfig=application.pluginManager.getConfig(package)><cfset hasPluginConfig=false><cfelse><cfset hasPluginConfig=true></cfif><cfif not hasPluginConfig and not isUserInRole('S2')>    <cfif not structKeyExists(session,"siteID") or not application.permUtility.getModulePerm(pluginConfig.getValue('moduleID'),session.siteID)>        <cflocation url="#application.configBean.getContext()#/admin/" addtoken="false">    </cfif></cfif><cfif not isDefined("$")> <cfset $=application.serviceFactory.getBean("muraScope").init(session.siteid)>

</cfif>

</cfsilent>

index.cfmThe root index.cfm for a plugin is where developers can either display documentation or create custom admin functionalilty.

A simple example:

<cfinclude template="plugin/config.cfm" /><cfsavecontent variable="body"><cfoutput><h2>#pluginConfig.getName()#</h2><p>Description of you plugin goes here.</p></cfoutput></cfsavecontent><cfoutput>#application.pluginManager.renderAdminTemplate(body=body,pageTitle=pluginConfig.getName())#</cfoutput>

45

Page 46: Programmers Guide MuraCMS

A key method for keeping the Mura admin look and feel is the application.pluginManager.renderAdminTemplate() method.  It wraps the provided body string argument with the main admin layout allowing for seamless user experience.

#application.pluginManager.renderAdminTemplate(body={body},  pageTitle={htmlPageTitle},  jsLib= {prototype|jquery},  jsLibLoaded={true|false}  )#

PluginConfigThe variables.pluginConfig object is available in all plugin components that extend the base mura.plugin.pluginGenericEventHandler.cfc.  You can access a plugin's pluginConfig object through the base mura.cfobject that all Mura object extend through getPlugin({plugin|moduleID|package}).

The pluginConfig provides developers with access to the properties configured in the /plugin/config.xml.

<cfset setting=pluginConfig.getSetting({setting name})>

As well as plugin-specific Application and Session objects.

Application ObjectThe pluginConfig object has it own application object that provides a container for key value pairs that you would like to persist for the plugin.  It has an optional purge boolean argument to tell the pluginConfig to return a brand new object which defaults to false.

<cfset pApp=variables.pluginConfig.getApplication()><cfset pApp.setValue("myService",this)><cfset pVar=pApp.getValue("myService").doMethod()>

Session ObjectThe pluginConfig object has it own session object that provides a container for key value pairs that you would like to persist for users interacting with the plugin. It has an optional boolean purge argument to tell the pluginConfig to return a brand new object which defaults to false.

<cfset pSession=variables.pluginConfig.getSession()><cfset pSession.setValue("myService",this)><cfset pSession=pSession.getValue("myService").doMethod()>

Session and Application Object AutowiringMura's Session and Application object offer simple dependency auto-wiring.  When pulling an value from the object Mura looks for defined methods matching the set{key} format and then simply looks to see if {key} also exists in the object. If it does, Mura fires {object}.set{key}({key}).  Mura will also look into the main Mura service factory for key values that are not found locally.

EXAMPLE SERVICE 1<cfcomponent extends="mura.cfobject" output="false">

<cfset variables.service2=""><cffunction name="setService2" output="false"><cfargument name="service2">

46

Page 47: Programmers Guide MuraCMS

<cfset variables.service2=arguments.service2></cffunction><cffunction name="getService2" output="false"><cfreturn variables.setService2></cffunction></cfcomponent>

EXAMPLE SERVICE 2<cfcomponent extends="mura.cfobject" output="false"><cfset variables.service1=""><cfset variables.userManager=""><cfset variables.pluginConfig=""><cffunction name="setService1" output="false"><cfargument name="service1"><cfset variables.service2=arguments.service2></cffunction><cffunction name="getService1" output="false"><cfreturn variables.setService1></cffunction><cffunction name="setUserManager" output="false"><cfargument name="userManager"><cfset variables.userManager=arguments.userManager></cffunction>

<cffunction name="getUserManager" output="false"><cfreturn variables.userManager></cffunction>

<cffunction name="setPluginConfig" output="false"><cfargument name="pluginConfig"><cfset variables.pluginConfig=arguments.pluginConfig></cffunction><cffunction name="getPluginConfig" output="false"><cfreturn variables.pluginConfig></cffunction></cfcomponent>Putting it Together<cfcomponent extend="mura.plugin.pluginGenericEventHandler"><cffunction name="onApplicationLoad" output="false"><cfargument name="$"><cfset var service1=createObject("component","service1").init() ><cfset var service2=createObject("component","service2").init() ><cfset variables.pApp=variables.pluginConfig.getApplication()><cfset variables.pApp.setValue("service1",service1)><cfset variables.pApp.setValue("service2",service2)></cffunction><cffunction name="onOtherEvent" output="false"><cfargument name="$"><cfset var service1=variables.pApp.getValue("service1")><cfset var service2=service1.getService2() ><!--- The userManager was never explicitly set into the plugin application object. It was pulled form the main Mura service factory ---><cfset var userManager=service2.getUserManager()

47

Page 48: Programmers Guide MuraCMS

<!--- The pluginConfig also never explicitly set.  But since the plugin application object know what plugin is belongs to it can automatically set it as well ---><cfset var pluginConfig=service2.getPluginConfig()</cffunction></cfcomponent>

48

Page 49: Programmers Guide MuraCMS

Adding to the HTMLHeadQueueThe pluginConfig object also allow developers to add the current requests HTMLHeadQueue:

<cfset variables.pluginConfig.addToHTMLHeadQueue({path}) >

The "path" argument is a file path starting from the root of your plugin.

• {plugin root}/htmlhead/inc.cfm

The contents of "inc.cfm":

<cfoutput><link href="#pluginPath#css/contributor_tools.css" rel="stylesheet" type="text/css" /></cfoutput>

Add inc.cfm to the HTMLHeadQueue:

<cfset variables.pluginConfig.addToHTMLHeadQueue("htmlhead/inc.cfm") >

One import note is that if the code that you are injecting into the head of the page requires the JS library to be loaded (ie. jquery) you need to explicitly do so.

<cfset $.loadJSLib()><cfset variables.pluginConfig.addToHTMLHeadQueue("htmlhead/inc.cfm") >

EVENTHANDLER AUTO-WIRINGAn import method available is the pluginConfig.addEventHandler({component}). It allow developers to autowire an entire component's methods by naming convention.

Registered Patterns:• standard{event}

• on{event}<cfcomponent extends="mura.plugin.pluginGenericEventHandler"><cffunction name="onApplicationLoad" output="false><cfargument name="$"><cfset variables.pluginConfig.addEventHandler(this)></cffunction><cffunction name="onCustomEvent" output="false"><cfargument name="$"><!--- do custom stuff ---></cffunction></cfcomponent>

Retrieving a Plugin's Assigned SitesYou can retrieve a query of a plugin's assigned sites by using the pluginConfig.getAssignedSites() method.

<cfset rsSites=getPlugin("myPlugin").getAssignedSites()><cfloop query="rsSites"><cfoutput>#rsSites.siteid#<br/></cfoutput></cfloop>

49

Page 50: Programmers Guide MuraCMS

Retrieving a Plugin's pluginConfigIf you need to retrieve a pluginʼs pluginConfig object from outside of that plugins normal phase of execution you can access it by using the base mura.cfobject method getPlugin(). This methods takes a single argument of either pluginID, moduleID, Name or Package.

$.getPlugin( {pluginID} );$.getPlugin( {moduleID} );$.getPlugin( {name} );$.getPlugin( {package} );

Retrieving a pluginConfig by the value of the pluginʼs “Package” element in itʼs /plugin/config.xml allows developers to have a consistent way of referencing the plugin from Mura instance to Mura instance.

Keeping Your Site Auto-Update SafeWhen updating your site's files with the built in Mura auto-updater it is important to know what gets updated in order to avoid having your custom code overwritten.  The following files and directories are update safe.  You can add custom files to other directories but do not edit existing files (as they may be overwritten during an auto-update).

• {siteID}/includes/contentRenderer.cfc

• {siteID}/includes/eventHandler.cfc

• {siteID}/includes/servlet.cfc

• {siteID}/includes/loginHandler.cfc

• {siteID}/includes/themes

• {siteID}/includes/email

• {siteID}/includes/display_objects/custom

• {siteID}/includes/plugins

50

Page 51: Programmers Guide MuraCMS

Custom CachingMura has a built in caching mechanism that (when enabled) will store content information in memory for reuse throughout a given site. The purpose for this is to improve overall site performance by decreasing the amount of reads to the database.It's important to note that caching is done in a "lazy load" fashion. This means that the item will not be cached until it is called upon for the first time. It is also important to note that caching uses "soft references". This allows for improved memory management for the JVM. To ensure that there are no issues with soft references, whenever a page is updated the cache gets flushed entirely starting the caching process over again.

There are a few ways to use caching within Mura:

The first way is to use the built in cf_cacheomatic tag. For those who are not familiar with the tag, it give the user the ability to cache any desired display content.

The second way is more or less baked into Mura itself but is worth mentioning here. Whenever a page is requested a content bean is created. If caching is turned on then the contents of the content bean are automatically cached for reuse.

How to use itWithin Mura's admin console you can go to any given site's Site Settings "Basic" tab and turn the "Site Caching" option to "on". 

Additional Cache SettingsCache Capacity: You can set a maximum size for your site cache.  When this is set the cache becomes a LRU cache (Least Recently Used).  When the cache reaches its capacity it will purge the least accessed key when any new keys are added.  A Cache Capacity of 0 means unlimited.

Cache Free Memory Threeshold: The Cache Free Memory Threshold sets a minimum required percent of available jvm memory in order to cache new key.  This must have a value.  If 0% is entered it will default to 40%.

51

Page 52: Programmers Guide MuraCMS

The cf_cacheomatic tag:<cf_CacheOMatic key="{a unique key}" nocache="{1=don't cache, 0=cache}">    Content here ....</cf_CacheOMatic>

Example Usage

<cf_CacheOMatic key="page#$.content('contentID')#" nocache="#$.event('noCache')#">    <p>        <cfoutput>        The title of this page is #$.content('menuTitle')#.        </cfoutput>    </p></cf_CacheOMatic>

52