Introduction
Mura content management has moved with the times, evolving from a traditional CMS to one that recognizes the fluid and ever-changing process of delivering content from a variety of sources to an equally diverse set of platforms. Flexibility and maintainability are key ingredients to addressing this requirement, and Mura has matured these tools to allow developers of not only traditional HTML + CSS websites but those building advanced JavaScript-rendered decoupled and/or headless websites via advanced frameworks.
In this section we will examine the Mura API, which consists of several components but chiefly the Mura JS JavaScript framework, which is itself a standalone Node project, as well as Mura ORM and the Mura JSON API. These three components enable a wide variety of options to the modern front-end developer.
Decoupled Websites
With the increasing shift for front-end developers to JavaScript-based frameworks like React, Angular and Vue JS, decoupled websites are becoming increasingly common. A decoupled website is one where the front-end of the website is being rendered and/or delivered independent of the underlying content management system. Often the CMS provides the content via a dedicated, secure API.
Mura is fully-capable and compatible with this type of website, with an underlying JSON API, as well as the Mura JS framework for delivering content in a variety of formats, from raw data-based JSON to server-rendered, hydrated and/or compartmentalized content blocks ready for consumption and display. To help developers move to this type of development, as well as provide insights into how experienced JavaScript developers might integrate into Mura, we have developed several starter or example projects that should facilitate this need. We have also created a basic, "vanilla" JavaScript-only decoupled example, which we will examine more fully below. Otherwise, feel free to visit the GitHub repository for the framework that best suits your needs.
Mura.js
Mura.js originally began as a lightweight utility to decouple Mura's dependency on jQuery, a popular JavaScript library. Since its inception, Mura.js has also grown into a JavaScript framework for interacting with Mura's JSON API, which allows Mura to be the main content creation hub within an organization, and allows developers to build their applications as pure JavaScript clients.
Familiar Syntax
If you've ever used jQuery, you'll find the syntax quite familiar.
Selecting Elements
The example below illustrates how to use Mura.js to select multiple DOM elements with the same class, and replace the content of each of the selected elements with custom HTML.
<script>
Mura(function(m) {
m('.target').each(function() {
m(this).html('Mura found you!');
});
});
</script>
Ajax Support
Mura.js includes baked-in Ajax (asynchronous JavaScript and XML) support. The example below illustrates how to perform a simple Ajax request.
<script>
Mura.ajax({
type: 'post',
url: 'https://domain.com/path/',
data: { key: value },
success: function(resp) {
console.log(resp);
},
error: function(resp) {
console.log(resp);
}
});
</script>
Promise Support
Mura.js also includes support for JavaScript promises. See the following examples for more details.
Mura.post() With Promises
<script>
Mura.post('https://domain.com/path/', { key: 'value' })
.then(function(result) {
// handle success
})
.catch(function(err) {
// handle error
};
</script>
Mura.get() With Promises
<script>
Mura.get('https://domain.com/path/')
.then(function(result) {
// success
// do something with the result
Mura('#target').html(result.data.html);
})
.catch(function(err) {
// handle error
console.log(err);
});
</script>
Chaining Promises
The following example illustrates how you could handle multiple Ajax requests with Mura.js using JavaScript Promises chaining, and avoid the "pyramid of doom". In other words, avoid nesting your Ajax requests.
<script>
var exampleFunc1 = function() {
return new Promise(function (resolve, reject) {
Mura.post('https://domain.com/path/', { apikey: 'value' })
.then(function(result) {
if ( result.hasOwnProperty('id') ) {
resolve(result);
} else {
reject('result does not contain an id');
}
})
.catch(function(err) {
reject(err);
});
});
};
var exampleFunc2 = function(id) {
return new Promise(function (resolve, reject) {
Mura.get('https://domain.com/path/?id=' + id)
.then(function(result) {
resolve(result);
})
.catch(function(err) {
reject(err);
});
});
};
exampleFunc1()
.then(function(exampleFunc1Result) {
return exampleFunc2(exampleFunc1Result.id);
})
.then(function(result) {
console.log(result);
})
.catch(function(err) {
console.log(err);
});
</script>
DOM Event Handlers
Mura.js allows for registering DOM event handlers, as shown in the example below.
<script>
Mura('#mybutton').on('click', function(e) {
e.preventDefault();
console.log(e);
});
</script>
You can also register multiple event handlers using a custom "addEventHandler
" method, as shown below.
<script>
Mura('.myclass').addEventHandler({
click: function(e) {
e.preventDefault();
console.log(e);
}
, touch: function(e) {
// do something
console.log(e);
}
});
</script>
Mura.DOMSelection Class
The DOM selection methods available via Mura.js is handled by the Mura.DOMSelection
class. This wraps your selected targets via the Mura()
method.
Mura.js allows you to handle selected DOM elements as a single object, or as a collection.
Single Object Example
Mura('#target')
.html('Hello world!');
Collection Example
Mura('.target')
.each(function() {
Mura(this)
.html('Hello world!');
});
Supported DOMSelection methods may be found at https://github.com/blueriver/MuraJS/blob/master/src/core/domselection.js.
Mura ORM With Mura.js
Mura.js enables JavaScript developers to interact with Mura ORM, exposing access to its ORM Feed API, and allows for common CRUD (Create, Read, Update, and Delete) functionality.
Mura.js CRUD Functionality
Outlined below are code examples for performing basic CRUD operations on Mura ORM objects/entities via Mura.js. For developers who are primarily used to working with server-side languages, it may take a little time to adjust to working on the client side, because we need to "wait" until the object/entity is loaded in order to work with it. For this reason, it may be helpful to review how to work with JavaScript Promises.
Loading/Reading Mura ORM Objects/Entities
This example simply illustrates how to load a Mura ORM entity, using Mura.js.
<script>
var personid = 'some-uuid';
Mura.getEntity('person')
.loadBy('personid', personid)
.then(function(person) {
console.log(person);
})
.catch(function(err) {
console.log(err.get('errors'));
});
</script>
Creating/Updating Mura ORM Objects/Entities
This example drives home how to segregate your Ajax calls using JS Promises, and avoid nesting or stacking your Mura.js methods.
<script>
var getPersonByID = function(personid) {
return new Promise(function(resolve, reject) {
Mura.getEntity('person').loadBy('personid', personid)
.then(function(person) {
resolve(person);
})
.catch(function(err) {
reject(err);
console.log(err.get('errors'));
});
});
};
var savePerson = function(person) {
return new Promise(function(resolve, reject) {
person.save()
.then(function(result) {
resolve(result);
})
.catch(function(err) {
reject(err);
});
});
}
getPersonByID('some-uuid')
.then(function(person) {
person.set('namelast', 'Withington');
return savePerson(person);
})
.then(function(result) {
console.log(result);
})
.catch(function(err) {
console.log(err);
});
</script>
Deleting Mura ORM Objects/Entities
This is another example of how to segregate your Ajax calls using JS Promises to avoid nesting your Mura.js methods.
<script>
var getPersonByID = function(personid) {
return new Promise(function(resolve, reject) {
Mura.getEntity('person').loadBy('personid', personid)
.then(function(person) {
resolve(person);
})
.catch(function(err) {
reject(err);
console.log(err.get('errors'));
});
});
};
var deletePerson = function(person) {
return new Promise(function(resolve, reject) {
person.delete()
.then(function(result) {
resolve(result);
})
.catch(function(err) {
reject(err);
});
});
}
getPersonByID('some-uuid')
.then(function(person) {
return deletePerson(person);
})
.then(function(result) {
console.log(result);
})
.catch(function(err) {
console.log(err);
});
</script>
Mura.js Feed API
The following example illustrates how to obtain a feed of Mura ORM objects/entities, and then loop over the returned recordset. As you'll see, it's quite similar to using Mura's ORM Feed syntax.
var person;
Mura
.getFeed('person')
.where() //optional
.prop('namelast')
.isEQ('Levine')
.orProp('namelast')
.beginsWith('Withing')
.getQuery()
.then(function(people) {
// handle success
people.each(function(person, idx) {
result = person.get('namefirst') + ' ' + person.get('namelast');
console.log(result);
});
})
.catch(function(err) {
// handle error
console.log(err);
});
This example demonstrates how to use the aggregate method for a Mura.js Feed.
The example assumes a custom ORM object named "widget
" exists, and has a property of "price
".
var widget;
Mura
.getFeed('widget')
.aggregate('count', '*')
.aggregate('sum', 'price')
.aggregate('min', 'price')
.aggregate('max', 'price')
.aggregate('avg', 'price')
.getQuery()
.then(function(widgets) {
// handle success
console.log(widgets.getAll());
})
.catch(function(err) {
// handle error
console.log(err);
});
See the "Key Methods" area of the Feed Bean section, for details on the Mura.js Feed API Methods.
Loading JavaScript and CSS Files With Mura.js
Mura.js allows JavaScript developers to load their JS and CSS files synchronously or asynchronously. This is especially useful for modules rendered via CFML.
Note: This is NOT recommend for modules rendered via JavaScript. See the Anatomy of a Module's Rendering Via JavaScript section for more information.
Mura.loader()
The Mura.loader()
method is for JavaScript and CSS parallel loading with dependencies management. Using this method also prevents duplicate JavaScript and/or CSS files from being loaded.
There are two primary methods associated with Mura.loader()
, loadjs()
and loadcss()
described below.
loadjs(url, cb)
Parameter |
Description |
url |
If the first parameter is an array , files will be loaded asynchronously. If it's a string , the files will be loaded synchronously. If the file is located under your theme, you may use m.themepath to dynamically generate the path to the theme location. For example: loadjs(m.themepath + '/script.js') |
cb |
A callback function to execute when all scripts have been loaded. |
loadcss(url, attrs, cb)
Parameter |
Description |
url |
The URL for the CSS file. If the file is located under your theme, you may use m.themepath to dynamically generate the path to the theme location. For example: loadcss(m.themepath + '/file.css') |
attrs |
You may optionally pass in an object to specify the media type attribute for the CSS file. For example: loadcss(m.themepath + '/file/css', {media: "print"})
Valid options include:
- all
- aural
- braille
- embossed
- handheld
- print
- projection
- screen
- tty
- tv
|
cb |
A callback function which executes immediately. |
Examples
This example illustrates the basic syntax for loading a JavaScript file, with a desired callback function.
<script>
Mura(function(m) {
m.loader()
.loadjs(m.themepath + '/js/mylibrary.js', function() {/* callback */});
});
</script>
The example below illustrates loading some scripts in parallel with another batch of scripts being loaded in order. In the example below, the first loadjs()
will be executed in parallel of the second loadjs()
. However, in the second loadjs()
, the myDependentLib.js
file won't be loaded until myRequiredLib.js
has finished loading. Then, when they've finished loading, the callback function will execute.
<script>
Mura(function(m) {
m.loader()
.loadjs(m.themepath + '/myLib.js')
.loadjs(
m.themepath + '/myRequiredLib.js',
m.themepath + '/myDependentLib.js',
function() {
// callback
});
});
</script>
The example below illustrates loading multiple CSS & JavaScript files, and includes a callback function that runs after the script files have all been loaded.
<script>
Mura(function(m) {
m.loader()
.loadcss(m.themepath + '/path/to/all.css', { media: 'all' })
.loadcss(m.themepath + '/path/to/print.css', { media: 'print' })
.loadjs(
m.themepath + '/path/to/script1.js',
m.themepath + '/path/to/script2.js',
function() {
// Now do something with the loaded JS
});
});
</script>
Modules With Mura.js
As introduced in the Anatomy of a Module section, developers may choose to render their modules using purely JavaScript. When doing so, the module's index.cfm
file should only contain the following line of code, which informs Mura it has nothing to render via server-side processing, and all rendering will occur via the "client" or browser.
<cfset objectparams.render="client">
Then, in order to load your script(s), you'll need to use a custom event handler, and register the onRenderStart
event to dynamically load your script file(s), as illustrated below.
// ../yourmodule/model/handlers/yourhandler.cfc
component extends='mura.cfobject' {
function onRenderStart(m) {
// if script should be included in the <head>
arguments.m.addToHTMLHeadQueue('<script src="/path/to/script.js"></script>');
// OR
// if script should be included before closing </body> tag
arguments.m.addToHTMLFootQueue('<script src="/path/to/script.js"></script>');
}
}
By using addToHTMLHeadQueue
and/or addToHTMLFootQueue
, your scripts will only be loaded once, regardless of how many times your module has been applied to the layout.
The Custom JavaScript File
Assuming you've followed the instructions above to get your custom script file included into either the "head
" portion of your theme, or before the closing "body
" tag, you will most likely want to control when your script(s) are executed. However, you may also simply allow your scripts to run at all times.
Running Scripts on Every Request
If you merely wish for your script(s) to execute, regardless of whether or not your module has been applied to the layout, then you may simply include any JavaScript you wish. In other words, since your script files are being added on every request via your custom event handler's onRenderStart
method, your scripts will automatically execute. This is useful for merely adding a utility script, such as a tracking script, etc.
The content of your script file may look as simple as the following example.
// your-script.js
console.log('Hello from your module!');
Running Scripts Only If Your Module Has Been Applied
Unless your scripts are utilities, such as tracking scripts, developers will most often wish their scripts to execute only when the module has been applied to the layout. In addition, when the module includes a configurator, each instance of the module will have access to its own configuration information.
First, it's important to understand that modules rendered via the client, are automatically registered to Mura's namespace under Mura.Module.YourModuleDirectoryName
. Also, Mura will automatically invoke a "renderClient
" method via Mura.UI
, on each request. By defining your own "renderClient" method, you're able to pretty much do whatever you want.
The example below illustrates a skeleton script. You should substitute "yourModuleDirectoryName
" with the actual directory name of your display object.
// your-script.js
Mura.Module.YourModuleDirectoryName = Mura.UI.extend({
renderClient: function() {
// Your code goes here ...
return this;
}
});
You can also defined a "renderServer" method if defined will be used in universal node js frameworks like NextJS and NuxtJS
// your-script.js
Mura.Module.YourModuleDirectoryName = Mura.UI.extend({
renderClient: function() {
// Your code goes here ...
return this;
},
renderServer: function() {
// Your code goes here ...
return this;
}
});
this.context
When using JavaScript to render a Mura "Module" via the client, developers are most interested in two (2) primary pieces of information:
- How to target the container element of where the module has been applied to the layout.
- How to access "
objectparams
", or the data collected via a configurator.
You can easily access either of these by using "this.context
" in your custom script.
this.context.targetEl
- Use this to target the container element of where the module has been applied to the layout. Keep in mind, each instance of the module in the layout will have its own, unique container element.
this.context.{yourObjectParam}
- Use this to access any "
objectparams
", or data collected via a configurator. Each instance of the module will have access to its own, unique configuration data.
The example below illustrates how to use the above information, and may be used as a starter file in your own projects.
// your-script.js
Mura.Module.YourModuleDirectoryName = Mura.UI.extend({
renderClient: function() {
// reference to the container element
this.container = Mura(this.context.targetEl);
// assuming you have an objectparam named 'mytext'
var txt = this.context.mytext || 'mytext is empty';
// Having fun by replacing the content of the container
// Remember, using Mura.js, we can do jQuery-esque DOM manipulation
this.container.html('<h3>Hello from yourDisplayObject</h3>');
this.container.append('<h4>' + txt + '</h4>');
// The rest of your code goes here ...
return this;
}
});
If you're interested in seeing what else is available via Mura.UI
, feel free to review the code found at https://github.com/blueriver/MuraJS/blob/master/src/core/ui.js.
To see an example of how to use Mura.js along with Mura ORM, Grunt, and Handlebars, visit https://github.com/stevewithington/muracontacts/tree/js.
Forms & Mura.js
Mura forms are loaded asynchronously. So, if you wish to run some scripts, you need to use a special method to "reopen" the form, and then add your scripts. This can be done by leveraging a special Mura.js method: Mura.DisplayObject.Form.reopen({});
.
Once the form has been "reopened," simply leverage one or more of the predefined events from the code example below to inject any custom logic.
NOTE: If not using deferred Mura.js then you do not need to reopen the display object classes within a Mura.preInit() method. The option to defer the loading of Mura.js is controlled by the this.deferMuraJS theme value in the contentRenderer.cfc file.
Mura.preInit(function(m) {
Mura.DisplayObject.Form.reopen({
// triggered after the form has rendered on the browser
onAfterRender: function() {
var form_container = this.context.targetEl
, the_form = m(form_container).find('form');
console.log('afterRender triggered');
// This is where you register your custom form listeners,
// for example ...
m('button.form-submit').on('click', function(event) {
console.log('Form button clicked!')
console.log(event.target.form);
});
}
// triggered when the form has been submitted,
// before processing/validation begins
, onSubmit: function () {
var the_button = event.target
, the_form = the_button.form;
console.log('onSubmit triggered');
console.log(the_button);
console.log(the_form);
// you could run a script here (obviously) ... then,
// return true if you want to continue,
// or false if you wish to stop processing
return true;
}
// triggered after submit, and form has errors
, onAfterErrorRender: function() {
var resp = JSON.parse(event.currentTarget.response)
, errors = resp.data.errors;
console.log('afterErrorRender triggered');
console.log(errors);
}
// triggered after successful form submit (no errors)
, onAfterResponseRender: function () {
var response_container = this.context.targetEl;
console.log('afterResponseRender triggered');
console.log(response_container);
}
});
});
JSON API
Introduction
The JSON API allows Mura CMS to be the main content creation hub within an organization, and allows developers to build their front-ends as pure JavaScript clients.
Base URL
The base URL for the API endpoints is:
https://yourDomain.com/{context}/index.cfm/_api/json/v1/
API Endpoint Reference
Method |
Endpoint |
Usage |
Returns |
GET |
/{siteid}/{entityname}/{id} |
FindOne |
data |
GET |
/{siteid}/entityname}/{ids} |
FindMany |
data |
GET |
/{siteid}/{entityname}/new |
FindNew |
data |
GET |
/{siteid}/{entityname}/? |
FindQuery |
data |
GET |
/{siteid}/{entityname}/{id}/{releatedentity}/? |
FindRelatedEntity |
data |
GET,POST |
/?method=findCalenderItems&siteid={siteid}&calendarid={calendarid}&start={start}&end={end}&format={format} |
FindCalendarItems |
data | events |
POST |
/?method=generateCSRFTokens&siteid={siteid}&context={entityid} |
GenerateCSRFTokens |
data |
POST |
/{siteid}/{entityname}/?csrf_token={csrf_token}&csrf_token_expires={csrf_token_expires} |
Save |
data |
DELETE |
/{siteid}/{entityname}/{id}/?csrf_token={csrf_token}&csrf_token_expires={csrf_token_expires} |
Delete |
data |
GET,POST |
/{siteid}/login/?username={username}&password={password} |
Login |
data |
GET,POST |
/{siteid}/logout |
Logout |
data |
Response Format
On success, the HTTP status code in the response header is 200 OK
and the response body contains a data object in JSON format. On error, the header status code is an error code and the response body contains an error object.
Example Response
{
"data": {
"key": "value"
}
}
Response Status Codes
The JSON API uses the following response status codes:
Status Code |
Description |
200 |
OK - The request has succeeded. The client can read the result of the request in the body and the headers of the response. |
400 |
Bad Request - The request could not be understood by the server due to malformed syntax. The message body will contain more information; see Error Handling, below. |
401 |
Unauthorized - The request requires user authentication or, if the request included authoriation credentials, authorization has been refused for those credentials. Also, the JSON API feature may not be enabled for the site; see How To Enable, above. |
403 |
Forbidden - The server understood the request, but is refusing to fulfill it. For example, requestor does not have permission to a requested method. |
404 |
Not Found - The requested resource could not be found. This error can be due to a temporary or permanent condition. |
Error Handling
When an error occurs, the response data
attribute will not exist. The response will instead contan an error
attribute.
Example Error Response
{
"error": {
"message": "Insufficient Account Permissions"
}
}
Example Response Handler
$.getJSON('/index.cfm/json/v1/v10/content/').then(
function( resp ) {
if ( 'error' in resp ) {
//handle error
} else {
//do stuff
}
}
});
FindCalendarItems
Get calendar content items.
Endpoint
GET https://yourdomain.com/{context}/index.cfm/_api/json/v1/?method=findcalendaritems&siteid={siteid}&calendarid={calendarid}&start={start}&end={end}&format={format}
Request & Query Parameters
Path parameter |
Value |
context |
The path to where Mura CMS resides within the webroot (typically, an empty string). |
siteid |
The SiteID of where the entity will be stored. |
calendarid |
The contentid of a Mura CMS content item with the "Type" set to Calendar. |
start |
Optional. The start date to filter by. |
end |
Optional. The end date to filter by. |
format |
Optional. Options are default (the default setting) or fullcalendar . This determines how the response is formatted. |
categoryid |
Optional. Filters results by categoryid. |
tag |
Optional. Filters results by tag. |
Example "Default" Response
If multiple entities are found, an items
array will be present in the data
object.
{
"data": {
"items": [
{
"contentid": "2C91A3B0-E375-9383-0B05636E6926868D",
"title": "Event 1",
...
},
{
"contentid": "6C92F953-E042-CBF6-42F66BD74A4BEC0B",
"title": "Event 2",
...
},
...
]
}
}
If only one entity is found, the data
object will contain the entity's keys and corresponding values.
{
"data": {
"contentid": "2C91A3B0-E375-9383-0B05636E6926868D",
"title": "Event 1",
...
}
}
Example "FullCalendar" Response
An events
array will be returned.
{
"events": [
{
"contentid": "2C91A3B0-E375-9383-0B05636E6926868D",
"title": "Event 1",
...
},
{
"contentid": "6C92F953-E042-CBF6-42F66BD74A4BEC0B",
"title": "Event 2",
...
},
...
]
}
FindMany
Get multiple entities.
Endpoint
GET https://yourdomain.com/{context}/index.cfm/_api/json/v1/{siteid}/{entityname}/{ids}
OR
GET https://yourdomain.com/{context}/index.cfm/_api/json/v1/?method=findmany&siteid={siteid}&entityname={entityname}&ids={ids}
Request & Query Parameters
Parameter |
Value |
context |
The path to where Mura CMS resides within the webroot (typically, an empty string). |
siteid |
The SiteID of where the entity will be stored. |
entityname |
The entity's name. |
ids |
A comma-delimited list of entity IDs. |
Example Response
If multiple entities are found, an items
array will be present in the data
object.
{
"data": {
"items": [
{
"id": "2C91A3B0-E375-9383-0B05636E6926868D",
"name": "Example",
"links": {
"relatedentitylink1": "http://...",
"relatedentitylink2": "http://..."
}
},
{
"id": "6C92F953-E042-CBF6-42F66BD74A4BEC0B",
"name": "Another Example",
"links": {
"relatedentitylink1": "http://...",
"relatedentitylink2": "http://..."
}
},
]
}
}
If only one entity is found, the data
object will contain the entity's keys and corresponding values.
{
"data": {
"id": "2C91A3B0-E375-9383-0B05636E6926868D",
"name": "Example",
"links": {
"relatedentitylink1": "http://...",
"relatedentitylink2": "http://..."
}
}
}
FindNew
Gets a new entity.
Endpoint
GET https://yourdomain.com/{context}/index.cfm/_api/json/v1/{siteid}/{entityname}/new
OR
GET https://yourdomain.com/{context}/index.cfm/_api/json/v1/?method=findnew&siteid={siteid}&entityname={entityname}
Request & Query Parameters
Parameter |
Value |
context |
The path to where Mura CMS resides within the webroot (typically, an empty string). |
siteid |
The SiteID of where the entity will be stored. |
entityname |
The entity's name. |
Example Response
{
"data": {
"id": "2C91A3B0-E375-9383-0B05636E6926868D",
"name": "Example",
"links": {
"relatedentitylink1": "http://...",
"relatedentitylink2": "http://..."
}
}
}
FindOne
Find an entity.
Endpoint
GET https://yourdomain.com/{context}/index.cfm/_api/json/v1/{siteid}/{entityname}/{id}
OR
GET https://yourdomain.com/{context}/index.cfm/_api/json/v1/?method=findone&site={siteid}&entityname={entityName}&id={id}
Request & Query Parameters
Parameter |
Value |
context |
The path to where Mura CMS resides within the webroot (typically, an empty string). |
siteid |
The SiteID of the site being searched. |
entityname |
The entityname being searched. For example: "content ", "user ", "feed ", etc. |
id |
The object ID of the entity being searched for. |
Example Response
The data
object will contain the entity's keys and corresponding values.
{
"data": {
"targetparams":"",
"path":"00000000000000000000000000000000001,2C91A3B0-E375-9383-0B05636E6926868D",
"menutitle":"Testing",
"releaseDate":"",
"fileid":"",
"responsesendto":"",
"id":"2C91A3B0-E375-9383-0B05636E6926868D",
"type":"Page",
"lastupdatebyid":"AD771D5C-BE0A-D43D-47F4E201B378BD7A",
"forceSSL":0,
"responsemessage":"",
"subtype":"Default",
"remoteurl":"",
"contenttype":"",
"childtemplate":"",
"keypoints":"",
"moduleid":"00000000000000000000000000000000000",
"inheritobjects":"Inherit",
"searchExclude":0,
"featureStop":"",
"newfile":"",
"remotesourceurl":"",
"displayStop":"",
"remotesource":"",
"remotePubDate":"",
"tags":"",
"majorVersion":0,
"extendautocomplete":true,
"contentsubtype":"",
"url":"/index.cfm/testing/",
"sortby":"orderno",
"displayTitle":0,
"approvalstatus":"",
"credits":"",
"oldfilename":"",
"featureStart":"",
"siteid":"default",
"doCache":1,
"imagesize":"small",
"imagewidth":"AUTO",
"target":"_self",
"minorVersion":0,
"approvalgroupid":"",
"assocfilename":"",
"lastUpdate":"2015-01-29T15:45:51",
"summary":"",
"images":{},
"template":"",
"moduleassign":"",
"displayStart":"",
"isFeature":0,
"filesize":"",
"approvingchainrequest":false,
"categoryid":"",
"tcontent_id":"0",
"isLocked":0,
"isNav":1,
"newstags":"",
"orderno":1,
"htmltitle":"Testing",
"urltitle":"testing",
"approvalchainoverride":false,
"body":"",
"requestid":"",
"restrictgroups":"",
"active":1,
"metadesc":"",
"audience":"",
"links":{
"site":"http://docs.getmura.com:8080/index.cfm/_api/json/v1/default?method=findOne&entityName=site&siteid=default",
"categoryassignments":"http://cf11:8080/index.cfm/_api/json/v1/default?method=findQuery&siteid=default&entityName=contentCategoryAssign&contenthistid=2C92F953-E042-CBF6-42F66BD74A4BEC0B",
"parent":"http://docs.getmura.com:8080/index.cfm/_api/json/v1/default?method=findOne&siteid=default&entityName=content&id=00000000000000000000000000000000001",
"stats":"http://docs.getmura.com:8080/index.cfm/_api/json/v1/default?method=findOne&siteid=default&entityName=stats&id=2C91A3B0-E375-9383-0B05636E6926868D",
"crumbs":"http://docs.getmura.com:8080/index.cfm/_api/json/v1/default?method=findCrumbArray&siteid=default&entityName=content&id=2C91A3B0-E375-9383-0B05636E6926868D",
"comments":"http://docs.getmura.com:8080/index.cfm/_api/json/v1/default?method=findQuery&siteid=default&entityName=comment&contentid=2C91A3B0-E375-9383-0B05636E6926868D",
"relatedcontent":"http://docs.getmura.com:8080/index.cfm/_api/json/v1/default?method=findRelatedContent&siteid=default&id=2C91A3B0-E375-9383-0B05636E6926868D",
"renderered":"http://docs.getmura.com:8080/index.cfm/_api/json/v1/v10/_path/testing",
"kids":"http://cf11:8080/index.cfm/_api/json/v1/default?method=findQuery&siteid=default&entityName=content&parentid=2C91A3B0-E375-9383-0B05636E6926868D"
},
"restricted":0,
"metakeywords":"",
"nextN":10,
"fileext":"",
"notes":"",
"display":1,
"created":"2015-01-29T15:45:51",
"contenthistid":"2C92F953-E042-CBF6-42F66BD74A4BEC0B",
"responsedisplayfields":"",
"expires":"",
"imageheight":"AUTO",
"filename":"testing",
"relatedcontentsetdata":"",
"sortdirection":"asc",
"contentid":"2C91A3B0-E375-9383-0B05636E6926868D",
"changesetid":"",
"displayinterval":"Daily",
"sourceiterator":"",
"mobileExclude":0,
"extendsetid":"",
"remoteid":"",
"parentid":"00000000000000000000000000000000001",
"preserveid":"2C92F953-E042-CBF6-42F66BD74A4BEC0B",
"title":"Testing",
"lastupdateby":"Steve Withington",
"approved":1,
"extenddata":"",
"blogtags":"",
"responseChart":0
}
}
FindQuery
Get an array of entity items.
Endpoint
GET https://yourdomain.com/{context}/index.cfm/_api/json/v1/{siteid}/{entityname}/?
OR
GET https://yourdomain.com/{context}/index.cfm/_api/json/v1/?method=findquery&siteid={siteid}&entityname={entityname}/?
Request & Query Parameters
Parameter |
Value |
context |
The path to where Mura CMS resides within the webroot (typically, an empty string). |
siteid |
The SiteID of where the entity will be stored. |
entityname |
The entity's name. For example: ?entityname=content |
expand |
Optional. A comma-separated list of entity relationshipds that you would like expanded in the result. You can use '*' to have all entity relationships expanded: ?expand=kids,crumbs |
expandDepth |
Optional. The amount of levels that when a match is found in the expand parameter it will be expanded: ?expand=kids,crumbs&expandDepth=2 |
fields |
Optional. A comma-separated listed of fields to return. For example: ?fields=title,summary,contentid |
maxitems |
Optional. Limit the number of records to return. For example: ?maxitems=10 |
itemsperpage |
Optional. Sets the desired number of items to return for each page. For example: ?itemsperpage=3 |
pageindex |
Optional. Sets the desired page for pagination. For example: ?pageindex=2 |
sort |
Optional. Control the sort order and direction of entities by specific attributes/fields. To sort decending, prefix the attribute/field with a minus sign (- ). You may explicitly use the plus sign (+ ) to indicate the default setting of ascending. For example, to sort by "credits" ascending, and "title" decending: ?sort=credits,-title |
cachedwithin |
Optional. Sets the desired cache timespan in seconds. For example: ?cachedwithin=120 |
Custom Query Parameters
Filter results by passing an attribute name of your entity, and the value to search for. Use the star (*
) to denote wildcard.
The following example assumes the entity has an attribute named title
:
GET https://yourdomain.com/{context}/index.cfm/_api/json/v1/?method=findquery&siteid={siteid}&entityname={entityname}&title=about*
This should return any entities with a title
attribute that starts with about
, such as "about, About, About Us, About Time."
Example Response
If one or more entities are found, an items
array will be present in the data
object.
{
"data": {
"endindex": 1,
"startindex": 1,
"totalpages": 1,
"totalitems": 1,
"links": {
"self": "http://domain/index.cfm/_api/json/v1/v10/?&sort=title&entityname=content&siteid=default&fields=title,summary,contentid&itemsperpage=10&maxitems=50&method=undefined&pageIndex=1"
},
"itemsperpage": 10,
"items": [
{
"entityname": "content",
"images": {},
"contentid": "00000000000000000000000000000000001",
"siteid": "default",
"url": "/",
"links": {
"renderered": "http://domain/index.cfm/_api/json/v1/v10/_path/",
"categoryassignments": "http://domain/index.cfm/_api/json/v1/default?method=findQuery&siteid=default&entityName=contentCategoryAssign&contenthistid=14CA1DCF-41A8-4D04-861AFE9D4162CD7C",
"relatedcontent": "http://domain/index.cfm/_api/json/v1/default?method=findRelatedContent&siteid=default&entityName=content&id=00000000000000000000000000000000001",
"crumbs": "http://domain/index.cfm/_api/json/v1/default?method=findCrumbArray&siteid=default&entityName=content&id=00000000000000000000000000000000001",
"stats": "http://domain/index.cfm/_api/json/v1/default?method=findOne&siteid=default&entityName=stats&id=00000000000000000000000000000000001",
"self": "http://domain/index.cfm/_api/json/v1/v10/_path/",
"comments": "http://domain/index.cfm/_api/json/v1/default?method=findQuery&siteid=default&entityName=comment&contentid=00000000000000000000000000000000001",
"site": "http://domain/index.cfm/_api/json/v1/default?method=findOne&entityName=site&siteid=default",
"parent": "http://domain/index.cfm/_api/json/v1/default?method=findOne&siteid=default&entityName=content&id=00000000000000000000000000000000END",
"kids": "http://domain/index.cfm/_api/json/v1/default?method=findQuery&siteid=default&entityName=content&parentid=00000000000000000000000000000000001"
},
"id": "00000000000000000000000000000000001",
"title": "Home",
"summary": ""
}
],
"pageindex": 1
},
"method": "findQuery",
"apiversion": "v1"
}
FindRelatedEntity
Gets a related entity.
Endpoint
GET https://yourdomain.com/{context}/index.cfm/_api/json/v1/{siteid}/{entityname}/{id}/{relatedentity}/?
OR
GET https://yourdomain.com/{context}/index.cfm/_api/json/v1/?method=findquery&siteid={siteid}&entityname={relatedentity}&entitynamefk={id}
Request & Query Parameters
Path parameter |
Value |
context |
The path to where Mura CMS resides within the webroot (typically, an empty string). |
siteid |
The SiteID of where the entity will be stored. |
entityname |
The entity's name. |
id |
The ID of an entity. |
relatedentity |
The name of a related entity. |
entitynamefk |
The ID of an entity. |
Example Response
If multiple entities are found, an items
array will be present in the data
object.
{
"data": {
"items": [
{
"id": "2C91A3B0-E375-9383-0B05636E6926868D",
"name": "Example",
"links": {
"relatedentitylink1": "http://...",
"relatedentitylink2": "http://..."
}
},
{
"id": "6C92F953-E042-CBF6-42F66BD74A4BEC0B",
"name": "Another Example",
"links": {
"relatedentitylink1": "http://...",
"relatedentitylink2": "http://..."
}
},
]
}
}
If only one entity is found, the data
object will contain the entity's keys and corresponding values.
{
"data": {
"id": "2C91A3B0-E375-9383-0B05636E6926868D",
"name": "Example",
"links": {
"relatedentitylink1": "http://...",
"relatedentitylink2": "http://..."
}
}
}
GenerateCSRFTokens
Get a CSRF (Cross-Site Request Forgery) token. The CSRF token is required for special API methods such as delete
and save
.
Endpoint
GET https://yourdomain.com/{context}/index.cfm/_api/json/v1/?method=generateCSRFTokens&siteid={siteid}&context={entityid}
Request & Query Parameters
Parameter |
Value |
context |
The path to where Mura CMS resides within the webroot (typically, an empty string). |
siteid |
The SiteID of where the entity will be stored. |
context |
The primary id of the entity currently being processed. |
Example Response
{
"data": {
"csrf_token": "469153A8855FFA7ECB5A11BC8EB3F3C4",
"csrf_token_expires": "42041.6270023"
}
}
Register Custom API Method
This is an example of how to register a custom JSON API method:
<cfscript>
var APIUtility = getBean('settingsManager').getSite({siteid}).getAPI('json', 'v1');
APIUtility.registerMethod(methodName='applyProperty', method=applyProperty);
public any function applyProperty() {
// do something
}
</cfscript>
Register Custom API Linking Method
Within the API, all entities contain a "link" attribute that contains HREFs to access related entities. For example, a content entity would have a a value set to access the content children with entity.link.kids
. By default, Mura will read through the entity's properties and auto populate its links for you. However, sometimes you will want to add custom link logic.
<cfscript>
var APIUtility=getBean('settingsManager').getSite({siteid}).getApi('json','v1');
APIUtility.registerLinkMethod(method=myCustomLinkMethod);
public any function myCustomLinkMethod(entity,links){
if ( entity.getEntityName()=='targetEntity' ) {
arguments.links['runs']='#getEndPoint()#?method=myCustomMethod&siteid=#arguments.entity.getValue('siteid')#';
}
}
</cfscript>
Creating API methods with Module Beans
You can create components within a module's {moduleDir}/model/beans directory that extend eithermura.bean.bean or mura.bean.beanORM. Within these components any method set to remote access with be availabe via the JSON/REST API.
component extends="mura.bean.bean" {
remote function mymethod(){
return "Hello!";
}
}
You can then call the method like this https://www.domain.com/index.cfm/_api/json/v1/{siteid}/{beanName}/{methodName} .
Register Custom Mura ORM Entity
This is an example of how to register a custom Mura ORM entity:
<cfscript>
var APIUtility = getBean('settingsManager').getSite({siteid}).getAPI('json', 'v1');
APIUtility.registerEntity(entityName='myCustomEntity', config={});
</cfscript>
Mura ORM Entity Config Options
Argument |
Type |
Description |
fields |
string |
A comma-separated list of fields that you would like to return as a default for the JSON objects. |
allowfieldselect |
boolean |
If false , Mura will not allow custom field selects. |
moduleid |
string |
The Mura CMS module or plugin that permissions for this entity should be tied to. |
public |
boolean |
If false , will apply permissions based on the config. |
Example Mura ORM Entity Config
<cfscript>
myConfig = {
fields = 'domain,siteid',
allowfieldselect = false
};
var APIUtility = getBean('settingsManager').getSite({siteid}).getAPI('json', 'v1');
APIUtility.registerEntity(entityName='myCustomEntity', config=myConfig);
</cfscript>
Creating Mura ORM Entities from within Modules
You can create components within a module's {moduleDir}/model/beans directory that extend mura.bean.beanORM. Then do a manual application reload with the ?applydbupdates url variable.
component
extends="mura.bean.beanORM"
entityname="widget"
displayname="Widget"
table="widget"
orderby="name"
bundleable=false
scaffold=true
public=false
{
property name="widgetid" fieldtype="id";
property name="siteid" default="default" required=true datatype="varchar" length="25";
property name="name" required=true datatype="varchar";
property name="description" datatype="text";
}
You can then call the it's endpoint like this https://www.domain.com/index.cfm/_api/json/v1/{siteid}/{beanName} . (Learn more about Mura ORM)
Save
Save an entity.
Endpoint
POST https://yourdomain.com/{context}/index.cfm/_api/json/v1/{siteid}/{entityname}/?csrf_token={csrf_token}&csrf_token_expires={csrf_token_expires}
OR
POST https://yourdomain.com/{context}/index.cfm/_api/json/v1/?method=save&siteid={siteid}&entityname={entityname}&csrf_token={csrf_token}&csrf_token_expires={csrf_token_expires}
Request & Query Parameters
Path parameter |
Value |
context |
The path to where Mura CMS resides within the webroot (typically, an empty string). |
siteid |
The SiteID of where the entity will be stored. |
entityname |
The entity's name. |
csrf_token |
The csrf_token is generated by the generateCSRFTokens method. |
csrf_token_expires |
The csrf_token_expires is generated by the generateCSRFTokens method. |
Example Response
The data
object will contain the entity's keys and corresponding values.
{
"data": {
"id": "2C91A3B0-E375-9383-0B05636E6926868D",
"name": "Example",
"links": {
"relatedentitylink1": "http://...",
"relatedentitylink2": "http://..."
}
}
}
Delete
Delete an entity.
Endpoint
DELETE https://yourdomain.com/{context}/index.cfm/_api/json/v1/{siteid}/{entityname}/{id}/?csrf_token={csrf_token}&csrf_token_expires={csrf_token_expires}
OR
DELETE https://yourdomain.com/{context}/index.cfm/_api/json/v1/?method=delete&siteid={siteid}&entityname={entityname}&id={id}&csrf_token={csrf_token}&csrf_token_expires={csrf_token_expires}
Request & Query Parameters
Path parameter |
Value |
context |
The path to where Mura CMS resides within the webroot (typically, an empty string). |
siteid |
The SiteID of where the entity is stored. |
entityname |
The entity's name. |
id |
The ID of the entity. |
csrf_token |
The csrf_token is generated by the generateCSRFTokens method. |
csrf_token_expires |
The csrf_token_expires is generated by the generateCSRFTokens method. |
Example Response
The data
element will be an empty string.
{
"data": ""
}
Login
Login a user.
Endpoint
POST https://yourdomain.com/{context}/index.cfm/_api/json/v1/{siteid}/login/?username={username}&password={password}
OR
POST https://yourdomain.com/{context}/index.cfm/_api/json/v1/?method=login&siteid={siteid}&username={username}&password={password}
Request & Query Parameters
Parameter |
Value |
context |
The path to where Mura CMS resides within the webroot (typically, an empty string). |
siteid |
The SiteID of the site attempting to login to. |
username |
The User's username. |
password |
The User's password. |
Example Response
{
"data": {
"status": "success"
}
}
data object
Key |
Value Type |
Value Description |
status |
string |
success or failed , depending on the result of the attempt to login. |
Logout
Logout the current user.
Endpoint
POST https://yourdomain.com/{context}/index.cfm/_api/json/v1/{siteid}/logout
OR
POST https://yourdomain.com/{context}/index.cfm/_api/json/v1/?method=logout&siteid={siteid}
Request & Query Parameters
Parameter |
Value |
context |
The path to where Mura CMS resides within the webroot (typically, an empty string). |
siteid |
The SiteID of where the entity will be stored. |
Example Response
{
"data": {
"status": "succes"
}
}
data object
Key |
Value Type |
Value Description |
status |
string |
success is always returned. |
Decoupled Mura & JS Frameworks
You can build Decoupled Mura websites using any JavaScript framework, or plain Vanilla JavaScript as well. We have developed several example integrations for you to use as a starting point to understanding how to best accomplish this.
Vanilla JavaScript
The MuraDecoupled GitHub project is an example of building a Mura Decoupled website using standard JavaScript and Mura JS. The project is a Docker repository, so you will need that installed on your computer. This will allow you to quickly spin up the example and begin exploring a vanilla Mura / Javascript integration.
Initial Setup
You will need a couple of items installed on your test machine. The latest version of Node, as well as Docker.
Next you will need the project itself. You should download MuraDecoupled to a working directory (i.e. a directory where the code can be deployed).
Next you need to start Mura up:
git clone https://github.com/murasoftware/mura-decoupled.git
cd mura-decoupled
git checkout master
docker-compose up
Then go to http://localhost:8888 to initialize Mura's install. You can login with the default (admin/admin) and edit the default site's settings:
- Domain= The domain of the remote site that the the content will be served on. (localhost)
- Is Remote = true
- Remote Context = The directory structure off of the remote site's web root that the site lives (Leave Empty)
- Remote Port = The port of the remote site (80)
- Resource Domain = The domain that Mura will use the access resource like css and js scripts that are dynamically loaded. (localhost)
You can now visit the site at http://localhost
And finally go to your Mura admin (http://localhost:8888/admin) and reload Mura one more to and it will see the mura.config.json from the ./app directory.
For the rest of the details on this project, you can view the ReadMe file on the MuraDecoupled project itself.
Key Project Files
There are several key project files you will want to examine and/or modify while exploring this example project.
Directory or File |
Editable |
Description |
/index.html |
Yes |
This is the landing page for the website, and contains only a) a reference to the local Mura JS instance, the application's /app/index.js file, and the most basic HTML required (HEAD/BODY tags). |
/app/index.js |
Yes |
This is your core JavaScript project file, the one that does all the required work of fetching content via Mura JS and rendering it to the /index.html <BODY> tag. |
/app/mura.config.json |
Yes |
This is the configuration file for the project. If you are familiar with Mura, you will recognize many of the standard configuration settings. This is where you register templates and configuration variables, register modules, and adjust site settings. |
/app/configurators/examplejs.html |
Yes |
This is the base file for the ExampleJS module, a custom module built for this site. |
JavaScript
The /app/index.js in the vanilla (sans any external frameworks like jQuery) JS Decoupled example is the core JavaScript project file, containing all of the JavaScript-related to rendering the base decoupled website, where Mura JS acts as both the DOM selector and framework-agnostic ... framework.
The first thing to be aware of is where the content is rendered to.
let currentContent;
This is the variable where the current content is registered to. Note that this becomes a Mura JS "Content" object, containing not only the associated content but a range of helper functions to render the page (i.e. navigation).
Next is the Mura initializaiton function:
Mura.preInit(function(){
// your init code here
}
The "preInit" function is called as a precursor to the Mura "render" function, so that all actions taken here are pre-render. This can be important if your code informs what will take place on the page (such as the instantiation of Modules that the page will need to render, as per this example).
Within this block, we will create a number of custom Mura Modules, which are self-contained and often dynamic components that make up a decoupled website's page. For instance, the page's header, footer, any sidebar or main content area, can all be created as Modules. Custom Modules can also be created, and contained within any of these aforementioned layout Modules.
Custom Module
In this example, we first create a custom module for our project called "Examplejs".
Mura.Module.Examplejs = Mura.UI.extend({
renderClient: function() {
this.context.mytitle = this.context.mytitle || "The 'Title' param has not been assigned";
this.context.targetEl.innerHTML=`<h2>${Mura.escapeHTML(this.context.mytitle)}</h2>`;
return this;
}
});
Note that this Module, and indeed all Modules, are all extensions of the Mura.UI class. This class provides the important renderClient() and renderServer() methods that render your content.
You will notice that Modules are essentially an encapsulated, reusable component with their own render function. If you recall from the Creating Custom Modules section, this.context is the variable that holds all data associated to the module, including any settings registered via the configurator when it is added to the page.
Note that this.context contains one reserved variable name, this.context.targetEl. This is the target element to which your rendered content is applied, which is then rendered by the renderClient() function.
Layout Modules
We next find the actual components. First the Header Module, which will render our breadcrumbs and navigation:
Mura.Module.Header = Mura.UI.extend({
//...
});
We achieve this by referencing the current (referenced as currentContent) .get('crumbs') method. If you examine the JSON API endpoint of any content page, you will see a variety of endpoints available, including one called "crumbs". This request returns an array of shorthand "minimal" content beans called ContentNavBeans. We then render the data returned by this request:
const crumbCollection=await currentContent
.get('crumbs')
.then((crumbs)=>{
return crumbs;
});
As with all Mura JS asynchronous requests, the response is returned as a Promise and in this example uses the await operator to handle the request.
Next we have a custom Module called Examplecollectionlayout, which is included as an example of how you might render a Collection in a decoupled website.
Mura.Module.Examplecollectionlayout = Mura.UI.extend({
renderClient: function() {
// ... rendering code here
}
});
You will find several important references to this in the mura.config.Json, which should help you understand the context of how these are registered specifically as "collection"-rendering objects. This Module includes several key concepts related to collections, such as paging navigation, via helper methods that are included in the context of any Mura Collection.
Note that this Module contains a conditional scroll, where new content is automatically called (asynchronously and then appended to the bottom of the page as the user scrolls downward. This is of course not a necessary embellishment, but is a commonly used one and good example of the flexibility of these types of requests.
With these two Modules sorted, we have reached the point where we are ready to render the page. All of this will occur in the encapsulated Mura() namespace, which ensures that all pre-render actions have been completed.
Mura(function(){
});
The first thing we have here is our layout template. A decoupled site can contain any number of templates (as defined in your mura.config.json file), but for this example we have one, "default", defined as follows:
const templates={
default:`
<nav id="primary-nav">
<ul class="mura-primary-nav"></ul>
</nav>
<div class="mura-region-container" data-region="primarycontent"></div>
<footer>
<!-- Create a component named "Footer" and it will render here-->
<div class="mura-object" data-object="component" data-objectid="footer"></div>
</footer>
<div class="mura-html-queues"></div>
`
};
In this template we defined several regions: a place for our navigation (mura-primary-nav), a container for our primary content (mura-region-container), and a footer. There is also a mura-html-queues which we use as a container to load any queued assets. Let's look a closer look at the footer, as this is an example of how you can hard-code a Module into your templates.
Footer
<footer>
<!-- Create a component named "Footer" and it will render here-->
<div class="mura-object" data-object="component" data-objectid="footer"></div>
</footer>
In this code you can see the div contains a class of mura-object and an attribute data-object of "component". When an object has a class of mura-object, this tells Mura JS that this is a Module, upon which the the element's data-object will be used to determine which Module is being referred to. In this case, we are referring to one of Mura's baked-in Modules, the Component Module. Finally, there is also a data-objectid of "footer", which identifies the specific Component we want loaded. You can apply this same methodology to bake in custom Modules, such as the ones we created above.
Rendering
Now that we have a complete template, we can begin the process of rendering the page. We create a dedicated render() function, as we want to refer to this function whenever the hash of the page changes (as when a user clicks on a navigation link).
First, lets look at the request that loads the current hash (url) and retrieves the content:
let hash= location.hash || '#';
let query=Mura.getQueryStringParams();
Mura.renderFilename(
hash.split('#')[1],
Mura.extend(Mura.getQueryStringParams(),{expand:"crumbs"})
).then(function(content){
currentContent=content;
});
In this example, we are retrieving the "hash" of the url and passing this to the renderFilename() function, which takes not only the hash but any query string params, and uses those to retrieve the requested content page from Mura.
Configuration
The /app/mura.config.json file contains the configuration required to assemble your decoupled Mura website. This is where you will enter all of your global and site-specific configuration, settings specific to the Layout Manager, identify modules and even deploy custom Mura ORM objects.
{
"global":{
"rendererProperties":{
"templateArray":["Default"],
"collectionLayoutArray":["Examplecollectionlayout"],
"hashurls":true,
"primaryContentTypes":"Page,Link,File",
"spacingoptions":[
{"name":"Tight","value":"tight"},
{"name":"Normal","value":"normal"},
{"name":"Loose","value":"loose"}
],
"modulethemeoptions":[
{"name":"Brand Default","value":"module-brand"} // ...
],
"coloroptions":[
{"name":"White","value":"#ffffff"} // ...
],
"ckeditor":{
"contentcss":"http://localhost/app/css/editor.css",
"templates":"",
"stylesset":"",
"customconfig":""
}
},
"modules":{
"Examplejs":{
"name":"Examplejs",
"contenttypes":"*",
"configurator":[
{"type":"text","name":"mytitle","label":"My Label"}
]
},
"Examplecollectionlayout":{
"name":"Example Collection Layout",
"contenttypes":""
},
"Header":{
"name":"Header",
"contenttypes":"*"
}
},
"entities":{
},
"sites":{
"default":{
"rendererProperties":{
},
"modules":{
}
}
}
}
}
Configuration |
Description |
global |
Contains all configuration information for the decoupled Mura instance. |
renderProperties |
Configuration related to the rendering of content and Layout Manager. |
templateArray |
An array of template names, as defined within the JavaScript code. |
collectionLayoutArray |
An array of Module names that are to be treated as Collections. |
hashurls |
If set to true, the renderClient() function will update when the url hash changes. |
primaryContentTypes |
The list of Content Types that will be displayed in the Front End Editing Toolbar. |
spacingOptions |
Spacing options defined for the Layout Manager |
moduleThemeOptions |
Classes available to Module themes in the Layout Manager |
colorOptions |
Named colors available to Module themes in the Layout Manager |
ckeditor |
Editor-specific configuration |
entities |
An array of Mura ORM objects described in JSON (see the Assembler for details) |
modules |
The custom modules identified in the project's JavaScript (see Decoupled JavaScript) |
sites |
Contain a list of all the Mura sites available in the Front End Editing Toolbar |
default |
An example "default" site identified in the site Array |
renderProperties (et.al.) |
All configuration options available to global can be set here as well |
You can of course include your own configuration information here, and access it via Mura.get('value'). For more insight into these settings, you may want to examine the Content Renderer documentation, as all settings there pair to the ones you see above.
Angular
The MuraAngularDecoupled GitHub project is an example of building a Mura Decoupled website using Angular as your JS framework. The project is a Docker repository, so you will need that installed on your computer. This will allow you to quickly spin up the example and begin exploring the Mura JS / Angular integration.
The latest up-to-date instructions are provided with the repository.
React & NextJS
The MuraNextJSDecoupled GitHub project is an example of building a Mura Decoupled website using the React framework NextJS as your JS framework. The project is a Docker repository, so you will need that installed on your computer. This will allow you to quickly spin up the example and begin exploring the Mura JS / React - NextJS integration.
The latest up-to-date instructions are provided with the repository.
Vue & NuxtJS
The MuraNuxtJSDecoupled GitHub project is an example of building a Mura Decoupled website using the Vue framework NuxtJS as your JS framework. The project is a Docker repository, so you will need that installed on your computer. This will allow you to quickly spin up the example and begin exploring the Mura JS / Vue - NuxtJS integration.
The latest up-to-date instructions are provided with the repository.