Building a Drupal CMS to manage content on iOS - Part 1 (the Drupal bit)

This series introduces a Content Capture system that allows content for (in this case) iOS applications to be captured, managed and deployed through Drupal. The source code for this project can be found at https://github.com/CMDT/ContentCaptureExample. This is a minimum approach emphasising the fundamental features needed to get up and running. This means that it can be extended in many ways to fit a number of desired needs.

The approach taken to creating this project is fairly flexible, you do not need to be a developer to create new content types or to update the content in an application. Users of the iOS application can view it's content offline, as an internet connection is only required to download new or updated content. Some effort has been made to reduce the fragility of the system and the backend can be modified independently of the front end.

This series has two parts:

  • Part 1: The Drupal side of the system will be discussed, creating a new content type and capturing data to be delivered to the mobile app.
  • Part 2: The iOS side of the system will be discussed, checking for updated content, consuming and displaying it.

This article, Part 1, provides an overview of Drupal in terms of capturing content and introduces two important Drupal modules to be able to do this. We created one of the modules, Content Packager, therefore a walkthrough of how to use this module is included. A breakdown of this module’s code is introduced for those interested, as this code does not need to be understood in order to use Drupal to capture content.

Getting Started with Drupal

To begin you need an installation of Drupal, which can be downloaded at https://drupal.org/download. If you have not used Drupal before there are a number of good articles and tutorials explaining Drupal. For an installation guide I would start here https://drupal.org/documentation/install.

Drupal is being used to capture content as it allows us to create our own content types. A standard piece of content is called a Node in Drupal. We can create new content types to store data about different items and these all derive from Node. For this system we have created a content type called City which has the fields; name, body and image, as shown below.

The machine names are important later on for both the code for the custom Drupal module and the mobile application.

Introducing the Drupal Nodequeue Module

You will need to install and enable the nodequeue module for Drupal, as it is a prerequisite for the Drupal custom module, Content Packager. It can be found at https://drupal.org/project/nodequeue.

A nodequeue is a group of content collected in an ordered list. So, a nodequeue can store a collection of nodes, including instances of our content types. The nodequeue module allows for content to be added to a nodequeue, content to be removed from a nodequeue and the content within a nodequeue to be reordered. The reason we are using the nodequeue module is so we can create different nodequeues for different content. This allows us to manage the content which is downloaded by the iOS app. For example, if there was a change to only one piece of content the app will only download the nodequeue it belongs to, rather than re-downloading all the content in the app. As a note, each nodequeue can contain a mixture of content types. However, for our Cities example we are only including the content type City in the Cities nodequeue.

Create a nodequeue called Cities, with the following settings.

By ticking only the City box under Types we can control the content allowed to be added to the nodequeue.

Now you are ready to add some content. One of the Cities I created is shown below.

Create some Cities and add them to the Cities nodequeue.

Drupal Custom Module Content Packager

The custom module created for this system is called Content Packager and is available at: http://digitallabsmmu.com/download/content_packager_v1.0.zip. You will need to download, install and enable this module.

The purpose of this module is to deliver each nodequeue (group) of content as a downloadable zip. It provides a page to manage updates to the groups of content. This can be accessed through the left-hand side navigation bar using the Update Content link.

The Update Content page displays all the nodequeues, allowing them to be sorted, viewed or updated. Once new content has been added to a nodequeue or existing content has been either modified or deleted, the nodequeue can be updated via this page. Updating the nodequeue creates a new downloadable zip for this group of content and stores a version number for the nodequeue. This version number is used by the iOS application.

The Cities nodequeue can now be updated to create a downloadable zip as shown below.

Using this module with the update link allows you to control when you want new content to be available on the mobile app. You can make changes to the nodequeue whenever you want but these will only be available to the app when that nodequeues update link is clicked.

An Overview of the Code behind the Content Packager Module

To use the Content Packager module for Drupal you do not need to understand the code, as the module provides the functionality needed to package groups of content into zipped files ready to be used in an app. However, for those of you interested in the code this section provides an overview! As a reminder this module provides the basic functionality required - there are many ways that it can be extended!

Drupal requires custom modules to be written in PHP and provides an extensive API for use when creating a custom module. Drupal’s API can be found at https://api.drupal.org/api/drupal. Custom modules can extend Drupal using Drupal’s “hooks”. A hook is a PHP function that a custom module needs to implement in order to ‘hook’ into Drupal. Content Packager uses both Drupal’s API and the nodequeue module’s API.

Content Packager copied and modified a small part of the existing code from the nodequeue module to display the table on the Update Content page. It displays the table seen above and uses Drupal’s Menu system to call the function to create the zip file, update_nodequeue_content. Drupal’s menu system defines a path to this function as follows:

  $items['content/packager'] = array(
    'page callback' => 'update_nodequeue_content',
    //permissions required to view page
    'access arguments' => array('content packager'),
    'type' => MENU_CALLBACK,
);

This means that when an Update link is clicked this update_nodequeue_content function is called by accessing the URL defined above. The code calling this function via the Drupal menu system is highlighted below.

  l('Update', 'content/packager/'. $queue->$qid)

The l function creates a link with the first parameter, and the second parameter is the URL to access when the link is clicked. This second parameter is using the Drupal menu system. The first part of this parameter, content/packager, is the part defined in the $items array from above. The second part, .$queue->qid, is the parameter needed to pass to the update_nodequeue_content method, which is the nodequeue id.

For example, with our Drupal Cities website it would be http://contentcaptureexample.digitallabsmmu.com/content/packager/1, with 1 being the nodequeue id. This menu item has access arguments specified which would require someone with the required privileges to be logged in to avoid the 'Access denied' message.

... 'access arguments' => array('content packager'),

The update_nodequeue_content function uses a function from the nodequeue module’s API to retrieve all the nodes in a given nodequeue. These nodes are then converted into JSON. The JSON is added to a zip folder in a file called manifest.json. Images are also added to the zip file. This is done by looping through the nodes in the nodequeue and searching for a field that begins with the field_image. If a field_image is found the image’s uri is retrieved and used to add the image to the zip file. This code is shown below:

  //write the manifest directly from the $json string
  $zip->addFromString('manifest.json', $json);
 
    //check for images in each node within the nodequeue
    foreach ($nodes as $node) {
 
      //get the nodes keys to check if they are images
      foreach ($node as $key => $value) {
 
        //check if there is a key that begins with field_image and it is not empty
        //checks the string for the reg expression but checks that the array is not null
        if(preg_match('/^field_image/', $key) && $node->$key != NULL) {
 
          //save the array ready to access its elements
          $image = $node->$key;
 
          //get the image's uri
          $image_uri = $image['und'1['0']['uri'];
 
          //add the image to the zip file and name it its existing filename
          $zip—>addFile(drupal_realpath($image_uri), $image['und'1['0']['filename']);
        }
      }
  }

As this code for retrieving images uses field_image, a naming convention should be used when adding fields for images, during the process of creating a new content type. This naming convention involves always using the prefix field_image. In this Cities example, when I created the Image field I used the name field_image_city. This was entered for the Machine Name, when creating the Drupal content type.

Once the zip file has been created certain details need to be added into the Content Packager module’s database. These details include the version number of the nodequeue and the path to the zip file that was created. The database schema is created when the Content Packager module is installed. This is done by implementing Drupal’s hook_schema function in the content_packager.install file.

A function called get_version_path was created within the Content Packager module. This function retrieves a particular nodequeue’s path to its packaged content and its version number, from the Content Packager module’s database table. This function is the function that will be accessed from the iOS application. It will be accessed using Drupal’s menu system, which means it can be accessed via a URL. The following item in the menu hook of our custom module points 'contentpackagerjson' in the URL towards the php function get_version_path.

   $items['contentpackagerjson'] = array(
      'access callback' => true,
    'page callback' => 'get_version_path',
      'delivery callback' => 'drupal_json_output',
   );

So for a node queue with an ID of '1' set up we can access the latest version number and a path to download the zip package as follows;

http://contentcaptureexample.digitallabsmmu.com/contentpackagerjson/1

e.g running this I got the following JSON response;

{
  "file_path" : "http:\/\/contentcaptureexample.digitallabsmmu.com\/sites\/default\/files\/download_1403791940.zip",
  "version" : "5"
}

Summary

To recap, you will have:

  • Downloaded and installed Drupal
  • Installed and enabled both the Nodequeue module and the Content Packager module
  • Created your own content type - City
  • Created some cities using the new content type
  • Created a Cities nodequeue
  • Added your cities to the Cities nodequeue
  • Used Content Packager to package the Cities nodequeue content into a zipped folder
  • Created a URL to access the latest version details and path to a downloadable zip for a NodeQueue.

Next, the iOS application will need to be created to pull down and utilise this packaged content from Drupal. This tutorial is coming soon.