Before you can build an Ember app to embedded in Drupal 7, you’ll need to get Drupal running “headlessly” i.e. outputting data via an API instead of rendering HTML through a theme. I’ve written a tutorial to demonstrate how to set this up: Exposing Drupal 7 data in a custom RESTful API with the RESTful module.

There are a number of ways you can run Ember and other JS frameworks alongside headless Drupal, for example as standalone apps connected to Drupal only by the API. In this tutorial we’ll be embedding an Ember app in a Drupal page. This has a number of advantages e.g. allowing your app to utilise Drupal’s Permission API and menu system.

1. Create a custom Drupal module

We’ll be making an application in this tutorial called Animal World. to display different animals. Our custom module will be called animal_world

2. Create a new Ember project within your custom module

For this tutorial you’ll need to install the Ember-CLI. Once installed, navigate to the animal_world folder and create a new Ember project:

animal_world$ ember new ember-app

You can check your Ember app has installed:

animal_world$ cd ember-app
animal_world/ember-app$ ember serve

Navigate to http://localhost:4200 and you’ll see it running on a Node server.

3. Get Drupal to load your Ember app

We need to get Drupal to load the Ember app when the user navigates to a certain path. (If you want your app to run standalone, you can skip this step). Let’s launch the app when users visits http://animals.dev/animal-world. Add this code to your custom module:

animal_world/animal_world.module

define('ANIMAL_WORLD_URL', 'animal-world');
define('ANIMAL_WORLD_APP', 'ember-app');
 
/**
 * Implements hook_menu().
 */
function animal_world_menu() {
 
  $items[ANIMAL_WORLD_URL] = array(
    'title'             => 'Animal World',
    'access callback'   => TRUE,
    'page callback'     => 'animal_world_page',
    'delivery callback' => 'animal_world_delivery',
    'type'              => MENU_CALLBACK
  );
 
  return $items;
}
 
/**
 * The page callback function. Loads the Ember app
 */
function animal_world_page() {
 
 $app = ANIMAL_WORLD_APP
 $path = drupal_get_path('module', 'animal_world') . '/' . $app;
 
 drupal_add_js("{$path}/dist/assets/vendor.js");
 drupal_add_js("{$path}/dist/assets/{$app}.js");
 drupal_add_css("{$path}/assets/vendor.css");
 drupal_add_css("{$path}/assets/{$app}.css");
 
 return theme('animal_world_ember');
}
 
/**
 * Delivery callback used to override html.tpl.php from the module
 *
 * @param $page_callback_result
 */
function animal_world_delivery($page_callback_result) {
 
  global $language;
 
  // Pass variables to the template.
  $vars = array(
    'language'    => $language,
    'head_title'  => drupal_get_title(),
    'favicon'     => '',
    'styles'      => drupal_get_css(),
    'scripts'     => drupal_get_js(),
    'messages'    => drupal_get_messages(),
    'content'     => $page_callback_result,
    'ember_base'  => url(ANIMAL_WORLD_URL, array(
      'absolute' => TRUE,
    )) . '/',
  );
 
  echo theme('animal_world_html', $vars);
 
}

We need to add a bit more code, but let’s first look at what the above does:

  • The hook_menu implementation tells Drupal what to do when a user visits path /animal-world.
  • The page callback function loads the files needed for your Ember app. Keep in mind that as you create your app, Ember will build it into a single .js and .css file. The page callback loads those, as well as the vendor .js and .css files.
  • The delivery callback simply wraps your ember app in a Drupal theme file. By default it would just be wrapped inside your theme html.tpl.php file. If you’re okay with that, you don’t need a delivery callback. I want a custom template though so I don’t get all the navigation, footer etc from my theme. We pass the theme the obvious parameters, but I’m also passing ’ember_base’ which will be explained below.

Both callbacks are calling the theme function, so let’s now add a hook implementation for that:

animal_world/animal_world.module

/**
 * Implements hook_theme()
 *
 * @return mixed
 */
function animal_world_theme() {
 
  $theme['animal_world_embed'] = array(
    'template'    => 'animal-world-embed',
    'path'        => drupal_get_path('module', 'animal_world') . '/templates'
  );
 
  $theme['animal_world_html'] = array(
    'template'    => 'animal-world-html',
    'path'        => drupal_get_path('module', 'animal_world') . '/templates',
    'variables'   => array(
      'language'    => NULL,
      'head_title'  => NULL,
      'favicon'     => NULL,
      'styles'      => NULL,
      'scripts'     => NULL,
      'messages'    => NULL,
      'content'     => NULL,
      'ember_base'  => NULL
    ),
  );
 
  return $theme;
}

Then create a templates directory in your module and add these files:

animal_world/templates/animal-world-embed.tpl.php

<div id="ember-app"></div>

animal_world/templates/animal-world-html.tpl.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN"
  "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print $language->language; ?>" version="XHTML+RDFa 1.0">
 
<head>
  <title><?php print $head_title; ?></title>
  <base href="<?php print $ember_base; ?>" />
  <?php print $styles; ?>
  <?php print $scripts; ?>
</head>
<body>
<?php
if (!empty($page['message'])):
  foreach($page['message'] as $type => $message):
    ?>
    <div class="messages <?php print $type; ?>">
      <ul>
        <?php foreach($message as $msg): ?>
          <li><?php print $msg; ?></li>
        <?php endforeach; ?>
      </ul>
    </div>
    <?php
  endforeach;
endif; ?>
 
<?php print $content; ?>
 
</body>
</html>

What you need to know about the above code is:

  • The animal_world_embed template is used to embed the Ember app into the page. We’ll set this up from within the Ember app as well.
  • The animal_world_html template is simply a replacement of the default html template for the theme. It’s important that you add a tag in the head and set the base URL of the Ember app, otherwise Ember may have trouble dealing with routes.

4. Configure Ember

Let’s switch over to EmberJS now. By default, Ember will attach the application template to the <body> tag of an HTML page. We’re instead going to specify a different root element property:

animal_world/ember_app/app.js

App = Ember.Application.create({
  rootElement: '#ember-app'
});

Now when Ember fires up, it will know to embed output in the template file animal-world-embed.tpl.php

Go to http://animals.dev/animal-world and you’ll see the Ember app as loaded by Drupal!

5. Load and display Drupal content in Ember

Getting Drupal to load the Ember app is the first bit, now we want Ember to grab data from Drupal’s API and display it.

Let’s set up a very simple app that will do nothing more than display some Drupal content type to the screen.

Firstly create a route that we’ll call main:

animal_world/ember-app$ ember generate route main

Now set it’s path. We want the main route to be used immediately when the app is launched.

animal_world/ember-app/app/router.js

Router.map(function() {
  this.route('main', {path: '/'});
});

In the tutorial I wrote on setting up a Drupal web service, I demonstrated with a content type called dogs. Let’s use that content type again. The API method and it’s output are below:

http://animals.dev/api/v1.0/dogs/1

{
  "dog": [
    {
      "id": 1,
      "label": "Rusty",
      "breed": "Kelpie",
      "age": 6
    }
  ]
}

We’ll now load this content type into an Ember model:

animal_world/ember-app$ ember generate model dogs

animal_world/ember-app/app/models/dogs.js

export default DS.Model.extend({
    label:          DS.attr(),
    breed:          DS.attr(),
    age:            DS.attr(),
});

As long as the JSON object output by the API is the same as what I’ve specified above, Ember will automatically load the JSON data into the model we created. But there is one additional configuration we have to make: we need to set the API endpoint namespace in a custom adapter.

animal_world/ember-app$ ember generate adapter application

animal_world/ember-app/app/adapters/application.js

export default DS.RESTAdapter.extend({
    namespace:      'api/v1.0',
});

With all the above done, Ember will be correctly loading the models from Drupal’s API. The last thing to do is add to our main template so that Ember will display the dogs content to screen

animal_world/ember-app/app/templates/main.hbs

<h2>Dogs</h2>

    <li>
        <h3></h3>
        <ul>
            <li>}</li>
            <li></li>
        </ul>
    </li>

Now when we visit our animal world app we’ll see a simple representation of our dogs content type.


You can use the RESTful module to create a custom web service that exposes data from your Drupal 7 instance. For example, if you have a content type called “dogs” with fields “label”, “age”, “breed”, you could have instances of the dogs content type available as a JSON object at an endpoint like:

http://animals.dev/api/v1.0/dogs/1

{
  "dog": [
    {
      "id": 1,
      "label": "Rusty",
      "breed": "Kelpie",
      "age": 6
    }
  ]
}

The RESTful module can expose data from many data types in Drupal including taxonomies, nodes etc.

Let’s assume we have the dogs content type setup in Drupal. How do we set up a web service?

1. Install the Restful module

https://github.com/RESTful-Drupal/restful

2. Create a custom module

e.g. animals_api, with these files:

animals_api/animals_api.info

name = Animals API
description = RESTful API for animals based on restful module
core = 7.x
dependencies[] = restful

animals_api/animals_api.module

<?php
/**
* Implements hook_ctools_plugin_directory(). This hook is used to inform the
* CTools plugin system about the location of a directory that should be
* searched for files containing plugins of a particular type.
*/
function restful_custom_ctools_plugin_directory($module, $plugin) {
  if ($module == 'restful') {
    return 'plugins/' . $plugin;
  }
}

3. Declare a rest method

Create a plugins directory and a restful directory inside that:

animals_api/plugins animals_api/plugins/restful

To declare the dogs method, we create a file called dogs.inc

animals_api/plugins/restful/dogs.inc

<?php
$plugin = array(
 'label' => t('Dogs'),
 'resource' => 'dogs',
 'name' => 'dogs',
 'entity_type' => 'node',
 'bundle' => 'dogs',
 'description' => t('Help Me Choose answers (aka features)'),
 'class' => 'RestfulEntityBaseNode',
);

Note:

  • The resource ‘dogs’ determines the root URL of the method
  • The name ‘dogs’ must match the name of the file
  • The bundle ‘dogs’ is the machine name of the content type in Drupal (if it were a taxonomy you wanted to expose etc, you’d use the machine name of the vocabulary etc)
  • ‘class’ (and ‘formatter’, not yet shown) will be explained below…

With that, we should now be able to access:

http://animals.com/api/v1.0/dogs/

And we’d if we’ve added one instance of dogs we’ll see:

{
   "count": 1,
   "self": {
     "title": "Self",
     "href": "http://animals.dev/api/v1.0/dogs"
   },
   "data": [
     {
       "id": 1,
       "label": "Rusty"
     }
   ]
 }

And to view entity with id 1:

http://animals.com/api/v1.0/dogs/1

{
  "self": {
    "title": "Self",
    "href": "http://animals.dev/api/v1.0/dogs/1"
  },
  "data": [
    {
      "id": 1,
      "label": "Rusty"
    }
  ]
}

4. Customise the fields

By default, you’ll only see the label field of each entity in the output. To do add additional fields, you need to extend one of the bases classes supplied by RESTful.

In our dogs.inc file we used the RestfulEntityBaseNode class to define output. We’ll change that to our own custom class RestfulDogs:

animals_api/plugins/restful/RestfulDogs.class.php

class RestfulDogs extends RestfulEntityBaseNode {
 
  /**
   * Overrides RestfulEntityBaseNode::publicFieldsInfo().
   */
  public function publicFieldsInfo() {
    $public_fields = parent::publicFieldsInfo();
 
    $public_fields['age'] = array(
     'property' => 'field_age',
     'sub_property' => 'value',
    );
    $public_fields['breed'] = array(
     'property'          => 'field_breed',
     'process_callbacks' => array(
       array($this, 'process_breed')
     )
    );
    return $public_fields;
  }
 
   /**
    * Callback to return the breed taxonomy name
    */
   public function process_breed($tax) {
     if (!empty($tax->tid)) {
       $tax_obj = taxonomy_term_load($tax->tid)
       if ($tax_obj) {
         return $tax_obj->name;
       }
       else {
    return false;
       }
    }
    return null;
  }
}

So what’s going on here? We need to override publicFieldsInfo() to be able to specify what fields we want to expose in the web service. This function returns an array of fields which we can simply add to.

Notes:

  • The ‘property’ property is where you put the machine name of the field in your entity that you want to expose
  • The ‘sub_property’ property is the child element that contains the actual value of the field you want to expose
  • The ‘process_callbacks’ property is where you can list one or more functions to transform your field before it’s rendered. In the example above, I’ve processed the field ‘breed’. Let’s assume that field is a term reference and will only render a tid in the API output. We want the taxonomy name to display instead. The callback will actually wrap the tid in an entity wrapper, so the callback function ‘process_breed’ receives a taxonomy object as an argument. Pretty cool.

Now the output will include the additional fields:

http://animals.dev/api/v1.0/dogs/1

{
 "self": {
    "title": "Self",
    "href": "http://animals.dev/api/v1.0/dogs/1"
  },
  "data": [
    {
      "id": 1,
      "label": "Rusty",
      "breed": "Kelpie",
      "age": 6
    }
  ]
}

5. Format the output

Finally, let’s transform the output a bit. I want to use this API as store for an EmberJS application, so I need my JSON object to look a little different.

We can extend the formatter class to achieve this. Firstly create another directory:

animals_api/plugins/formatter/

Declare a new formatter:

animals_api/plugins/formatter/custom_json.inc

<?php 
 
$plugin = array(
 'label' => t('Custom JSON Formatter'),
 'description' => t('Defining a custom formatter to allow ntegration with Ember'),
 'name' => 'custom_json',
 'class' => 'RestfulCustomFormatterJson',
);

Extend one of the formatter classes:

animals_api/plugins/formatter/RestfulCustomFormatterJson.class.php

class RestfulCustomFormatterJson extends \RestfulFormatterJson implements \RestfulFormatterInterface {
 
 /**
  * {@inheritdoc}
  */
 public function prepare(array $data) {
 
   $output = parent::prepare($data);
 
   $key = 'data';
 
   switch ($this->handler->getBundle()) {
     case 'dogs':
       $key = dog';
       break;
   }
 
   $output[$key] = $output['data'];
   unset($output['data']);
 
   if (isset($output['count'])) {
     unset($output['count']);
   }
   if (isset($output['self'])) {
     unset($output['self']);
   }
 
   return $output;
 }
}

What’s this code do?

  • Firstly, I want the root of the JSON object to be ‘dog’ not ‘data’. To do this, I’ve created a switch based on the bundle name. We only have dogs in our api, but if we have cats, ferrets etc, we want a root that reflects those content types too.
  • Secondly, I don’t want the ‘count’ and ‘self’ data in my JSON object, so I’ve simply unset those.

The final step is to apply this formatter to the dogs method. In our dogs.inc file we can include another property in the plugin array:

'formatter' => 'custom_json'

Now we get this as our output. Notice the JSON root has changed and the default ‘count’ and ‘self’ properties have disappeared:

http://animals.dev/api/v1.0/dogs/1

{
  "dog": [
    {
      "id": 1,
      "label": "Rusty",
      "breed": "Kelpie",
      "age": 6
    }
  ]
}

I’m planning a follow-up post for integrating EmberJS with Drupal via the RESTful module.


When Drupal is running headlessly it’s theme is missing and instead the user is interacting with another application that is likely sending and receiving data to Drupal via an API.

The advantages of doing this can be presented from two perspectives:

  1. I want to use Drupal for my web app. Why go “headless” and use a front-end framework? Standard Drupal 7 is fine for simple, form-based apps. You get jQuery and jQuery UI out of the box, so form validation, conditional fields etc aren’t too much hassle. But Drupal (or any CMS for that matter) is really not up to the task of building a highly interactive web app.
  2. I want use a front-end application framework. Why build it on top of Drupal? CMS’s are great for managing complex data types including storage, retrieval, permission-based access etc. A CMS will also have an UI which will be handy for administrating your app. Drupal just happens to be particularly good at all these things. It’s APIs, hook system and thousands of modules make it the superior choice.

To build a web app with a JS framework on top of Drupal 7, you need to do the following:

  1. Get Drupal to function headlessly. This can be done by providing an API for your Drupal instance that allows another application to access the content. I’ve written a tutorial on how to do that with the RESTful module: Exposing Drupal 7 data in a custom RESTful API with the RESTful module
  2. Get your front-end app to talk to Drupal. If Drupal is exposing data via an API this isn’t too hard to achieve. I’ve also written a tutorial on getting an EmberJS app to work from inside Drupal: Embedding An EmberJS App In Drupal 7

If you want to serve retina images with WordPress plugin WP Retina 2x but you’re using bfi_thumb, you’re going to have a slight problem.

bfi_thumb creates new file names that are not picked up by WP Retina 2x. For example, you may have an image file called your_image.jpg and WP Retina 2x may have prepared another called your_image@2x.jpg to serve on retina devices. But when you bring bfi_thumb to the party as well, it’ll create it’s own file e.g. your_image_resized.jpg. When your page is loading WP Retina 2x sees your_image_resized.jpg, it doesn’t have a retina-equivalent prepared.

I overcame this on my site by making a modification to bfi_thumb so it creates two resized thumbnails: your_image_resized.jpg and your_image_resized@2x.jpg. You can check out the code on Github.


If you’re using AWS Route 53 to manage DNS records, it’s a good idea to backup in case of accidental deletion and other such misfortunes.

Of course you’ll want it automated so here’s a way to do with cron on a linux system:

  1. Install the AWS Command Line Interface. This tool allows you to administer your various AWS services via the command line.
  2. Install cli53. This tool extends the AWS CLI by offering more high-level commands for easy Route 53 administration
  3. Once you have those setup, the following command will export a zone record to a file:

    $ cli53 export example.com --file example.com.bk
    
  4. You need to specify what domain you want the zone record for, there’s no “all” option. So, you could go ahead an run the command repeatedly for all your domains, but who wants to do that? To do it programmatically, this following command will get the list of domains, iterate through them, and export each one, piping the result to a separate file:

    cli53 list | grep 'Name:*' | cut -f6- -d' ' | while read line; do cli53 export ${line} >> ~/backup/${line}bk; done
    
  5. To have this happen automatically, you can simply create a bash script and have cron run it once per day or whatever you like:

    #!/bin/bash
    cli53 list | grep 'Name:*' | cut -f6- -d' ' | 
    while read line; 
    do
        cli53 export ${line} >> ~/backup/${line}bk; 
    done
    

    Note: the cli53 command won’t work in a bash script unless you provide the full path or add to the $PATH variable e.g /usr/local/bin/cli53

  6. Save it, let’s say to /path/to/script.sh, make that file executeable, and add it to cron:

    $ crontab -e
    

    Add this to the bottom of the file to run the script once per day:

    00 00 * * * sh /path/to/script.sh
    

Note that your backups will be overwritten each time the script runs, so you might add a date to the file name to create daily snapshots.Or better still: why not upload your backup files to a versioned S3 bucket for safe storage? That also be done with AWS CLI, here’s a modified version of the bash script to do just that:

    #!/bin/bash
    cli53 list | grep 'Name:*' | cut -f6- -d' ' | 
    while read line; 
    do
        cli53 export ${line} > ~/backup/${line}bk;
        aws s3 cp ~/backup/${line}bk s3://mybucket;
        rm ~/backup/${line}bk;
    done