Вы находитесь на странице: 1из 52

Programmers Training

Mura CMS Version 5.2


Version: 1.3 Published: June 28, 2010

FILE STRUCTURE! Site Directories ! The Mura Tag! The Mura Event Model!

4 4 6 7

Core Directories and Files !......................................................................................................................4

Basic Structure!.......................................................................................................................................4

Embedding Java, CFML or cfscript! .........................................................................................................6

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 Cong Scope! .................................................................................................................................14 Global Cong 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 !


2

30

Using addEventHandler() Outside of a Plugin!......................................................................................30

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

Custom Types and Output! Creating Plugins in Mura CMS !

33 41

Class Extension Manager: Overview! ....................................................................................................33

Cong.xml! .............................................................................................................................................41 Plugin.cfc!..............................................................................................................................................44 license.txt !.............................................................................................................................................44 cong.cfm!.............................................................................................................................................45 index.cfm!..............................................................................................................................................45 PluginCong !.........................................................................................................................................46

Custom Caching!

51

How to use it! .........................................................................................................................................51 Additional Cache Settings !....................................................................................................................51 The cf_cacheomatic tag:!......................................................................................................................51

FILE STRUCTURE
Core Directories and Files
When you rst install Mura CMS, you'll see a standard set of les and folders:" "
/admin - This contains the main Mura content administration code. /cong - This contains the conguration les - including the settings.ini.cfm where Mura stores it#s core settings. Attribute values can contain dynamic expression denoted inside ${expression}. /cong/mappings.cfm -Mura will create this le on the rst request after installation. This le provides a place to add and edit CF mappings that Mura and any sub-applications use /cong/cfapplication.cfm -Similar to the mappings.cfm, this le provides an update safe place to add and edit CF Application.cfc variables. /cong/appcfc - This directory contains individual Application.cfc elements, which can be used to assemble plugin Application.cfcs. /cong/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-specic les 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. /wysiwyg"- This directory contains the main WYSIWYG HTML content editor.

Site Directories
Basic 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 congured to run their display objects locally are deployed (In addition to" /plugins). /{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 les that have been packaged with this theme. /{siteID}/includes/themes/{theme}/resourceBundles This directory contains resource bundle les that have been packaged with this theme. You only need to add keys that are unique to this theme.

The Mura Tag


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

Embedding Java, CFML or cfscript


To embed Java, ColdFusion or cfscript into a page, create a separate le 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:
dspInclude Displays the given cfm le 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

The Mura Event Model


The Mura application ow is a chain of events that re 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:

Life-Cycle of a Front End Request


This is the general life-cycle of events that get red when a Front-End Request is made (page render, etc.). Note that Events that begin with "onGlobal" are dened 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 nd 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. onGlobalRequestStart onSiteRequestInit onSiteRequestStart standardSetContentRenderer standardSetContentHandler ! standardSetPreviewHandler """ ! standardSetAdTrackingHandler ! standard404Validator ""! standard404Handler ! ! onSite404 standardWrongDomainValidator standardWrongDomainHandler standardTrackSessionValidator ! standardTrackSessionHandler standardSetPermissionHandler standardSetIsOnDisplayHandler standardDoActionsHandler standardRequireLoginValidator ! standardRequireLoginHandler standardSetLocaleHandler standardDoResponseHandler ! onRenderStart "" """ "! ! standardLinkTranslator "" """ "! ! standardFileTranslator "" """ """" !! onBeforeFileRender "" """" "" "!! onAfterFileRender "" """ "! ! standardTranslationHandler "" """ """ "!! standardHTMLTranslator "" """ """" "" "! ! ! onSiteEditProleRender "" """ """ ""! ! ! onSiteSearchRender "" """ """ """" ! ! ! onSiteLoginPromptRender "" """ """ """" ! ! ! onContentOffLineRender "" """ """ """" ! ! ! onContentDenialRender "" """ """ """" ! ! ! on{type}{subType}BodyRender

"" """ """ """" ! "" """ """ """" !

! !

! !

on{type}BodyRender onBeforeFormSubmitRedirect

"" """ """ """" ! ! ! onFormSubmitPollRender ! ! ! ! onFormSubmitResponseRender "" """ """ """" ! ! ! onBeforeFormSubmitSave ! ! ! ! onAfterFormSubmitSave ! ! ! standardForceSSLValidator ! ! ! standardForceSSLHandler "" "! onRenderEndonSiteRequestEnd onGlobalRequestEnd

Contextual Events
Contextual events are red only in response to other events - they are only red 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 Events
onApplicationLoad ! ! onGlobalSessionStart! ! onSiteMissingTemplate! ! onGlobalError! ! ! onAfterAutoUpdate! ! ! ! ! ! ! onSiteSessionStart onSiteSessionEnd onSiteError onBeforeAutoUpdate onGlobalThreatDetect

Admin Rendering Events


onDashboardPrimaryTop onDashboardPrimaryBottom onDashboardSidebarTop onDashboardSidebarBottom onContentEdit onGroupEdit onUserEdit onFEToolbarAdd (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 Events


onSiteDeploy! ! onAfterSiteDeploy ! ! ! onBeforeSiteDeploy onAfterSiteDeployRender"" " ""

Login Events
onSiteLogin! ! onSiteLoginSuccess ! onSiteLoginBlocked ! ! ! ! onGlobalLogin onGlobalLoginSuccess onGlobalLoginBlocked

Content Events
Node-Level events only re for node-level content types (ie. Page, Portal, Gallery, File, Calendar, Gallery) onBeforeContentSave (Node Level Only) onBefore{type}Save onBefore{type}{subType}Save onAfter{type}Save onAfter{type}{subType}Save onAfterContentSave (Node Level Only) onBeforeContentDelete (Node Level Only) onBefore{type}delete onBefore{type}{subType}delete onAfterContentDelete (Node Level Only) onAfter{type}delete onAfter{type}{subType}delete onBeforeContentDeleteVersionHistory (Node Level Only) onBefore{type}DeleteVersionHistory onBefore{type}{subType}DeleteVersionHistory onAfterDeleteVersionHistory (Node Level Only) onAfter{type}DeleteVersionHistory onAfter{type}{subType}DeleteVersionHistory onBeforeContentDeleteVersion (Node Level Only) onBefore{type}ContentDeleteVersion onBefore{type}{subType}ContentDeleteVersion onAfterContentDeleteVersion (Node Level Only) onAfter{type}ContentDeleteVersion (Node Level Only) onAfter{type}{subType}ContentDeleteVersion

Content Comment Events


onBeforeCommentUpdate ! onBeforeCommentSave!! onAfterCommentUpdate! onAfterCommentSave ! ! ! ! ! ! onBeforeCommentCreate onBeforeCommentDelete onAfterCommentCreate onAfterCommentDelete

10

Category Events
onBeforeCategoryUpdate ! onBeforeCategorySave! ! onAfterCategoryUpdate !! onAfterCategorySave ! ! ! ! ! ! onBeforeCategoryCreate onBeforeCategoryDelete onAfterCategoryCreate onAfterCategoryDelete

Feed Events
onBeforeFeedUpdate ! ! onBeforeFeedSave ! ! onAfterFeedUpdate ! onAfterFeedSave! ! ! ! ! ! ! onBeforeFeedCreate onBeforeFeedDelete onAfterFeedCreate onAfterFeedDelete

User Events
onBeforeUserUpdate! ! ! onBeforeUserSave ! ! ! onBeforeUser{subType}Update ! ! onBeforeUser{subType}Save! ! onAfterUserUpdate ! ! onAfterUserSave ! ! onAfterUser{subType}Update ! onAfterUser{subType}Save ! ! ! ! ! onBeforeUserCreate onBeforeUserDelete onBeforeUser{subType}Create onBeforeUser{subType}Delete onAfterUserCreate onAfterUserDelete onAfterUser{subType}Create onAfterUser{subType}Delete

11

The Mura Scope


The 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 <cfset <cfset <cfset event.setValue({siteID})> $.init(event)> myStruct.siteID={siteid}> $.init(myStruct)>

Content Scope
The 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

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 Scope
The 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 Scope
The 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

A simple example of using the Component Scope in a template: <cfoutput> <div>#$.setDynamicContent($.component('body'))#</div> </cfoutput>

Current User Scope


The Current User scope wraps the new Session User Facade object, which in turn wraps the session.mura struct. The main benet 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 Cong Scope


Site Cong scope is specic 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 Cong Scope


The Global Cong scope wraps the global conguration for your Mura instance, which is congured in your Mura intance's /cong/settings.ini.cfm.
Returns value: $.globalConfig({property});

14

Sets and returns the value: $.globalConfig({property},{property value}); Returns full congBean: $.globalGlobal(); Any custom attributes set in the Mura instances /cong/settings.ini.cfm are available: $.siteConfig(customVar);

Helper Methods and Template Rendering Methods


Along with these scopes, Mura has additional helper methods and template rendering methods.

Helper Methods
For 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

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

16

Understanding Base Mura Objects


Mura Beans
Beans 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, leName, 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 dened 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 rst 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

Get a common eld: 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 gure 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 Bean
Load 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 lename (index.cfm/lename/): content = $.getBean( "content" ).loadBy( filename={filename} [, siteID={siteID}] );

18

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({lePath/URL});

Content Comment Bean


Load 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 Bean
Load by userID: user = $.getBean( "user" ).loadBy( userID={userID} ); Load by username:

19

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 Bean
Load 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

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

Site Bean
The siteBean can be accessed from the Mura Scope as well as directly from the settingsManager." It contains key information methods to reference a specic site conguration. 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 le referencing: site.getAssetPath(); site.getThemeAssetPath(); These methods return the starting le path to be used for <cnclude>: 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();

21

Mura Iterators
Mura 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 rst 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 rst 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 Methods
Iterator.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

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 length as 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 Query


The 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

Mura Feeds
Feeds allow you to create custom Mura queries.

Content Feed
Load 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})> <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

Local ContentRenderer.cfc
Every 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-specic 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 specic eventHandler method as: <cfoutput>#$.dspDateTime()#</cfoutput> Or in the body of a content node as: [mura]$.dspDateTime()[/mura]

Available Renderer Settings


In addition to being able to redene 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 le 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

<!---<cfset this.imageInList="jpg,jpeg,png,gif">---> This tells Mura whether to serve images indirectly through leManager.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 <!---<cfset <!---<cfset <!---<cfset <!---<cfset this.headline="h2"/>---> this.subHead1="h3"/>---> this.subHead2="h4"/>---> this.subHead3="h5"/>---> this.subHead4="h6"/>--->

ShowMeta for Link and File Nodes


The 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

HTMLHeadQueue
Mura provides an HTMLHeadQueue where you can add les 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 le 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 le 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 HTMLHEADQUEUE


Inside of an le 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.cfc
Every site in your Mura instance also has a local eventHandler.cfc located {siteID}/includes/eventHandler.cfc." It is provided to allow developers to dene listening methods on a per-site basis." You can either directly dene 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

Theme ContentRenderer.cfc
New as of Mura core versin 5.2.2439 you are now able to provide a theme specic 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 Injection
The 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 /cong/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 Bundles Mura has a built in way to chain resource bundle factories so that you can easily deploy your own. Built-In Factories
Gaining 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 nd a direct language and region match it will look for just a language match. Example: {language}.properties Example: en.properties

28

If you have custom keys that you would like to deploy as part of a theme you can deploy them in a resource bundle le 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 le 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 Factories
You can also easily create custom resource bundle factories. <cfset customFactory=createObject("component","mura.resourceBundle.resourceBundleFactory").in it(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

Mapping Events in Mura


In addition to dened 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 Handler
Every site in Mura has its own eventHandler.cfc where you can dene event handling methods without needing to bind it to an actual plugin."

You can nd this le at: /{siteID}/includes/eventHandler.cfc


Since the local eventHandler.cfc is a single le and is instantiated on every request if you need to dene 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 Handler


You can also place an eventHandler.cfc into the root your theme directory."

You can nd this le 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 Handler


Here'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

Loading a Custom EventHandler


Here'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 ofcial 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 red since the application was last loaded.

Using addEventHandler() in a Plugin


You can also map events with plugins with only slight variation to using the local eventHandler.cfc

Simple plugin CONFIG.XML


Notice that there's only one handler for onApplicationLoad dened 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

Simple plugin eventHandler.cfc


Here you can see that the onApplicationLoad() uses the variables.pluginCong.addEventHandler() to register the entire component and all of its dened 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

Custom Types and Output


Class Extension Manager: Overview
The 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 elds/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 specic attributes (cost, weight, version number, etc.) Site Members with specic attributes (gender, age, eye color, favorite ice cream avor, etc.) Events with specic 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

dening 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 elds to use in Mura CMS. to help dene 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.

Attribute Sets are groups of attributes that associate to a given subType. Each group can be uniquely named

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

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

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 "elds" you create per Attribute Set. An example of a new Attribute would be a "Common skills" eld (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. Here are the elds 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 rst one found based on the attribute name itself. is used in its place.

Label: This is the friendly label that you would want everyone to see. If one is not supplied then the name eld
35

Hint: Next to each eld 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 eld is meant for in the admin view of that subType. etc.).

Input Type: This eld dictates how the eld will be rendered (Selectbox, MultiSelect, HTMLEditor [FCKEditor], Default Value: This eld holds a default value if one is not selected by the user. Required: This eld 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 eld 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 nd a "Reorder" link that will give you the ability to reorder the attributes if you like - this is primarily for display purposes.

36

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 Attributes


There 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>

37

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 prole 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/SubTypes


While it's easy to create Extended Attributes and SubTypes manually, creating them programmatically provides even more functionality and exibility. 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 <cfset <cfset <cfset subType = application.classExtensionManager.getSubTypeBean() /> subType.setType( "Portal" ) /> subType.setSubType( "newPortalType" ) /> 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 SET


Get 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 <cfset <cfset <cfset <cfset <cfset attribute = extendSet.getAttributeByName( "AutoApprove" ) /> attribute.setLabel( "Auto Approve Topics" ) /> attribute.setType( "RadioGroup" ) /> attribute.setOptionList( "Yes^No" ) /> attribute.setOptionLabelList( "Yes^No" ) /> attribute.setDefaultValue( "Yes" ) />

Save the attribute: <cfset attribute.save() />

Customizing Default Output


38

It's possible to customize standard Mura output without editing the main display les. 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 RESEST
The rst way can be done for any display object le 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 le 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 INCLUDE


This way you can create les that only target a specic 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 redene the markup." Notice the the naming convention for the le.

dsp_{Type}_{SubType}.cfm
Or, if this is something that you would like include in to a theme you can also add your custom le 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

39

USING THE SITE LOCAL EVENTHANDLER.CFC


The 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 specic 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 benet 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.CFC


In 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.

40

Creating Plugins in Mura CMS


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

/plugin/cong.xml{|.cfm} /plugin/plugin.cfc /plugin/license.txt /plugin/cong.cfm index.cfm PluginCong


Cong.xml
The cong.xml is a conguration le that tells Mura what it needs to know about the custom extension." It can be either /plugin/cong.xml or /plugin/cong.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 cong object from the base mura.cfobject.getPlugin("[package|pluginID|moduleID]") . <package>HelloWorldPlugin</package> The version attribute is simple meta data for the developer to store the plugins specic version: <version>1.1</version> The loadPriority determines the order that the plugins will le 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> 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 pluginCong.getSettings('[setting name]'). <name>YourName</name>

41

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 rst 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 le 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")>

42

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

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.cfc
The /plugin/plugin.cfc allows developers to run code that is need to properly install, update or delete their plugin." The plugin's cong is automatically available as variables.pluginCong." <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.txt
If you place a le 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

cong.cfm
The cong.cfm le is not required - it is a helper le that you can include into your plugin's root index.cfm (and other les that are directly access via the Mura admin)." It provides the request with the plugin's pluginCong. The main concept is that the request.pluginCong 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 conguration information. <cfsilent> <cfif not isDefined("pluginConfig")> <cfset pluginID=listLast(listGetat(getDirectoryFromPath(getCurrentTemplatePath()),listLen(get DirectoryFromPath(getCurrentTemplatePath()),application.configBean.getFileDelim())-1,a pplication.configBean.getFileDelim()),"_")> <cfset pluginConfig=application.pluginManager.getConfig(pluginID)> <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.cfm
The 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.getNam e())# </cfoutput>

45

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}! )#

PluginCong
The variables.pluginCong object is available in all plugin components that extend the base mura.plugin.pluginGenericEventHandler.cfc." You can access a plugin's pluginCong object through the base mura.cfobject that all Mura object extend through getPlugin({plugin|moduleID|package}). The pluginCong provides developers with access to the properties congured in the /plugin/cong.xml. <cfset setting=pluginConfig.getSetting({setting name})> As well as plugin-specic Application and Session objects.

Application Object
The pluginCong 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 pluginCong 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 Object
The pluginCong 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 pluginCong 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 Autowiring


Mura's Session and Application object offer simple dependency auto-wiring." When pulling an value from the object Mura looks for dened methods matching the set{key} format and then simply looks to see if {key} also exists in the object. If it does, Mura res {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

<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

<!--- 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

Adding to the HTMLHeadQueue


The pluginCong object also allow developers to add the current requests HTMLHeadQueue: <cfset variables.pluginConfig.addToHTMLHeadQueue({path}) > The "path" argument is a le 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-WIRING
An import method available is the pluginCong.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 Sites You can retrieve a query of a plugin's assigned sites by using the pluginCong.getAssignedSites() method. <cfset rsSites=getPlugin("myPlugin").getAssignedSites()> <cfloop query="rsSites"> <cfoutput>#rsSites.siteid#<br/></cfoutput> </cfloop>

49

Retrieving a Plugin's pluginCong If you need to retrieve a plugin#s pluginCong 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( $.getPlugin( $.getPlugin( $.getPlugin( {pluginID} ); {moduleID} ); {name} ); {package} );

Retrieving a pluginCong by the value of the plugin#s Package element in it#s /plugin/cong.xml allows developers to have a consistent way of referencing the plugin from Mura instance to Mura instance.

Keeping Your Site Auto-Update Safe


When updating your site's les 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 les and directories are update safe." You can add custom les to other directories but do not edit existing les (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

Custom Caching
Mura 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 rst 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 ushed entirely starting the caching process over again. There are a few ways to use caching within Mura: The rst 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 it
Within 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 Settings


Cache 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%.

The cf_cacheomatic tag:


<cf_CacheOMatic key="{a unique key}" nocache="{1=don't cache, 0=cache}"> !! !Content here ....

51

</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

Вам также может понравиться