Skip to content


Subversion checkout URL

You can clone with
Download ZIP
This repo is an ebook about the locomotive cms that is currently in work. Feel free to Fork !!
Pull request Compare This branch is 50 commits behind geraudmathe:master.
Fetching latest commit...
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.

Locomotive CMS Guide

This book is incomplete and should evolve in the future. Any contribution is very welcomed !

For any questions or advices about this book, ask, and if you need support from Locomotive team, ask


  1. Foreword
  2. Overview
  3. Getting something running in 5 minutes
  4. Templating
  5. Models
  6. Locomotive Editor
  7. Using Locomotive in an existing Rails app
  8. Tips
  9. Appendix


Why this guide ?

There is already an official documentation reference, which lists almost everything. Still, a pragmatic guide to Locomotive is missing, especially for beginners.

What's more, since there is a lot of goodness in the Locomotive's Google Group, it seems relevant to gather good practices & hacks in one place.

This guide isn't the official one, even if some members of the Locomotive core team have reviewed some parts of it.

Why should you use locomotive ?

Locomotive is a CMS that has been created with a main guideline: keep it simple !

  • Keep it simple, for the lambda user who doesn't write code.
  • Keep it simple, for the developer who shouldn't have to go deep in architecture, and should be able to edit a website quickly.
  • Keep it simple, for the author who needs to be focused on content, and shouldn't have to go through several pages to edit.

If you recognize yourself in on of the case listed above, you should use Locomotive.


From a "business" point of view, Locomotive have several benefits to sell :

  • Front-end editing of static texts, using Aloha editor
  • Hosting on Heroku / AWS very cheap, almost free
  • Finally, a great looking backoffice !

Philosophy behind the CMS

TODO: demander à Didier de l'expliquer ?


During this reading, it is assumed that:

  • You know what is Ruby and Rails and you've a good feeling with terms like Gem, Bundle, deployment
  • You know what is a data model, and ideally what is a document-oriented storage like Mongo
  • You have basic knowledge about shell and command-line interface

Organization of this book

This guide is structured as follow :

First, an Overview of the CMS aims to introduce the environment and the main things to know about.

Getting something running in 5 minutes may help Locomotive's beginners walking through the


Anatomy of a Locomotive app

Locomotive CMS is crafted as an engine.

A Rails engine is an application packaged in a rubygem that is able to be run or mounted within another Rails application. An engine can have its own models, views, controllers, generators and publicly served static files. (more about engines)

What's inside ?

  • Rails 3
  • Mongoid
  • Devise
  • Liquid
  • Haml
  • Formtastic
  • Carrierwave

TODO: complete this

Key features

You have out of the box :

  • Multi sites : manage multiple websites with one application instance
  • Flexible content types
  • Front-end inline editing (Aloha editor)
  • Content localization
  • Restful API
  • Haml / Sass support
  • Liquid templating langage
  • A very nice User Interface

Getting something running in 5 minutes

TODO: définir avec géraud et didier ce que l'on fait dans cette app, liste des choses à voir : editable texts, models, templates (héritage), tags liquid de base, … ?

il n'y a pas de template de base, sauf si on achète loco editor là il y en a mais sinon non, pas dans la version 2.0


Templating Logic

Basics of inheritance

The logic in Locomotive is differs a bit from what you are certainly used to, it may be weird a first, but it's actually very simple.

In the classic, Rails way, you have the following architecture, with page's content integrated in the application layout using the yield statement :

+- Views
    +- layout
        +- application
    +- mysite
        +- index
        +- first page
        +- second page

In Locomotive, it's a bit different :

+- Pages
    +- index
        +- first page
        +- second page

All pages inherit from index. This way, the index contains the application's layout and the index page content. How do you re-use the layout without re-using the index page content ? By introducing {% block 'block_name' %} ... {% endblock %} : since all pages inherit from index, you declare blocks of content inside the layout (index), which will be overwritten in child pages. Here is a the simpliest example :

Index page :

    <title>My index page</title>
      layout header
    <div id="content">
      {% block content %}
        the content of the index page
      {% endblock %}
      layout footer

A page, which inherits from index :

{% extends parent %}

{% block content %}
  the content of this page
{% endblock %}

By extending index, 'a-page' re-use all of its content, except the content inside the {% block %} tag which is overwritten. This tag is written with the Liquid syntax which will is explained later.

You can have as many {% block %} tags as you want, everywhere in the layout, as long as the name of each block is unique. For a basic application which only have one layout, that's all you need to know.


Going further

Several levels of inheritance

The principle of page's inheritance can be applied to every page. When you create a page, it automatically inherits from index, but you can also make it inherits from another page, by specifying it's parent :

Specifying parent

By doing so, you can define as many levels as you want :

+- Pages
    +- index
        +- first page
            +- child of first page
                +- child of first page's child
        +- second page

Inherit from an other page than the parent one

When you extend the parent's layout, you use the tag {% extends parent %}, but what if you would like to extends a page which isn't a direct parent ?

For example, how would you make "first page" extends "second page" ?

+- Pages
    +- index
        +- first page
        +- second page

It's simple : {% extends first_page %} ! You specify the page you want to extend with its slug.


What about several layouts ?

Let's say your website needs two layouts, how do it without putting the entire index in {% block %} tags ? It's actually fairly simple : you are not forced to make a page inherits its content from another.

Remember the previous page we created which inherited from index :

{% extends parent %}

{% block content %}
  the content of this page
{% endblock %}

Well, actually the tag {% extends parent %} can be removed, so the page doesn't extends any other page, letting you define a brand new layout if needed.

Let's illustrate this with an example:

  • the main layout of the site will be define in index
  • a second layout will be defined in a page called "alternate_layout"
  • we will have a page called "normal" which will use the main layout
  • and finally an other page called "alternate_page" which will use the alternate layout

The skeleton will look like that :

+- Pages
    +- index
        +- normal
        +- alternate_layout
            +- alternate_page

Here we go :

First, the index page :

    <title>The Main Layout</title>
      Main header
    <div id="content">
      {% block main_content %}
        the content of the index page
      {% endblock %}
      Main footer

The "normal" page, which inherits from it index :

{% extends parent %}

{% block main_content %}
  the content of the normal page
{% endblock %}

Then the "alternate layout" page, which doesn't extends its parent, index :

    <title>The Alternate Layout</title>
      Alternate header
    <div id="content">
      {% block alternate_content %}
        the content of the alternate layout page, it can be empty if you just want to define an empty layout
      {% endblock %}
      Alternate footer

And finally, the "alternate page", which inherits from "alternate layout". You may notice the {% extends alternate_layout %} instead of {% extends parent %}, as explained in the previous part.

{% extends alternate_layout %}

{% block alternate_content %}
  the content of alternate page, using the layout defined in alternate_layout.liquid.html
{% endblock %}


To conclude with templating basics, it's worth to know that Locomotive gives you the ability to put some blocs of code in a separate file called snippet, in the same way Rails does with partials. That's very usefull when you have to build a modular layout without repeating code.

Like Rails, you can pass a variable to the snippet, or simply include a static bloc of code. The following example will cover both cases, don't bother with the liquid syntax which will be explained in the next part.

+- Pages
    +- index
+- Snippets
    +- sidebar
    +- product_information

Here is the index, which includes the sidebar, and also loops on products model and include the snippet "product_information" at each product item :

    <title>Snippet example</title>
    <div id="content">
      <!-- Loop on products  -->
      {% for product in contents.products %}
        <!-- Include "product_information" snippet with the current product -->
        {% include 'product_information' with product %}
      {% endfor %}
    {% include 'sidebar' %}

Then the sidebar :

<div id="sidebar">
  the sidebar

And finally the product_information snippet which uses the context "product" :

<div class="product">
{{ }} :  {{ product.price }}$


Liquid syntax

Liquid is a templating library extracted from Shopify, the project is hosted at Locomotive reuse a lot of the original library.

Everything in 2 markups

The liquid syntax is a templating engine based on a set of functions that allow the developer (or the designer, since you don't need strong code skills to write it) to keep focus on the rendering of the data, not on the way it could render it. Liquid defines 2 types of markup, pretty close to what you are used to with Erb :

Ouput markup : matched pairs of curly brackets output the value of an object :

Erb :

<%= %>

Liquid :

{{ }}

Tag markup : matched pairs of culry brackets and percent, not resolved to text :

Erb :

<% name = %>

Liquid :

{% assign name with %}

Liquid is extracted from, but Locomotive extends it. To cover all, we will distinguish 3 cases :

original Liquid doc:


When writing a liquid template, you will access to a couple of objects representing for instance the current site, page, logged in account as well as collections such as your custom content types. They are also called 'drops'.

Available objects and their attributes are listed here :

SEO purpose

You can either use the object site and have the same meta all over your website :

    <title>{{ site.seo_title }}</title>
    <meta name='description' content='{{ site.meta_description }}' />
    <meta name='keyword' content='{{ site.meta_keywords }}' />

Or you can define SEO meta for each page :

    <title>{{ page.seo_title }}</title>
    <meta name='description' content='{{ page.meta_description }}' />
    <meta name='keyword' content='{{ page.meta_keywords }}' />


img magick


editable file =>!topic/locomotivecms/hOaqFUcZCm8 only in backoffice for 2.0

exemple: promotion

Creating a page

You have several option when you create a page. Let's take a look.

General information

General Information

Nothing complex, just specify the name of the page. The slug field will be updated automatically.

Be aware the slug will reference the url linked with the page you are creating, if you change it later, it could break links in the website.

Set the parent page, as explained in Templating Logic.

SEO settings

SEO settings

Edit the meta title, keyword, description for the page, or leave it empty if you want use the global meta.

These meta values will then be available for use in the template with Liquid tags, this way :

<title>{{ page.seo_title }}</title>
<meta name="keywords" content="{{ page.keywords }}"/>
<meta name="description" content="{{ page.description }}"/>

Advanced options

Advanced options

  • Handle :

    Used when you integrate Locomotive with a Rails app, see this chapter.

  • Response type :

    You can choose between HTML, RSS, XML or JSON. You may this way generate a RSS feed or build an simple API from your Locomotive site.

  • Templatized :

    Defines weither this page should be a template for a model instance, see this chapter.

  • Published :

    Since only authenticated accounts can view unpublished pages, this allows debugging a page on a deployed site.

  • Listed :

    The Liquid {% nav %} generates a menu (doc) based on your page. Control here weither this page appears in the generated menu.

  • Redirect :

    If you check this, you can redirect the page to a url.

    It then will be a 301 redirection, which from a SEO point of view, is a permanent redirection. You should use it when you have changed your urls. The search engine will then forget the previous page and update the url in its database.

    source: [](

  • Cache strategy :

    Define here the cache strategy for this page.

Recipe : Create a RSS feed


TODO: it's a draft, rewritte it

This chapter covers models, or the custom content, Locomotive let you build in the Ui. In this guide we use the word model as it's what we are used to, but in the Locomotive reference you will see content type, and content entrie for an instance of a content type.

The first subchapter aims to introduce the very basic creation and usage of models, the second one is about building relationships between your models. We cover then the rendering of models using Liquid, where we try to show the common use cases, and after that a subchapter is dedicated to the functionality of templating a model. Finally, we will cover the public submission of models, which allows frontend users to create instances.


First step of model creation, specify the name of the model :

Create model

As mentioned in the hint, you will reference your model in Liquid logic by its slug.

Then, you will define the fields (attributes) of your model in the following section :

Create fields

Fields types

The following types of attributes (fields) are available :

Types list

Let's detail each one of them. The rendering of these types will be detailed later.

Nota bene : you will encounter some properties not explained, it's because they are common to all field types and will be detailed later.

  • Simple input :

    A string, max 255 chars (?)

  • Text :

    Text field, but you can choose the format. When you add a field :

    type text 1

    you then have a properties panel which appears by clicking on the arrow on the right part of the line :

    type text 2

    If you choose Text formatting : HTML, you will have a WYSIWYG editor, TinyMCE :

    type text tinymce

    And if you choose Text formatting : none, you will have a simple textarea :

    type text textarea

  • Select :

    Displays a select list of options for the field.

    type select

    You have to put the options of the list in the property of the field, here we have "frontend" "backend" and "api" for the example. This type of attribute is handy, since it may avoids you the creation of a third-party model for simple lists like this one. But you have to know the options of the list are only editable from the model editing page. So when you are creating a model instance, you can't edit options of the list. It may be a problem if you don't want a user access to model's structure.

  • Checkbox :

    A simple boolean field.

  • Date :

    A date field which is editable by this :

    type date

  • File :

    A field of this type supports the upload of any kind of file.

The other fields specifying a relationship with an other model (belongs_to, has_many and many_to_many) will be explained in the next section, Models mapping.

Common fields properties :

When you define an attribute (or a field) for your model, you have some properties which are specific for each kind of attribute (detailed previously), and some which are common to every one.

type properties

  • Required / Optionnal :

    Defines weither this field is required or not for the validation.

    A model must have at least one field, that's obvious, and the first field you will define will be considered as the mandatory one, and will be automatically saved as Required. There is one exception though: you can't have a mandatory field whose type defines a relationship with an other model.

  • Name :

    Make sure the name of the field highlighten in yellow here match the "Name" property bellow. As the tip explains "Name of the property for liquid templates", it will be this value you will have to use in the liquid template, and not the value highlighten in yellow.

    It may seems obvious, it is, but if you change the name of the field (the one highlighten in yellow) and forgot the update accordingly the value of the field bellow, you will not be able to retrieve the object in Liquid and may wonder why...

  • Hint :

    Hint for the end user of the backoffice, displayed in the model form just below the field.

  • Localized :

    Used for internationalization, detailed here.

Presentation and Advanced options

When the attributes of the model are defined, click on "Create" to edit advanced options of the model :

models advanced props

For the purpose of the example, the following model will be used in what's follow :

models advanced model

So let's say we have a model of posts, with a title (string), some text (text), a category (select with options "frontend" and "backend") and a publishing_date (date).


Theses options let you customise how entries of your model are displayed in the backoffice page.

  • Label field :

    Choose the field of the model displayed for each entry.

    If you choose the field title, you have :

    models advanced label 1

    And if you choose publishing_date, you end up with :

    models advanced label 2

  • Group by field :

    Group entries by a common field value. This is available only for fields which have the type select. So here we can group by category :

    models advanced group by

  • Item template :

    Let you really customize the string displayed for each entry in the list of model's entries. For example, let's say I don't display my posts grouped by category, but still I would like the category appears aside the post title, I would do so :

    models advanced item template

    And my entries would display this way :

    models advanced item template1

Advanced options

And finally, last properties of your model:

  • Order by, Order direction : The ordering of items in your model, both in frontend and in backend.

  • Public Submission : Let frontend users create entries for the model, so typically you would use that option in a model "messages" for a contact form. This is detailed here.

Models mapping

Locomotive let you define the relationships between models you are used to in Rails, but the creation and usage of these mapped models can sometimes be uneasy, so let's see for each one of them of to deal with.

This part is dedicated to the models creation and mapping in the admin UI, and the template code shown here will be very concise and simple. For more about displaying models, read the next part.

We will see the belongs to, has many and many to many relationships. Then we will look at more complex mapping, and the last subchapter presents the Ui enabled option of a field.

Belongs to

We have the model books belongs_to authors.

First, we create the model authors in its simpliest form :


The mapping with the model books will be defined in books. Let's create books:

books step 1

We give him a title field, and a writter field which defines the belongs_to relationship.

But wait, we haven't mapped books to authors yet, so click on the "add" button and then on the down arrow to specify more options concerning this field :

books arrow

You then have the option panel where you choose the Class name of the model targeted by the belongs_to relationship :

books step 2

We are done, so click on the "Create" button to save the model.

Nota Bene : the name of the field defining the belongs_to relationship (here 'writter') can be named as you want, you don't have to name it the singular of the targeted model (here it would be 'author'), even if it may be a good practice. (???)

Now we have our models defined, let's add some dummy entries. We will create a new book entrie :

books entrie empty

We can give him a name, but the writter list is empty, right, because authors model hasn't any entries yet.

So create an author :

author add

And then go back in the book creation page, the author appears in the writter list :

book entrie valid

Great, save the entrie and we will check if it works.

In a dummy page, we loop on books entries, and for each one (here the only one), we display the title of the book and it's writter :

{% for book in contents.books %}
    {{ book.title }} written by {{ }} is a great lecture.
{% endfor %}

and it displays : Responsive Web design written by Ethan Marcotte is a great lecture..

Has many

Let's keep the previous models, and let's say books have reviews. A review is basically a piece of text, and it is often published in a media. What's more, a book has many reviews, but a review refers to one and only one book. So we are indeed in the relationship books has_many reviews.

First, we create the reviews model, which has a string field journal (in which the review is published) and a text field content, and also a belongs_to field book :

book reviews

The belongs_to field targeting the parent model class name, books, is required !

book reviews belongs to field

Save the model, and now go editing books. We will add a has_many field named reviews, targeting the Class name reviews, and Inverse of itself, so books :

book editing

Nota Bene : here again, the name of the field defining the has_many relationship (here 'reviews') can be named as you want.

Save the updated books model, and let's edit the previous books entrie :

book entrie

Let's add a review to this book, click on "Add a new entry" and fill it with dummy text :

book entrie reviewed

Click on "Save" to close the modal window and create the reviews entrie related to this books entrie. Click again on "Save" to update the book.

In the previous dummy page where we tested the belongs_to relationship, we add a loop on the reviews of a book :

{% for book in contents.books %}
    {{ book.title }} written by {{ }} is a great lecture.
    Reviews :
    {% for review in %}
        Published in {{ review.journal }} : {{ review.content }}
    {% endfor %}
{% endfor %}

and it displays :

Responsive Web design written by Ethan Marcotte is a great lecture.
Published in Web design monthly :
Awesome book, blablabla ...

Many to many

Finally, we will add the ability to associate tags to a book. Here, the model tags have many books and books have many tags.

Let's create the tags model.

tags creation 1 tags creation 2

We have the books field refering to books via a many_to_many relationship. Like previously, we specify the Class name of the targeted model, which is books. We also have to specify Inverse of (itself) tags here, but we can't, the select list is empty.

For now, save the tags model, we will get back here soon. Go edit the books model and add, as you may guessed, the tags field refering to tags via a many_to_many relationship. The Class name of the targeted model is tags. Yes I'm repeating myself a little, just in case. But here you can define the Inverse of (itself) which is books, so do it please :

books m2m update

Then save the books model and go back editing tags model : magic, the Inverse of attribute of the many_to_many field books is field with the appropriate value tags :

tags inverse of update

Here it is, your many to many is settled.

Now we will add some tags to our book, but unlike the previous cases, you can't create a tag entrie in the book entrie page :

m2m enable ui problem

We have to create a new tag entrie separatelly, and then add it when editing the book entrie. So we do :

m2m tag available in book

You don't have to select here the book entrie you want to connect the tag. And when we go back to the book entrie we were editing, the tag is available in the select list :


So let's (finally !) add our tag to our book, save, and check if everything is okay back in frontend :

{% for book in contents.books %}
    {{ book.title }} written by {{ }} is a great lecture.
    Tags :
    {% for tag in book.tags %}
        "{{ tag.text }}" 
    {% endfor %}
{% endfor %}

displays :

Responsive Web design written by Ethan Marcotte is a great lecture. 
Tags : 

And if we do the opposite, it's okay too (as expected after so much pain) :

{% for tag in contents.tags %}
    Tag : "{{ tag.text }}" is related to the following books :
    {% for book in tag.books %}
        {{ book.title }} written by {{ }}
    {% endfor %}
{% endfor %}

displays :

Tag : "responsive" is related to the following books : 
Responsive Web design written by Ethan Marcotte

More complex mapping

TODO: considerations about nested relationships and performance of associated mongo queries ? ask Didier

Tip: UI enabled

When you define a field which will reference a child model, the property "Ui enable" is available to you, and 'true' by default :

tips ui enable

This property sets either you can edit and create a child model entrie directly from parent or not.

When set to true, you can for example ...

  • qd on edite, on peut directement
  • qd on cree un parent, il faut dabord le creer et ensuite ds lediting on peut creer le child >> what happens if required field ??

Rendering models

In this subchapter, we will try to show the most common cases of rendering a model entries. It would be tedious to list every possible cases, the aim is only to give an overview of what's possible.

First we will see the very basics of iterating over a collection of entries and the available logic you can add, then the pagination of results and finally the scoping the querie of results.


The simpliest loop is an iteration over your model entries. We loop here on the model posts, the one from the previous Basics subchapter:

{% for item in contents.posts %}
<p>{{ item.title }}</p>
{% endfor %}

You loop over contents.slug-of-your-model, and for each entrie you have access to the custom fields of your model, and also to a list of attributes :

entries attributes

They are listed and explained in the documentation so there is no need to detail each one of them.

Adding logic

Logic liquid tags let you put some logic inside your loops.

TODO: find relevant / interesting examples ? else, nothing to explain really ?

Rendering model's attributes

Displaing Group by

In the Presentation and Advanced options of the Basics subchapter, we saw how customize the displaying of a model's entries. One of the options is to group entries by a field, at the condition the field you want group_by entries is a select type.

In frontend, you also have this feature. Here is an example, using the posts model, the one from the previous Basics subchapter.

{% for cat in contents.posts.group_by_category %}
    {{ }} :
    {% for entrie in cat.entries %}
        {{ entrie.title }}
    {% endfor %}
{% endfor %}

The syntax is the following : contents.slug-of-your-model.group_by_field_name, with the field name corresponding to the select type field you group by entries.

As explained in the documentation : The method returns an ordered Array of Hash. Each Hash stores 2 keys, name which is the name of the option and entries which is the list of the ordered entries for the option. The Array is ordered based on the order of the options set in the back-office.

So we first loop on each value of the select type field, wich is "cat", and then for each "cat" we loop on each entries of this category.

Paginate entries

Locomotive comes with a paginate tag.

{% paginate contents.posts by 3 %}
    {% for post in paginate.collection %}
        <p>{{ post.title }}</p>
    {% endfor %}
{% endpaginate %}

It creates a paginate objects with the following attributes :

paginate attributes

There are all pretty straighforward, but let's have a look at the parts attributes, it seems there is everything here to build the navigation of your paginated pages.

Here is an example of how we would to it :

{% paginate contents.posts by 1 %}
    {% for post in paginate.collection %}
        <p>{{ post.title }}</p>
    {% endfor %}

    <!-- pagination links -->
    {% for page in %}
        {% if page.is_link %}
            <a href="{{ page.url }}" >{{ page.title }}</a>
        {% else %}
            {{ page.title }}
        {% endif %}
    {% endfor %}
{% endpaginate %}

Well, that's fine if you want have the control of your markup, but if you don't, there is the filter default_pagination for rendering a clean pagination navigation without pain :

{% paginate contents.posts by 1 %}
    {% for post in paginate.collection %}
        <p>{{ post.title }}</p>
    {% endfor %}
    <!-- pagination links -->
    {{ paginate | default_pagination, next: 'Next', previous: 'Previous' }}
{% endpaginate %}

Which takes 2 arguments :

paginate filter

And which renders this kind of markup :

<div class="pagination ">
    <span class="disabled prev_page">« Previous</span>
    <span class="current">1</span>
    <a href="/posts?page=2">2</a>
    <a href="/posts?page=3">3</a>
    <a href="/posts?page=2" class="next_page">Next »</a>

Scope results

{% with_scope _slug: params.section %} {% assigns section = contents.sections.first %} {% endwith_scope %}

{% for article in section.articles %} ... {% endfor %}

with_scope: replace _permalink by _slug in the query

Templatize a model

The idea of a templatized page is that's a view of one instance of a model you specify in the templatized option of a page.

Here is how it works : let's say you have the model posts, the one from the previous Basics subchapter. You need to have the following pages structure :

templatized page archi

With :

  • posts page :

    The templatized mechanism expects to have "models" under a parent folder which makes more sense for SEO purpose. This page can be used for instance to list the products, or could be a redirect page.

    So for the example, here are the parameters of the page, but there isn't anything specific here :

    templatized page posts

    templatized page posts 2

    Again for the example, we could list in this page the entries of posts model, and display the link of each entrie. You have to build the relative url according this page's slug, and using the _permalink attribute of a model instance.

    templatized page posts liquid

  • template page :

    The template of the templatized model is defined as follow :

    templatized page 1

    You set the page posts as the parent of this one. A templatized page must have a parent, other than index.

    templatized page 2

    Set the parameter Templatized as true, and select bellow the model you want templatize.

    To follow the example, we will display a full post, using directly the posts instance (notice the singular) :

    templatized page template liquid

Note: in Locomotive 2.0, you can't have multiple nested levels of templatized pages. It will be possible with the 2.1 version, if you need this feature now, have a look at this branch and this discussion.

Recipe : Public Submission

//// Very big form, session problem hack :

Locomotive Editor

lien: Carrier Wave seems to be locking down ThemeAsset to only allow images!topic/locomotivecms/r7f-54gSg0U

Using Locomotive in an existing Rails app

sources :


Using multi-sites

notes :

Well, each site is fully independent form the others: they have different pages, domains, content types, ...etc. The ONLY part they have in common is that they have a "administration" access point based on a common domain name as explained in the guide ( but since we can use domain aliases, it's not a problem at all.

dev locally :

Export site

source: [](

In Locomotive 1.0, there was an export feature, which allowed to export the site (template, models, entries) in a zip.

This is no longer the case in Locomotive 2.0, since it now uses a REST API for push & pull commands.

Pushing a site (a Locomotive Editor one) is described in this section and here.

For now, there isn't any script for pulling a Locomotive app, but it's possible though, following these steps :

  1. Get an auth token
curl -d '' ''

Obviously change the email and password to be valid credientials.

Response will be something like

  1. Use the token to get the information you need.

Pages :

curl ''

Content Types :

curl ''

Content Entries ( assume we have a "Projects" model ) :

curl ''


localised snippets / pages

Customise TinyMCE

source: [](

Within your main app,

  1. create a partial at the "app/views/locomotive/shared/_main_app_head.html.haml" location note: create the folders if they don't exist

  2. edit the _main_app_head.html.haml file and insert this:

= javascript_include_tag 'locomotive_misc'

  1. create a javascript file at the "app/assets/javascripts/" and fill in with

window.Locomotive.tinyMCE.defaultSettings.valid_elements = ""

Note: basically, you can modify all the default tinymce settings for Locomotive. Take a look at this file if you need to check them:

Writing custom Liquid tags


Locomotive API

notes récupérées du google group :

I looked at the api source and it appears currently you can only query the entire model using it's slug which returns all entries in the model

Something went wrong with that request. Please try again.