Академический Документы
Профессиональный Документы
Культура Документы
FILE STRUCTURE! Site Directories ! The Mura Tag! The Mura Event Model!
4 4 6 7
Basic Structure!.......................................................................................................................................4
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
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
30
33 41
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.
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 most common objects used during a front end page request are:
! !
! !
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
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
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
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>
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
For example, value content, feed, user, category, userManager: $.getBean({bean type}); $.announceEvent({EventToAnnounce}); $.renderEvent({EventToRender}); $.getPlugin({package|pluginID|moduleID});
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
16
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
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});
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
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]
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"/>--->
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}
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}
29
/{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
31
32
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
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.
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).
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() />
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}
/{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
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.
40
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()>
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
{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.
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"."
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