Blog

Introducing the Immutable Mura 7.1

November 7, 2017 by Grant Shepert

We are on the precipice of launching Mura 7.1. If that sounds ominous, it's intentional. Mura has been moving towards a common goal since the beginning of the 6.0 branch. That goal can be summed up with what is fast becoming an industry standard—Immutable Infrastructure.

We're going to look at what's new in Mura 7.1 through the lens of Immutable Infrastructure, and explore the idea that you will be able to get much more done without sacrificing security, quality or time spent. If anything, you will discover you have more of all three.

"Immutable infrastructure is an approach to managing services and software deployments on IT resources wherein components are replaced rather than changed. An application or services is effectively redeployed each time any change occurs" - techtarget.com

This isn't something that's been achievable in the past. It requires a complex infrastructure that can easily manage, or at least orchestrate, many moving parts at once, in a unified way. Usually only large organizations with unlimited budgets could manage. That is no longer the case.

Docker First

Docker is a way to "containerize" your website, including its infrastructure (web server, database, internal and external resources), its component parts (theme, plugins and modules), and even its security and management (testing, development, source control, deployment, failure protection and security). With all of this made easier and put more immediately under your control, the black box becomes much smaller.

Docker logo

There is no room here to fully explain what Docker brings to Mura (in fact I've written an entire separate blog post on the subject, which is forthcoming), but suffice it to say, it will entirely change the way you work for the better.

Mura 7.1 is now fully "dockerized". We have created detailed instructions on how to use and deploy Mura on Docker, how to manage and configure your Docker instance, and more.

Containers for the Win

Using Docker adds the concept of Continuous Deployment - the ability to continually work on your website in local and shared development with full source control, and then deploy these fully tested instances to your live server - and a layer of enhanced Security, because your work is done in containers isolated from the rest of your system, and because you are incorporating the very latest fully-patched versions of *every* layer of your application from database to engine, in every build.

Testing is easier because every member of the development team is working in the exact same environment as the live version of the application. Support is easier because you can test using the exact same environment that is experiencing the problem.

All of this means fewer distractions, fewer worries about failure or the cost (in hours and dollars) of supporting the project. You spend less cycles managing and more doing, and that is a very good thing.

Remote Integrations

Mura exists in a world of clustered, stateless, immutable services, like Amazon AWS, Microsoft Azure and hundreds more like them, plus the innumerable products and services that exist within those platforms.

Mura 7.1 introduces a host of new settings.ini configuration options that will allow you to more easily integrate with these external services. For instance, defining your S3 store and asset directories is as simple as adding a few values to the settings.ini.cfm file, and reloading the application. You can also integrate Mura with a variety of external caches. No more worrying about local failures or complex customizations, these will now be available out of the box!

Auto Updates

Auto Updates have also changed. Rather than separately updating core files, and then the files for each Mura site, version 7.1 does not separate core and site updates.

We have also made it possible for you to define your own Mura update url, so you can have your own distribution endpoint for internal use. This will allow you to test updates in isolation, then deploy them across your infrastructure.

As an additional point of stability, Mura CMS 7.1 and beyond will no longer be released in the form of incremental updates with minute, ongoing patches and bug fixes. Instead, we will define specific release points which will be available to the auto-update process.

New Modular Directory Structure!

In order for Mura to be fully modular and container-friendly, we've made some important changes to the underlying directory structure. For one, we've eliminated site files. Themes are now installed globally into a root-level /themes folder, and display objects (now known as "modules" in keeping with common industry terms) live in the root-level /modules folder.

This will make Mura sites a lot easier to manage in general whether you are using Docker or not. No more duplication of code or custom shares between sites!

Assembler & Scaffolder

The introduction of Mura ORM in version 6.0 made integrating custom business logic into Mura much easier. However, entering data into your custom objects meant you had to build custom forms for each one, often requiring a plugin because this work happened on the admin side of things. As of Mura 7.1, these hurdles have been cleared.

Mura Assembler allows you to assemble custom Mura ORM entities via the Mura administrator. These entities can be stand-alone objects, or may exist in relationships with other objects (i.e. office locations linked to managers, linked to contact information).

Mura Scaffolder adds complete data management on top of Mura ORM entities, so that you can add, edit, delete and manage the data they contain. There is no extra work required; Scaffolder will take all the information it needs from your Mura ORM entity and build the form for you, including the links to any related entities!

It has never been so easy to extend Mura and build in custom functionality, without having to write a single line of code.

CaaS

Content as a Service is fast becoming the most sought-after aspect of a modern Content Management System. The ability to easily project your content out into the world via any number of known (and unknown) platforms like mobile, remote-integrated or "headless CMS" environments is crucial. Likewise, with so many developers and organizations moving towards JavaScript-based frameworks like React, Angular, Node, Vue and many others, it is crucial that a modern CMS be able to work seamlessly and fully with those technologies.

Mura 7.1 is the first version that makes this ability universal. Every piece of content, from a simple component to those living within custom Mura ORM entities, is available for remote consumption. Mura JS and the JSON API act as the gateways to the content and are fully supported by Mura's robust security and integrated permissions.

In Mura 7.1, you get to pick the tools you are most comfortable and proficient with when developing your project first, rather than having the foundation application determine the technologies you need to use!

Default Theme

There is no longer a default theme included in the Mura CMS core. Instead, a default theme is downloaded and installed the first time Mura runs after installation. This keeps the Mura instance lighter and more compartmentalized, and allows for maintenance of the default Mura theme as a separate project, with its own codebase.

The all-new Mura Boostrap 4 default theme is available in theme-only format or as part of a site bundle which includes sample content along with the theme files.

Aggregate Queries in Feeds

Another much-requested feature, aggregate queries, is now available in 7.1! The lack of aggregates meant that feeds didn't work as well for some situations where aggregates like sum, count or average are needed in addition to standard column values in a feed's underlying query. A little brainstorming broke the problem, and now they are easy to include!

Here's an example of an aggregate query in JavaScript:

<script>
// get and log a query object 
 Mura.getFeed('car')
       .aggregate('count','*') //count
       .aggregate('sum','price') //sum_price
       .aggregate('min','price') //min_price
       .aggregate('max','price') //max_price
       .aggregate('avg','price') //avg_price
       .getQuery()
       .then(function(resp){
         console.log(resp.getAll());
       });
</script>

This is a similar function, written in CFML (cfscript):

<cfscript>

// return a query
 rs=Mura.getFeed('car')
       .aggregate('count','*') //count
       .aggregate('sum','price') //sum_price
       .aggregate('min','price') //min_price
       .aggregate('max','price') //max_price
       .aggregate('avg','price') //avg_price
       .getQuery();

// return an iterator object       
 iterator=Mura.getFeed('car')
       .aggregate('count','*') //count
       .aggregate('sum','price') //sum_price
       .aggregate('min','price') //min_price
       .aggregate('max','price') //max_price
       .aggregate('avg','price') //avg_price
       .getIterator();

</cfscript>

Mura JS

Mura JS is Mura's javascript utility. Supplementing utilities like jQuery, it includes core Mura functionality via the JSON API to call feeds, create and manage Mura ORM entities and render content, and integrates fully with Mura's existing permissions. With Mura JS you can literally build an entire application, completely separate your Mura website and front end, using Mura as a CaaS provider to your custom application, whether that is a React website or Node.js desktop application.

As of 7.1, Mura JS is now managed as a separate, stand-alone project. There is an NPM package for Node, as well as a host of integrations for other JavaScript packages.

Swagger

Swagger is an API developer framework that allows you to test and extend supported remote APIs. It allows developers to easily learn about, test and interact with Mura's JSON API. For instance, hand-crafting a feed that links several entities used to mean a try/fail iteration of coding until you had it working. Now you can just plug in your parameters and play with the results until they fit with what you need.

Swagger logo

Mura automatically generates the necessary swagger.json files for all Mura ORM (core and custom) entities, enabling use of the world's largest tooling framework. Learn more about Swagger at swagger.io.

Smarter Event Handling

We have added much more granular event handling in Mura 7.1. Now, instead of being required to register event handlers across entire sites, developers can register events for specific Mura ORM entities only.

In previous iterations, events were triggered on a much broader scale, meaning it was easier for unplanned events or unrelated actions to trigger the events tied to your business logic. Now that you have more fine-grained control over these interactions, it will be easier to not only avoid these potential conflicts, but also to build much more targeted interactions.

This is a very basic example of a custom eventHandler registration via JavaScript:

<script>

Mura(".myclass").addEventHandler(
{
   click:function(a){
   },
   touch:function(a){      
    }
 }
);

</script>

This example shows a few more advanced options when registering events via CFML:

<cfscript>
  function onApplicationLoad(Mura){
    Mura.getBean('content')
        .loadBy(filename='contact')
        .on('renderStart',function(Mura){
          //WriteDump('test1');abort;
        }
      );
    Mura.siteConfig()
        .on('renderStart',function(Mura){
          //WriteDump('test2');abort;
        }
      );
    Mura.globalConfig()
        .on('renderStart',function(Mura){
          //WriteDump('test3');abort;
        }
      );
    Mura.getBean('content')
        .loadBy(title='My Form')
        .on('submitSave',function(Mura){
          //WriteDump('test1');abort;
        }
      ).on('submitResponseRender',function(Mura){
        //WriteDump('test1');abort;
        }
      );
    Mura.getBean('widget')
        .loadBy(id='...')
        .on('beforeSave',function(Mura){
            //WriteDump('test1');abort;
          }
        ).on('save',function(Mura){
          //WriteDump('test1');abort;
        }).on('afterWidgetSave',function(Mura){
          //WriteDump('test1');abort;
          }
        ).addEventHandler({
          beforeSave=function(Mura){
          },
          save=function(Mura){
          },
          afterSave=function(Mura){
          }
        });
    Mura.siteConfig()
      .on('beforeWidgetSave',function(Mura){
          //WriteDump('test1');abort;
        }
      ).on('widgetSave',function(Mura){
        //WriteDump('test1');abort;
      }).on('afterWidgetSave',function(Mura){
        //WriteDump('test1');abort;
        }
      ).addEventHandler({
        renderStart=function(Mura){
        },
        widgetSave=function(Mura){
        },
        afterWidgetSave=function(Mura){
        }
      });
    Mura.globalConfig()
      .on('beforeWidgetSave',function(Mura){
          //WriteDump('test1');abort;
        }
      ).on('widgetSave',function(Mura){
        //WriteDump('test1');abort;
      }).on('afterWidgetSave',function(Mura){
        //WriteDump('test1');abort;
        }
      ).addEventHandler({
        beforeWidgetSave=function(Mura){
        },
        widgetSave=function(Mura){
        },
        afterWidgetSave=function(Mura){
        }
      });
  }
</cfscript>

Note the use of 'writeDump' as a sample action - this would be replaced with the actual contents and logic of your custom events.

The Road Ahead

The era of artisan "assemble everything by hand" technology stacks just so you can get your website up and running—and having to do this on every single one of your team's local systems as well, is over.

The nice thing is, the transition isn't anywhere near as traumatic as it seems.

The Docker First approach has enabled us to make vast improvements in our own organization, in time spent developing and debugging, in coordinating work between teams spread across wide geographic locations, in delivering testable applications to clients and stakeholders, and in reducing the time spent managing and supporting deployed websites. Often these improvements came so quickly and easily, it was hard to appreciate them until months later, when we realized how much simpler everything was coming together.

Immutable Infrastructure is a big technical concept, and a bit intimidating when looked at from on high. The fact is, you can finally achieve it without having to greatly adapt your current routines. We've worked hard at making Mura 7.1 the kind of application that will fit in with the modern infrastructure, and reduce the barriers between where you are and what you want to achieve.

Take it for a Spin

Try out the new Mura CMS Docker image at hub.docker.com/r/blueriver/muracms/ or download the latest version from the Mura 7.1 github branch. As of the time of this publication, Mura 7.1 is in "release candidate" status, which means it is stable and publicly available, but not the official "current version" of Mura quite yet. As always, your input and feedback are appreciated while we wrap up a few remaining details and prepare for the release of Mura CMS version 7.1.