Mar 19 2012
Mar 19

Eight years ago, I stepped out of the corporate world and started Treehouse Agency, envisioning a model of web development centered around the client relationship. Over the next couple of years, others joined me as not only employees, but also as friends. With these friends, we grew from a couple of developers building websites to a 15-person agency building some of today’s biggest and most sophisticated Drupal implementations.

Today, it is with great excitement that I tell you we will continue our tradition of growing with friends–many, many more of them. As of today, Treehouse Agency is merging with Phase2 Technology, an agency that shares our dedication to top-tier open source platform development in the government, publishing, and non-profit sectors.

In recent years we have found ourselves working in friendly competition with each other, developing parallel reputations and complementary skillsets. Treehouse and Phase2 are a very natural fit for each other in terms of our skills, values, culture and client verticals.

In the coming weeks, our companies will integrate our operations, and the combined company will carry the Phase2 Technology name; I will join Phase2 as President, overseeing financial and human capital, including talent and culture development.

I look forward to the many exciting opportunities this merger brings to the table. We look forward to offering a more comprehensive selection of services to clients, growing thought leadership, and strengthening contrib projects including modules, core development, and Phase2′s collection of distributions.

I never imagined when I started Treehouse that it would grow into what it has become today; to be joining forces with a company like Phase2–which I and many of my colleagues have grown to respect so much–is amazing to me.

Thank you to all of those who have supported Treehouse over the years: clients, colleagues, and of course the amazing team that make us what we are today.

It’s been an incredible journey so far, and we are just getting started.

For the official details, you can read the press release here. At DrupalCon? Swing by booth #419 and say hello!!!
Mar 14 2012
Mar 14

When we last discussed State Machine, we highlighted how easy it was for developers to create custom workflows via the State Machine API.

The goal of State Machine is to provide an API first approach to workflows within Drupal. A simple user interface is included, but the developer ultimately has power and flexibility to extend and customize their workflows across various environments as they see fit.

We also reviewed State Flow, a sub-module packaged with State Machine which provides you with a base workflow, and showed how Energy.gov extends State Flow for its own custom needs.

We’re happy to share that State Machine has released a 2.x branch sporting some great new features to make your workflows even better.

Easier to Alter

The method for implementing a custom workflow changed from using variable_set() for defining the node types that should implement your custom workflow to using hook_state_flow_machine_type_alter(). Using Energy.gov as an example, we simply assign the machine type to match the key we declared in hook_state_flow_plugins().

/**
 * Implements hook_state_flow_plugins().
 */

function

energy_workflow_state_flow_plugins

() {
$info = array();
$path =

drupal_get_path

(‘module’, ‘energy_workflow’) . ‘/plugins’;
$info[‘energy_workflow’] = array(
‘handler’ => array(
‘parent’ => ‘state_flow’,
‘class’ => ‘EnergyWorkflow’,
‘file’ => ‘energy_workflow.inc’,
‘path’ => $path,
),
);
return $info;
} /**
 * Implements hook_state_flow_machine_type_alter()
 *
 * @param string $machine_type
 * @param object $node
 */

function

energy_workflow_state_flow_machine_type_alter

(&$machine_type, $node) {
$machine_type = ‘energy_workflow’;
}

You can also easily have multiple workflows by adding logic to change the machine type based on node type, instead of having to declare them as variables.

Bulk Revision Editing

A new administration page puts more power in users hands by allowing publishers to use your defined states and events on many items at once.

Similar to the philosophy of Views Bulk Operations, this is a neat feature for users who may want to fire events across multiple revisions.

For example, if you have micro-sites or a group of pages that all need to be launched simultaneously, State Flow includes a set of batch operations to publish those node revisions at once.

Revisions are administered by filters and operations. The filters dictate which revisions are selectable by administrators when they are applied. The operations serve as actions to perform on the revisions once they are selected.

For developers, these filters and operations are completely alterable and are packaged with their own hooks! This provides an opportunity to create unique bulk administration pages for your users.

Want to create a custom filter? Simply invoke hook_node_revision_filters() and add your filter to the form array. You can then invoke hook_query_node_revision_alter() to enact whatever logic you want for that custom filter.

Use cases include filters for revisions by taxonomy, the existence of certain field value, or even user access.

Here is an example of a tags filter and its query callback:

/**
 * Implements hook_node_revision_filters()
 */

function

mymodule_node_revision_filters

() {
$filters = array();

  //function to get tags
  $options = mymodule_tags_callback();

  $filters[‘mymodule_tags’] = array(
    ‘form’ => array(
      ‘#type’ => ‘select’,
      ‘#title’ => t(‘Content Tag’),
      ‘#options’ => $options,
    ),
  );

  return $filters;
}

/**
 * Implements hook_query_node_revision_alter()
 */

function

mymodule_query_node_revision_alter

(

QueryAlterableInterface

$query) {
// Get the filter form the session
if ($filters = $query->getMetaData(‘filters’)) {
if ($filter = isset($filters[‘elc_workflow_tags’])

?

$filters[‘mymodule_tags’] : NULL) {
/**
        * Custom code to join other tables
        */

}
}
}

This also true for custom operations. Utilizing hook_node_revision_operations(), we can easily create custom actions, in bulk, with the revisions exposed in this administration page.

Scheduling

Last, but not least, State Machine 2.x introduces workflow scheduling to allow users to set a day and time via the Date Popup module of when a node should transition to a defined state. This is done via a new submodule called State Flow Schedule.

State Flow Schedule extends State Flow to provide you with a default Scheduled state and Schedule event. Once a date and time is defined, the value is passed as a log message so users can easily view the workflow log of a node and check when it will transition.

Scheduling is heavily tied to cron. This means content won’t actually transition to the desired state until cron runs, so you should setup your environment accordingly.

Scheduling can also be extended. In Energy.gov, we had an additional event called Immediately Schedule to allow those with the appropriate permissions to skip the workflow for content starting in the draft state.

To utilize this, simply define the new event, then add it to array of definable scheduling events via hook_state_flow_schedule_events_alter().

/**
 * @file
 * Energy.gov implementation of State Flow, an extension of the State Machine class
 */

class EnergyWorkflow extends StateFlow {

  public function init() {
    // Initialize states
    $this->create_state(‘draft’, array(
      ‘label’ => t(‘Draft’),
    ));
    $this->create_state(‘needs review’, array(
      ‘label’ => t(‘Needs Review’),
    ));
    $this->create_state(‘approved’, array(
      ‘label’ => t(‘Approved’),
    ));
    $this->create_state(‘unpublished’, array(
      ‘label’ => t(‘Unpublished’),
    ));
    $this->create_state(‘published’, array(
      ‘label’ => t(‘Published’),
      ‘on_enter’ => array($this, ‘on_enter_published’),
      ‘on_exit’ => array($this, ‘on_exit_published’),
    ));
    $this->create_state(‘scheduled’, array(
      ‘label’ => t(‘Scheduled’),
      ‘on_exit’ => array($this, ‘on_exit_scheduled’),
    ));

    // Initialize events
    $this->create_event(‘for review’, array(
      ‘label’ => t(‘For Review’),
      ‘origin’ => ‘draft’,
      ‘target’ => ‘needs review’,
    ));
    $this->create_event(‘immediate publish’, array(
      ‘label’ => t(‘Immediate Publish’),
      ‘origin’ => ‘draft’,
      ‘target’ => ‘published’,
      ‘guard’ => ‘energy_workflow_guard_publisher’,
    ));
    $this->create_event(‘approve’, array(
      ‘label’ => t(‘Approve’),
      ‘origin’ => ‘needs review’,
      ‘target’ => ‘approved’,
      ‘guard’ => ‘energy_workflow_guard_editor’,
    ));
    $this->create_event(‘reject’, array(
      ‘label’ => t(‘Reject’),
      ‘origin’ => ‘needs review’,
      ‘target’ => ‘draft’,
      ‘guard’ => ‘energy_workflow_guard_editor’,
    ));
    $this->create_event(‘publish’, array(
      ‘label’ => t(‘Publish’),
      ‘origin’ => array(‘approved’, ‘scheduled’),
      ‘target’ => ‘published’,
      ‘guard’ => ‘energy_workflow_guard_publisher’,
    ));
    $this->create_event(‘unpublish’, array(
      ‘label’ => t(‘Unpublish’),
      ‘origin’ => ‘published’,
      ‘target’ => ‘unpublished’,
      ‘guard’ => ‘energy_workflow_guard_publisher’,
    ));
    $this->create_event(‘to draft’, array(
      ‘label’ => t(‘To Draft’),
      ‘origin’ => array(‘needs review’, ‘approved’, ‘unpublished’, ‘scheduled’),
      ‘target’ => ‘draft’,
    ));
    $this->create_event(‘schedule’, array(
      ‘label’ => t(‘Schedule’),
      ‘origin’ => ‘approved’,
      ‘target’ => ‘scheduled’,
      ‘guard’ => ‘state_flow_guard_schedule’,
    ));
    $this->create_event(‘immediate schedule’, array(
      ‘label’ => t(‘Immediate Schedule’),
      ‘origin’ => ‘draft’,
      ‘target’ => ‘scheduled’,
      ‘guard’ => array(‘energy_workflow_guard_publisher’, ‘state_flow_guard_schedule’),
    ));
  }

  /**
   * Other class info
   */

}

/**
 * Implements hook_state_flow_schedule_events_alter()
 *
 * @param array $scheduled_events
 */

function

energy_workflow_state_flow_schedule_events_alter

(&$scheduled_events) {
$scheduled_events[] = ‘immediate schedule’;
}

State Machine 3.x

These are great new features, and State Machine 3.x is promising to be even better!

The plan is to refactor State Flow to be entity agnostic and ultimately connect with Workbench Moderation 2.x.

Come out to the 2012 Drupalcon Denver workflow-based module collaboration sprint with Steve Persch and myself to get on the ground floor of this very unique and important collaboration.

Mar 01 2012
Mar 01

Today, I discovered an undocumented but cool feature in Drupal 7. If you’ve been doing any work with the Drupal 7 Form API, you probably know about the #attached property that you can add to any element. The #attached property allows you to associate JavaScript files, special settings to go into Drupal.settings, CSS files, or require the loading of a library specified in a hook_library call. It’s a really handy way to avoid having to find an appropriate place and use a bunch of drupal_add_js() or drupal_add_css() calls in your code.

What I discovered today is that you can also attach HTTP Headers to be set in the page response when an element is rendered. First, let’s look at where it is being used in drupal core:

    /**
     * Returns elements that are always displayed in the HEAD tag of the HTML page.
     */

    function _drupal_default_html_head() {
      // Add default elements. Make sure the Content-Type comes first because the
      // IE browser may be vulnerable to XSS via encoding attacks from any content
      // that comes before this META tag, such as a TITLE tag.
      $elements[‘system_meta_content_type’] = array(
        ‘#type’ => ‘html_tag’,
        ‘#tag’ => ‘meta’,
        ‘#attributes’ => array(
          ‘http-equiv’ => ‘Content-Type’,
          ‘content’ => ‘text/html; charset=utf-8′,
        ),
        // Security: This always has to be output first.
        ‘#weight’ => -1000,
      );
      // Show Drupal and the major version number in the META GENERATOR tag.
      // Get the major version.
      list($version, ) = explode(‘.’, VERSION);
      $elements[‘system_meta_generator’] = array(
        ‘#type’ => ‘html_tag’,
        ‘#tag’ => ‘meta’,
        ‘#attributes’ => array(
          ‘name’ => ‘Generator’,
          ‘content’ => ‘Drupal ‘ . $version . ‘ (http://drupal.org)’,
        ),
      );
      // Also send the generator in the HTTP header.
      $elements[‘system_meta_generator’][‘#attached’][‘drupal_add_http_header’][] = array(‘X-Generator’, $elements[‘system_meta_generator’][‘#attributes’][‘content’]);
      return $elements;
    }

This function is adding a <meta> tag that identifies that this page was generated using Drupal 7. It then adds to the #attached property the following (reformatted for readability):

    ‘#attached’ => array(
      ‘drupal_add_http_header’ => array(
        array(‘X-Generator’, $elements[‘system_meta_generator’][‘#attributes’][‘content’])
      ),
    )

And that’s it! If you inspect the HTTP Headers sent to you when you load a page, you’ll see an X-Generator header that identifies this site as being built with Drupal 7.

Web Inspector of HTTP Response Headers

So how might you use this when you’re writing your own custom code?

Here’s a hypothetical scenario where I think this might come in handy:

Imagine that you have created your own CAPTCHA system and it gets added to forms that are available to anonymous visitors. For performance you want most of your pages to be cached by Drupal and by your visitor’s browsers, so you have caching enabled. However, you want to make sure that your visitor’s browsers don’t cache the page with an old CAPTCHA on it. An easy way to address this would be to add an HTTP Cache-Control header whenever your CAPTCHA gets rendered.

Here’s some imaginary code:

    $form[‘captcha’] = array(
      ‘#type’ => ‘captcha’,
      ‘#attached’ => array(
        ‘drupal_add_http_header’ => array(
          array(‘Cache-Control’, ‘no-cache, no-store, must-revalidate’)
        ),
      ),
    );

Now, whenever the form with your CAPTCHA element gets rendered, Drupal will send Cache-Control headers to instruct your browser (or a reverse proxy cache like Varnish) not to cache that page.

Feb 28 2012
Feb 28

In the first part of this introduction to EntityFieldQuery, we looked at how simple it was to quickly construct a query with only a few lines of code. In this second part of the series, we’ll take a look at some more practical ways to put EntityFieldQuery to use.

Our Example Module

For the purposes of this series, I’ve created a simple module, EntityFieldQuery Example, that demonstrates some uses of EntityFieldQuery. It’s hosted on Github; you can grab the source code if you’d like to see the code in more detail or if you’d like to install it yourself.

Some quick notes about what the module contains: in addition to the callbacks and block code described below, the module installs three node types, efq_article, efq_page, and efq_photo. These are very simple node types: efq_article and efq_page each contain a body field for text, while efq_photo contains a single image field. In addition, all three content types contain a US States field, which we’ll be using to construct our example queries.

I recommend using Devel content generate to quickly generate content for your examples, although you can of course create it manually if you like. For the purposes of my examples, I created 200 nodes using the following command: drush genc --types="efq_page,efq_article,efq_photo" 200.

When you uninstall this module, it will clean up after itself, removing all created content. I’m thinking of teaching this technique to my cats.

Extending EntityFieldQuery

One of the first things to to remember about EntityFieldQuery is that it is a proper PHP class. The means that you can extend its behavior to suit your needs. For example, when using EntityFieldQuery, you might find that you are often trying to find nodes. If you are trying to find content that you will be listing in public pages and blocks, you probably want it to all be published. Additionally, lists of content tend to be published with the most recent content first.

Let’s create our own version of EntityFieldQuery, NodeEntityFieldQuery, with these properties already set:

class

NodeEntityFieldQuery

extends

EntityFieldQuery

{
// define some defaults for the class
public function

__construct

() {
// now we don’t need to define these over and over anymore
$this
->entityCondition(‘entity_type’, ‘node’)
->propertyCondition(‘status’, 1)
->propertyOrderBy(‘created’, ‘DESC’);
// define a pager
$this->pager();
}

  public function excludeNode($nid) {
    // code snip; we’ll come back to this.
  }

}

This is fairly straightforward. Each time we create a new NodeEntityFieldQuery, a number of things will be already pre-set for us, namely that we’re searching for published nodes and we want them returned in a reverse chronological order. Additionally, we instantiated a pager, since we’ll most likely want one. If we find ourselves using NodeEntityFieldQuery over and over again, we will save ourselves some time by having all these values pre-set.

Also note that we can extend the class with our own methods. We’ll come back to that method later.

A Simple Listing Page

Let’s start by creating a simple listing page. This is going to list all nodes of our three example content types in reverse chronological order, as well as providing a pager. First, we’ll need to define a menu item and callback for our page:

/**
 * Implements hook_menu().
 */

function efq_example_menu() {
  $items[‘efq’] = array(
    ‘title’ => ‘EntityFieldQuery example: recently published content’,
    ‘page callback’ => ‘efq_example_listing’,
    ‘access arguments’ => array(‘access content’),
  );
  return $items;
}

Now we can start our callback to provide the listing for us. We’re going to keep this very basic to start with: we’re just listing all the nodes of our three types, that are published, in reverse chronological order.

function

efq_example_listing

() {
// instantiate the query using our extended query object
$query = new

NodeEntityFieldQuery

();
// let our query know which node types we want
$query
->entityCondition(‘bundle’, array(‘efq_article’, ‘efq_page’, ‘efq_photo’)); // execute the query
$result = $query->execute();
$output = array();
// return the nodes as teasers
if (!empty($result[‘node’])) {
$output[‘nodes’] =

node_view_multiple

(

node_load_multiple

(array_keys($result[‘node’])), ‘teaser’);
$output[‘pager’][‘#markup’] =

theme

(‘pager’, $query->pager);
}
else {
$output[‘status’][‘#markup’] =

t

(‘No results were returned.’);
}
return $output;
}

That’s all we need to do to provide us with a listing page with a pager. It’s not exactly Drupal For Dummies, but it really isn’t terribly difficult, and in particular it’s very few lines of code to get that result.

Adding Arguments

Of course, it would be more interesting if we could filter this list some. Let’s add some arguments to our callback. Our current URL is at efq. Let’s set it up so that we can use URLs in the form efq/%state/%node_type as well. We’ll have to set up our callback such that both state and node_type are optional, and to be aware of the arguments if they’re present.

Our new callback isn’t substantially different:

function efq_example_listing($state = NULL, $node_types = array(‘efq_article’, ‘efq_page’, ‘efq_photo’)) {
  // instantiate the query using our extended query object
  $query = new NodeEntityFieldQuery();
  // set up some basic parameters
  $query
    ->entityCondition(‘bundle’, $node_types);
  // if $state is defined, add that fieldCondition to the query
  if (!empty($state)) {
    $query->fieldCondition(‘field_us_state’, ‘value’, $state);
  }
  // execute the query
  $result = $query->execute();
  $output = array();
  // return the nodes as teasers
  if (!empty($result[‘node’])) {
    $output[‘nodes’] = node_view_multiple(node_load_multiple(array_keys($result[‘node’])), ‘teaser’);
    $output[‘pager’][‘#markup’] = theme(‘pager’, $query->pager);
  }
  else {
    $output[‘status’][‘#markup’] = t(‘No results were returned.’);
  }
  return $output;
}

In our new callback, we look for a value for $state, and if it is present, we add a fieldCondition to our query. Additionally, we set up $node_types with a default value of an array of all of our content types, and look for a changed value. Note that the condition code is flexible:

  $query
    ->entityCondition(‘bundle’, $node_types);

$node_types can be an array, as is the default, or it can be a string, which is how it will come in from the URL. That’s fine; the condition will adjust to the type of argument you pass.

Building a Content-sensitive Block

So now we have a listing of our content, and it can accept arguments. It would be nice also if, on our individual nodes, we could find other content for our given state. Let’s construct a block that shows the five most recent items from that node’s state. Additionally, we should probably provide some links back to the listing pages, again based on state.

Constructing the framework for the block itself is basic Drupal:

/<

strong

>
*

Implements hook_block_info

().
*/
function

efq_example_block_info

() {
$blocks[‘content_by_state’] = array(
‘info’ =>

t

(‘Other Content In This State’),
‘cache’ =>

DRUPAL_NO_CACHE


);
return $blocks;
} /</

strong

>
*

Implements hook_block_view

().
*/
function

efq_example_block_view

($delta = ) {
$block = array();
switch ($delta) {
case ‘content_by_state’:
$block =

efq_example_content_by_state

();
break;
}
return $block;
}

Now of course we need to populate that block. Here’s the function that will do that for us (don’t worry, we’re going to break this down):

/**
 * Produces content for a block, based on the state of the host node.
 */

function

efq_example_content_by_state

() {
$block = array();
// if we don’t have a node to draw from, in our current setup, we won’t have a state and can’t continue
if ($node =

menu_get_object

()) {
// get the state value
$field_name = "field_us_state";
if (!empty($node->$field_name)) {
$items =

field_get_items

(‘node’, $node, $field_name);
$state = $items[0][‘value’];
}
// only continue if we have a state value
if ($state) {
// instantiate the query
$query = new

NodeEntityFieldQuery

;
// limit the query to our established node types
$node_types = array(‘efq_article’, ‘efq_page’, ‘efq_photo’);
$query->entityCondition(‘bundle’, $node_types);
// add the state value
$query->fieldCondition($field_name, ‘value’, $state);
// add a small range
$query->range(0, 5);
// remove the current node from the query
$query->excludeNode();

      // execute the query
      $result = $query->execute();
      $output = array();
      $block[‘subject’] = t(‘Other Content for @state’, array(‘@state’ => $state));

if (!empty($result[‘node’])) {
// return the nodes as teasers
$nodes =

node_view_multiple

(

node_load_multiple

(array_keys($result[‘node’])), ‘teaser’);
$block[‘content’][‘nodes’] = $nodes;
// let’s include links to the content listing for convenience
$node_types = array(
‘efq_article’ => ‘Articles’,
‘efq_page’ => ‘Pages’,
‘efq_photo’ => ‘Photos’,
);
$links = array(

l

(

t

(‘All Content for @state’, array(‘@state’ => $state)), "efq/$state"));
foreach ($node_types as $node_type => $node_type_name) {
$links[] = array(

l

(

t

(‘All @node_type_name for @state’, array(‘@node_type_name’ => $node_type_name, ‘@state’ => $state)), "efq/$state/${node_type}"));
}
$item_list = array(
‘#items’ => $links,
‘#type’ => ‘ul’,
‘#theme’ => ‘item_list’,
);
$block[‘content’][‘links’] = $item_list;
}
else {
$block[‘content’][‘status’][‘#markup’] =

t

(‘No results.’);
}
}
}
return $block;
}

Let’s break this down a bit.

// if we don’t have a node to draw from, in our current setup, we won’t have a state and can’t continue
if ($node =

menu_get_object

()) {
// get the state value
$field_name = "field_us_state";
if (!empty($node->$field_name)) {
$items =

field_get_items

(‘node’, $node, $field_name);
$state = $items[0][‘value’];
}
// only continue if we have a state value
if ($state) { // ….

The beginning of this is quite straightforward. If we don’t have a node, there’s no point in continuing. Also, we need the node to have a state value, or else there isn’t any point in looking for state content for our block.

      // instantiate the query
      $query = new NodeEntityFieldQuery;
      // limit the query to our established node types
      $node_types = array(‘efq_article’, ‘efq_page’, ‘efq_photo’);
      $query->entityCondition(‘bundle’, $node_types);
      // add the state value
      $query->fieldCondition($field_name, ‘value’, $state);
      // add a small range
      $query->range(0, 5);

This looks very similar to our page listing query. We define a new query via NodeEntityFieldQuery, let the query know what kinds of nodes we’re looking for, and give it the value for the state field. Additionally, we’re limiting the number of results returned to the 5 most recent, since this is a block.

One thing you might want to consider is that you may not want the results listed in the block to include the node you’re currently on. This makes sense: if you see the same page you’re already reading listed in a sidebar, it can feel sloppy, or else like you’re having your time wasted.

Luckily, it is not hard to do this. Remember when we extended EntityFieldQuery to create NodeEntityFieldQuery? Remember this bit?

  public function excludeNode($nid) {
    // code snip; we’ll come back to this.
  }

We can add our own methods to our new class. Let’s do that now.

  /**
   * If we’re on a node, and if the entity_type is node, exclude the local node from the query
   */

  public function excludeNode($nid) {
    if (!$nid) {
      $object = menu_get_object();
      $nid = $object->nid;
    }
    if (!empty($nid) && $this->entityConditions[‘entity_type’][‘value’] === ‘node’) {
      $this->propertyCondition(‘nid’, $nid, ‘<>’);
    }
    return $this;
  }

This is a fairly simple method. If you pass in a node ID it will use it; otherwise it will attempt to use the node ID on the page you’re currently on. If you’re using EntityFieldQuery to search for nodes, and there is a node ID, this method will tell your query to exclude the current node. The key item is this line:

      $this->propertyCondition(‘nid’, $nid, ‘<>’);

Note the “<>” operator. Normally if you’re looking to match a value or set of values, you can leave the operator out of your propertyConditions, fieldConditions, etc., because they’re set to “=” or “IN” by default, depending on whether you have a single value or multiple that you’re matching against. If you want to use a different operator for your condition, you have to enter it explicitly.

Now that we have our method, we just need to add a quick method call to our block callback function, and we’re set:

      // remove the current node from the query
      $query->excludeNode();

Most of the rest of the callback looks quite similar to our page listing callback:

// execute the query
$result = $query->execute();
$output = array();
$block[‘subject’] =

t

(‘Other Content for @state’, array(‘@state’ => $state)); if (!empty($result[‘node’])) {
// return the nodes as teasers
$nodes =

node_view_multiple

(

node_load_multiple

(array_keys($result[‘node’])), ‘teaser’);
$block[‘content’][‘nodes’] = $nodes;

We execute our query. We set the block title, since that is going to be the same whether we had content or not. If there are results from our query, we load them and then slot them into the block content.

We do generate a set of links to allow the user to get back to the page listings easily:

        // let’s include links to the content listing for convenience
        $node_types = array(
          ‘efq_article’ => ‘Articles’,
          ‘efq_page’ => ‘Pages’,
          ‘efq_photo’ => ‘Photos’,
        );
        $links = array(l(t(‘All Content for @state’, array(‘@state’ => $state)), "efq/$state"));
        foreach ($node_types as $node_type => $node_type_name) {
          $links[] = array(l(t(‘All @node_type_name for @state’, array(‘@node_type_name’ => $node_type_name, ‘@state’ => $state)), "efq/$state/${node_type}"));
        }
        $item_list = array(
          ‘#items’ => $links,
          ‘#type’ => ‘ul’,
          ‘#theme’ => ‘item_list’,
        );
        $block[‘content’][‘links’] = $item_list;

And finally, because we’re relatively thorough, we write a simple message to the block if not results are returned:

      else {
        $block[‘content’][‘status’][‘#markup’] = t(‘No results.’);
      }

This Looks Kind Of Familiar…

If you’re an old Drupal hand, you might be wondering why I wouldn’t just build this in Views. Indeed, this might look a bit masochistic if you’re used to Views. However, there are a few things to consider:

  1. EntityFieldQuery is core. If you are interested in keeping your installation as lean as possible, this is something to consider. Views is a contributed module, and so it automatically adds size and overhead to your Drupal install.

  2. EntityFieldQuery is small. The amount of code required to generate an equivalent listing in Views, compared to what we built above, is much, much greater. To be fair, your Views code will almost certainly be exported and managed via Features, and so in effect you’re not writing the Views code anyway. But, that highlights a different point: writing code this way keeps you in code, rather than requiring you to work in a point-and-click interface. For the average developer, this is a very good thing.

  3. EntityFieldQuery is code. You might want to have certain things exposed to change and not others. For example, on a recent project, we were building a site for a client that had site builders on staff. These people were comfortable working with Views, and we wanted to make sure that they had the ability to create and modify views. However, we wanted to also make sure that there was some core functionality that was left untouched. If we had built that core functionality with Views, it would have been exposed to change. By building that core functionality with EntityFieldQuery, we kept it strictly in code, which protected it.

Summing Up and Coming Up

There is a lot of code in this post. If you are interested in understanding this better, I recommend installing the sample module, creating some test content, and playing with the code. I think much of the code is a lot easier to understand in context.

In our next post, we’ll look at some more advanced EntityFieldQuery techniques. We’ll also look at scenarios when EntityFieldQuery is a good candidate for the job, and some scenarios when EntityFieldQuery is not what you want to use.

Feb 22 2012
Feb 22

Perhaps you’ve heard about Jekyll, the simple static site generator that powers Github Pages and a number of other sites including our friends at Development Seed. Jekyll is great because you can write a whole collection of items in Markdown and then easily turn it into a website.

Heykll and Jekyll

A few weeks ago, Steven Merrill and I had the idea to create a presentation in Impress.js using Markdown files for each slide. We enjoyed that Impress.js rivals many of the features of Prezi, but was ultimately just HTML+CSS+JS, which meant that versioning and tweaking the talk could be much simpler than Flash-based Prezi. For my lunch break that day, I dove in. The result is Hekyll, a Jekyll-based presentation generator for Impress.js. Learn more about the process of making Hekyll here.

Drupalcon Denver 2012 is rapidly approaching and Steven and I have been busy getting our slides ready for our Zagat.com Case Study presentation. We’ve been using Hekyll to collaborate and I have to say it’s been absolutely delightful. Our talk is in a hosted Git repository, so we can both be working on separate parts of the presentation simultaneously and, because all of the styling is done in CSS, we can focus on the content first and then make it look great.

Speaking of making it look great, Hekyll has the concept of ‘themes’ built in, which makes it super easy for you to create your own look and feel, or try out a few different ones. Right now, Hekyll only has a fairly basic theme based off the Impress.js demo, but we hope to add a few more simple general designs in the future.

Drupalcon Denver theme for Hekyll

Drupalcon Title Slide, powered by Hekyll

If you’re planning to speak at Drupalcon Denver and you’re interested in Hekyll, I’ve made a Hekyll theme based on the Drupalcon presentation templates, which is available (with installation instructions) on Github here: https://github.com/bmcmurray/drupalcon-denver-2012-hekyll.

I’d love to hear if you decide to use Hekyll for your presentation, and if you’d like to get involved, Steven and I are fairly actively working on feature improvements on Github.

Feb 21 2012
Feb 21

As we outlined in a previous post about building the Energy.gov platform without Views, extending EntityFieldQuery (EFQ) is essential to our development philosophy.

One hurdle with using EFQ, however, is adding new fields to existing content. Querying for nodes on an existing platform when a new field is added conflicts with some of the assumptions made in EFQ. Archiving content among various organic groups on an existing site is a great example scenario to illustrate this point.

Let's say that in certain cases, you want users to be able to archive a story from appearing on the front page for one group, but still want the story to be published in another. In this case, the list of stories on the front page are controlled by EFQ.

For the user, you could simply add a new field that allows them to choose which group the story should be archived.

For the developer, EFQ presents a problem because existing nodes will not contain a value for the new archive field in the archive field table. Because EFQ uses INNER JOINs, it assumes the existence of values in all relevant tables, meaning content without an archive value will be excluded altogether.

The solution* is to use an 'OR' query.

Our goal is to either show content that is not in list of groups selected for the archive field OR to show content that does not have a value for the archive field.

But EFQ, as a class, does not work with 'OR' queries, right?

Well, technically 'no', but it does break down into the SelectQuery class upon execution, so our answer is to use an alter.

Altering by Tag

Since we're already extending EFQ, we create a method to tag the query so we can utilize hook_query_tag_alter in the module.

/**
 * @file
 * extends EntityFieldQuery, providing some useful added methods and some defaults
 */

class EnergyEntityFieldQuery extends EntityFieldQuery {
  /**
   * define some defaults for the class
   */

  public function __construct() {
    $this
      ->entityCondition('entity_type', 'node')
      ->propertyCondition('status', 1)
      ->propertyOrderBy('created', 'DESC');

    //archive a node for display
    $this->archive();
  }

  /* Other methods we discussed earlier are here */

  /**
   * Set archive tag to be manipulated by an alter later. Checking for null
   * values doesn't work in EFQ because of INNER JOIN
   *
   */

  public function archive() {
    $this->addTag('archive');
    return $this;
  }

/**
   * Unset archive tag
   */

public function

clearArchive

() {
unset($this->tags['archive']);
}
}

Within the EnergyEntityFieldQuery (EEFQ) class, we also like to add an 'undo' method as this allows us to remove an assumption in our extension where needed.

Without going into too much detail, part of the execution of EEFQ involves invoking the appropriate alter hooks.

We then define the alter in our module where we can apply a LEFT JOIN and OR query.

/**
 * Implements hook_query_TAG_alter
 *
 * Check if archive taxonomy is set for a group, but include entities
 * that may not have a value
 *
 * @param QueryAlterableInterface $query
 */

function energy_content_query_archive_alter(QueryAlterableInterface $query) {
  //grab the taxonomy by the existing og relationship
  $group = og_context_determine_context();
  $tid = $group->etid;
  $query
    ->leftJoin('field_data_field_archive', 'a', 'node.nid = a.entity_id');
  if ($tid) {
    $or = db_or()
          ->condition('a.field_archive_tid', array($tid), 'NOT IN')
          ->isNull('a.field_archive_tid');
    $query
      ->condition($or);
  }
}

The organic groups code is specific to the Energy.gov platform, so it can be ignored. It merely communicates how we grab the taxonomy id for comparison.

The key aspects are to LEFT JOIN the appropriate field table, then use db_or() to set your conditions.*

In this case, we're not only going to exclude nodes that may have the value of the 'archive' tid I've set, but we also want those nodes who may have a NULL value in that field table.

Hopefully this technique provides you with some ideas of how to extend EFQ even further. Additionally, it should provide a solution to those who wondered how to perform OR queries with EFQ in Drupal 7.

*Assuming you are using the SQL storage engine

Feb 17 2012
Feb 17

January Drupal PlaydayEver wish you could spend a Saturday hanging out and working on your projects with other Drupal folks? New to Drupal and wish you could just turn to the expert sitting next to you and ask questions?

Come by the Treehouse office tomorrow, from 11am to whenever. Bring your laptop, your projects, your questions, your books…whatever you want to tinker with alongside a bunch of fellow Drupal professionals and enthusiasts. Drupal newcomers are always welcome! Just keep in mind that you’ll get more out of the day if you have Drupal up and running before you get there.

DrupalCon Denver site usability testing

Lisa Rex will be conducting usability testing for the DrupalCon Denver 2012 site. She is looking for 8 volunteers &emdash; 4 to test mobile and 4 to test via desktop browser. If you are interested in participating, you can contact Lisa through her Drupal.org contact form, find her in the #drupal-nyc channel on IRC, or just show up.

Get the details and RSVP for the Drupal Playday on Meetup.com or on groups.drupal.org

Feb 16 2012
Feb 16

Drupal 7 introduced the EntityFieldQuery API. This class is a mechanism for quickly building queries of entities and fields – typically nodes, but of course not limited to that.

Drupal 7 features a much richer database abstraction layer than previous versions of Drupal, but in the end you are still more or less building SQL. EntityFieldQuery allows you to construct queries without much knowledge of SQL at all.

Drupal core itself mostly uses EntityFieldQuery as a utility function, in many cases simply to check that a given field has a value somewhere in the system or not. It’s strange that such a powerful mechanism is put to so little use by core, but no matter. Let’s see how we can use it.

Starting Your Query

Starting an entity field query looks like this:

$query = new EntityFieldQuery();

Extremely simple, right? That’s all it takes to get started. Let’s now add some conditions to our query.

$query
 ->entityCondition(‘entity_type’, ‘node’)
 ->entityCondition(‘bundle’, ‘article’)
 ->propertyCondition(‘status’, 1)
 ->propertyOrderBy(‘created’, ‘DESC’);

Let’s take a look at what’s going on here. First of all, notice the lack of semicolons between method calls. Each EntityFieldQuery method returns the same EntityFieldQuery object the method is called on. This allows us to chain method calls together, which speeds up coding and makes it easier to read. This will look familiar to anyone who uses jQuery. This could just as easily be written like so:

$query->entityCondition(‘entity_type’, ‘node’)->entityCondition(‘bundle’, ‘article’)->propertyCondition(‘status’, 1)->propertyOrderBy(‘created’, ‘DESC’);

But, that is really not easy at all to read. I recommend putting each method on its own line. Future generations of coders will thank you.

Now let’s look at each method and see what’s happening.

->entityCondition(‘entity_type’, ‘node’)

The first method uses an entityCondition to tell the query to look for node entities. It may seem like specifying this is redundant, but as many other things in Drupal 7 are also entities – users, taxonomy terms, etc – you need to restrict your set.

->entityCondition(‘bundle’, ‘article’)

The second method tells the query which node types to restrict the set to. This can be left out if you would like to query all available nodes, but in most cases you will be looking for specific node types. Note also that the second argument, ‘article’ in this case, can be either a string or an array; the query will adjust automatically. So if you were looking for both article and page node types, you could rewrite that part of the chain as follows:

->entityCondition(‘bundle’, array(‘article’, ‘page’))

As you can see, it’s extremely easy to expand your query.

->propertyCondition(‘status’, 1)

The third method is a propertyCondition. A property in this case is any column in the base table for the given entity type. In the case of nodes, this could be whether it is published, the user that created the node, time of creation, etc. In our example above, we are modifying our query to only return published values.

->propertyOrderBy(‘created’, ‘DESC’)

The last method in our example above uses propertyOrderBy to set an order. In this case, we’re simply asking for the results to be returned in reverse chronological order.

Querying Fields and Limiting

Now let’s add a query on a field value using a fieldCondition. Let’s assume each of the node types in our example has a field ‘field_us_state’ assigned to it. We’re going to find nodes that are associated with the New York Tri-state area, which also includes Connecticut and New Jersey. This would look like this:

$query->fieldCondition(‘field_us_state’, ‘value’, array(‘CT’, ‘NJ’, ‘NY’));

We can order by field values as well, if that is useful to us. Note that ordering conditions are processed in the order that they are added to the query.

Perhaps for our purposes, we want to limit our query to the 10 most recent items. Let’s add a range:

$query->range(0,10);

Et Voilà

Finally, we execute the query and assign that to a variable:

$result = $query->execute();

This returns us an array of entity ids that match the conditions specified for the query. If we are querying nodes, the information will be under $result['node']. In most cases, what you actually want are the nids that have been returned. This is easily accomplished:

$nids = array_keys($result[‘node’]);

Putting that all together, we have:

$query = new EntityFieldQuery();
$query
  ->entityCondition(‘entity_type’, ‘node’)
  ->entityCondition(‘bundle’, ‘article’)
  ->propertyCondition(‘status’, 1)
  ->propertyOrderBy(‘created’, ‘DESC’)
  ->fieldCondition(‘field_us_state’, ‘value’, array(‘CT’, ‘NJ’, ‘NY’))
  ->range(0,10);
$result = $query->execute();
$nids = array_keys($result[‘node’]);

This is not very much code at all for this result. Moreover, we don’t need to know anything about SQL or even about the underlying storage engine being used. We simply write the query in this simple syntax, and EntityFieldQuery API does the job of translating that into a query that Drupal’s database abstraction layer understands and executes it for us.

What would we do with this list of nids? Pretty much anything. We might load them, for starters:

$nodes = node_load_multiple($nids);

We might want to display these 10 nodes in a teaser style. That’s also easily done. Let’s generate a Drupal render array:

$output = node_view_multiple($nodes);

More To Come

This is just the beginning. Look for part 2 of this post, where we will show more concrete examples of putting EntityFieldQuery to use. Also look out for posts from other Treehouse engineers explaining more advanced topics related to EntityFieldQuery.

Jan 20 2012
Jan 20

Drupal often makes certain things so easy to do, that we as developers don’t take the time to consider the alternatives. Lately I’ve been giving a lot of thought to the module dependency system and possible alternatives. The main reason for this is that dependencies create a more brittle overall system which in turn makes the code more difficult to maintain and re-use.

Scenario 1

I’ve seen many install profiles and modules that include modules like overlay or toolbar as a dependency. In almost every case, this was done so that those modules would be installed by default. The problem with using the dependency system is that these modules cannot be removed without modifying the code. If you wanted to use admin_menu in place of toolbar, you would need to first remove the dependency from the info file.

Solution

This one has a simple solution. Rather than using dependencies to install modules, use a simple install hook with module_enable.

/**
 * Implements hook_install().
 */

function example_install() {
  // Enable optional modules.
  module_enable(‘toolbar’, ‘overlay’);
}

Scenario 2

Say we have a module that creates a basic article node, with a title, body and taxonomy field for tags. Typically we would make taxonomy module a dependency of the article module. In the spirit of reusable code, we want to make our article module as self-contained as possible, so that we can use it on all of our Drupal projects.

But what if we have a project that has no need for taxonomy? Must we always enable the taxonomy module to use our article module, even if there is no need?

Solution

The answer to our problem comes in the form of a couple of new hooks for Drupal 7: hook_modules_enabled and hook_modules_disabled, give us the perfect opportunity to act upon new dependencies being enabled and disabled.

First we will create a helper function for our optional functionality. In this case, we want to create a tags vocabulary and add a taxonomy reference field to the article node type, only if the taxonomy module is available.

/**
 * Add the "tags" taxonomy field to a node type.
 */

function article_add_tags_field($type) {
  if (module_exists(‘taxonomy’)) {
    // create the tags vocabulary and add the field to the bundle, making sure
    // to check if they already exist.
  }
}

Next, we implement hook_enable to create our article node type. This technique works equally well with hook_install so choose the hook that is most appropriate for your situation. Notice that we are checking the existence of the taxonomy module before calling our helper function. This check could be skipped since it also happens in the helper, but I think checking it here reinforces the idea that this is an optional feature.

/**
 * Implements hook_enable().
 */

function article_enable() {
  // Borrowed from standard.install.
  $type = node_type_set_defaults#40;array(
    ‘type’ => ‘article’,
    ‘name’ => st(‘Article’),
    ‘base’ => ‘node_content’,
    ‘description’ => st(‘Use <em>articles</em> for time-sensitive content…’),
    ‘custom’ => 1,
    ‘modified’ => 1,
    ‘locked’ => 0,
  ));
  node_type_save($type);
  node_add_body_field($type);
  if (module_exists(‘taxonomy’)) {
    article_add_tags_field($type);
  }
}

Lastly, we implement hook_modules_enabled. This hook is called by the module system any time one or more modules are enabled. Here is where we have the opportunity to look for newly enabled modules that our module knows how to integrate with. If the taxonomy module is in the list of modules that have been enabled, we can fire our helper function.

/**
 * Implements hook_modules_enabled().
 */

function article_modules_enabled($modules) {
  if (in_array(‘taxonomy’, $modules)) {
    article_add_tags_field(node_type_load(‘article’));
  }
}

For completeness, you should probably also implement hook_disable and hook_modules_disabled, but I’ll leave that as an exercise for you.

We are using these two methods to keep our dependency chain light and add in optional functionality on our Drupal 7 projects. I think there is an opportunity to build true support for these types of optional integrations into Drupal 8. I’d love to hear how others are handling this situation. How are you handling dependencies and integrations in your modules, especially ones meant to be used in a variety of contexts and situations?

Jan 05 2012
Jan 05

Hey Drupal developers and site administrators! Ever spend your time handling things like block placement and microsite deployments at the request of your content editors?

Hey content editors and managers! Ever wish you could just do this stuff without relying on your developers and administrators–or becoming a de facto site administrator yourself?

This one’s for you.

Three of our modules from 2011–co-sponsored by Energy.gov–empower content editors with greater independence and more effective execution tools.

BEAN

BEAN gives editors an easy way to plug blocks into any page on a website without the typical administrative wrangling.

On the technical side, the module creates blocks as entities. Fields are used to define block types (similar to node types). Just like nodes, instances of blocks can be created and placed into pages. More geekery in this Neil Hastings post on the BEAN module.

State Machine

State Machine provides content editors with additional options for non-disruptive content revisions via the State Flow base implementation. By creating a draft version of existing content, ongoing revisions can occur without interfering with site performance. When a revision is ready to publish, the published version (when applicable) is automatically archived and replaced with the latest approved copy.

Developers get a treat as well–the State Machine module is really an API. Through this API, State Machine extendable and exportable through plugins. Code-based workflows can be created for multiple sites and easily tested–without affecting existing content and functionality. Fredric Mitchell breaks it down (with extensive code samples) here.

OG Tasks

OG Tasks is an add-on for the Organics Group Module. OG Tasks automates many of the tasks associated with creating new groups–such as when launching a microsite. Instead of relying on developers or site administrators, content editors can manage pre-configured group creation with just a few mouse clicks.

Neat, right? Kudos to Neil Hastings, Fredric Mitchell, Roger Lopez, Joe Turgeon, and Tim Cosgrove for their work on bringing these modules to fruition!
Nov 04 2011
Nov 04

This morning, I happened upon and quickly found myself frowning. I wasn't frowning because of the Wired editors' decisions about which apps are the best, it was because I was frustrated with a glaring design problem in their article and I suspect I know the reason it looks so bad:

Developers Built it That Way


Developer-enforced Design Flaws

It's not an isolated problem, really. A developer is given a functional spec that calls for a grid display of 'apps' which will have a company name and the app name. Seems reasonable, and with most modern CMSes, probably not too hard to build, either. It looks great with dummy lorem ipsum content when shown at the design review meeting, no one ever stops to wonder if their content will actually fit the requirements, though. And then when it comes time to put the real content in both company name and application name are required fields, so when you come to a company that is self-publishing an eponymous app, like Netflix, then your editors are stuck. They are forced to do something they know is wrong and they put the same name into both fields.

It's easy to pick on Wired, but this is something I see (and, I'll admit, have done) on any number of websites. Maybe it's the nature of building and using content management systems. We do a lot of work with Drupal around here and I think it's safe to say we run into this problem somewhat regularly. We build interfaces and want them to be easy–excruciatingly easy– for editors to use to create and manage content. So we separate content out into separate inputs in the content creation screens, we make different 'content types,' we create arbitrary divisions between what is content and what is 'auxiliary content.'

Recently, there was a bit of a scandal in the WHATWG around one spec editor's personal plan to deprecate the <time> element and replace it with something he saw as more appropriate. Jeremy Keith wrote a blog post about the event and in it talks about one of the key decision-making guidelines for the HTML spec: the Priority of Constituencies:

In case of conflict, consider users over authors over implementors over specifiers over theoretical purity. In other words costs or difficulties to the user should be given more weight than costs to authors; which in turn should be given more weight than costs to implementors; which should be given more weight than costs to authors of the spec itself, which should be given more weight than those proposing changes for theoretical reasons alone. Of course, it is preferred to make things better for multiple constituencies at once.

It seems to me that this would be a great guiding principle for CMS developers, too. Of course, with CMS development you might have many types of users: visitors, commenters, contributors, editors, managers, administrators, etc.; so how do you decide who should get precedence? I think the Priority of Constituencies still speaks to this scenario: privilege the visitors of your website over your content creators and staff over your developers.

What do you think? Do you see developer-enforced design flaws in the sites you visit? In the work you do? How can we bring the Priority of Constituencies into our work as website and CMS developers?

Nov 02 2011
Nov 02

How many times have the following issues happened on a project you've worked on?

  • Notices (or worse) appeared on production because of a PHP version mismatch between a developer's machine and the production web servers.
  • A new PHP extension or PECL extension had to be installed on production because it was installed in WAMP or MAMP?
  • A team member ran into difficult setting up their local environment and spent many hours stuck on something.
  • Team members didn't set up SSL or Varnish on their local machines and issues had to be caught on a dev server.
  • A team member would like to switch to Homebrew, but can't set aside the many hours to redo their setup until a project is done.

Tools like MAMP, XAMPP, the Aqcuia dev desktop, MacPorts and Homebrew all make it easy to get an *AMP stack up and running on your computer, and tools like MacPorts and Homebrew even make it pretty easy to install tools like Varnish and memcached.

While these tools make it easy to run a very close approximation of the production hosting stack on your local machine (arguably closer if you use Macintosh or Linux,) it will still have some key differences which will ultimately contribute at some point to a "Works on My Machine!" situation in your project.

Works On My Machine Badge

Luckily, virtualization has advanced to such a degree that there are cross-platform virtualization solutions such as VirtualBox, but just using a VM inside of VirtualBox doesn't solve the whole problem. It makes acquiring the correct versions of software easy, but keeping configuration in sync can still be a challenge for users who are not deeply familiar with Linux.

Vagrant is a Ruby gem that makes working with Linux virtual machines easy. You distribute a Vagrantfile to your team, and it does the following things for you:

  • Downloads and sets up virtual machines from a single .box file which it will download over HTTP or FTP.
  • Provisions the software and configuration on the VM using your choice of Chef, Puppet, or simple shell scripts
  • Automatically shares the directory with the Vagrantfile (and any subdirectories) to the virtual machine with Virtualbox's built-in file sharing
  • Forwards the SSH port (and optionally other ports) to your localhost and avoids collisions so you can always directly SSH to the machine
  • Optionally sets up a dedicated host-only IP address that you can use to connect to all services on the VM without port forwarding
  • Optionally shares directories to the VM over NFS from a Macintosh or Linux guest, which enables acceptable performance for a Drupal docroot

Since Vagrant handles the file sharing with the VM, you and your team don't have to mess around with setting up FUSE or the like and you can continue to use the tools that you're used to using locally, such as your text editor or garphical source control program.

In addition, so long as you have a single developer skilled in ops who can encapsulate the production configuration into a system like Chef or Puppet, these changes can be pushed down to the whole team. Once your ops team has a working Varnish configuration, for example, they can push that into the Vagrant repository, and then a working Varnish setup on all your developers' VMs is just a git pull and a vagrant provision away.

We've been working with Vagrant over the last few months and think it offers a number of advantages. All it takes to get started installing VirtualBox and the vagrant ruby gem. Detailed information on how to get started is available in the excellent Vagrant Quickstart guide.

I've put together a screencast that's just over 10 minutes long and shows the whole process of bringing up a CentOS 5.6 VM with the treehouseagency.com site shared from my local machine.

We'll be posting more example code over the coming weeks that will allow you to try out Drupal from your local machine on a Linux VM.

Sep 22 2011
Sep 22

At the last Drupal Indy User Group meeting, I did a presentation on integrating Openlayers and Apache Solr. In the presentation, I walk you through how to setup and configure the modules necessary to display search results on a map in Drupal 7. This is the same basic process which we used on Energy.gov.

The video is about 45 minutes long, so here is a general outline of the presentation. Be sure to watch the entire video or you will not see how all of the pieces are integrated:

  1. How to Geocode Content
  2. Sending Geocoded Content to Solr
  3. Local Solr with Apache Solr
  4. Openlayers
  5. Adding Solr Results to a Map
  6. Placing a Map on the Search Page
  7. Viewing Search Results on the Map

Modules covered in the video

Update...

To Help facility setting up Apache Solr with Local Solr I created this repository: https://github.com/treehouseagency/local-solr-config. It contains all of the files necessary to run solr with local solr on Mac OSX.

Instructions to Run:

  1. Checkout the repo.
  2. Change to the "examples" directory
  3. run "java -jar start.jar"
  4. Profit!
Sep 21 2011
Sep 21

Update: Please see the code at https://gist.github.com/1460818 for a working example with the current bean code base.

In my previous post I talked about how we decided to leverage EntityFieldQUery as an alternative to Views for aggregating lists of content. In this post we'll be looking at how Treehouse created the Bean module to create and place blocks containing these lists of content.

What Are Beans?

The purpose of the Bean module is to create blocks as entities. We can build out block types, using fields as with any Drupal 7 entity, and have instances of blocks. The concept is similar to how node types are commonly used in Drupal 7. An individual instance of the block type can be placed on any number of pages.

Depending on the construction of the block type, different instances of the block type could have different display settings as well as content. As with nodes, we're not limited to simply adding content to a Bean block. We can use fields to configure the display parameters of the block. This can allow us to give an editor the ability to easily and quickly construct custom queries for blocks.

Let's look at a real example from Energy.gov where we used Beans to aggregate and display content.

Building A Content Listing

This example is from the Energy.gov News Landing Page. The requirement for this block was to collect recent article nodes, optionally filtered by some taxonomy terms, and allow for 3 different type of displays. Users should be able to construct a block, choosing which article types they want to display, which topics they want to restrict their search to, and how many nodes to display for each display type.

To accomplish this task, we created a article listing block type. Let's walk through some of the code used to construct the block type.

Building The Plugin

The Bean module uses the Ctools plugin architecture so defining one should look familiar to some Drupal developers. First, in your module file, tell the Bean module that you are defining new block types.

/**  
  * Implements hook_bean_types_api_info().  
  */

function

mymodule_bean_types_api_info

() {
return array( 'api' => 1);
}

/**
 * Implements hook_bean_types().
 */

function

mymodule_bean_types

() {
$plugins = array();
$plugin_path =

drupal_get_path

('module', 'mymodule') .
'/plugins/bean';

  $plugins['listing'] = array(
    'label' => t('Listing'),
    'handler' => array(
      'class' => 'listing_bean',
      'parent' => 'bean',
    ),
    'path' => $plugin_path,
    'file' => 'listing.inc',
  );

return $plugins;
}

We defined a "Listing" Block type. The block type will use the class "listing_bean" and extends the plugin "bean." The code for the listing block type can be found in /plugins/bean/listing.inc. Nice and simple. Since we are adding a new file, be sure to add the following line to your mymodule.info file:

"files[] = plugins/bean/listing.inc"

This tells the Drupal code registry to look in this file for code to autoload.

For more detail about the full API for block types, see the bean_type_plugin_interface in includes/bean.core.inc in the Bean module. The bean_plugin abstract class takes care of most of the work, but you can always use your own base class if you wish. If you extend the default bean plugin then there are three methods you need to implement:

  • Values: These are the properties that your plugin will be using along with their defaults.
  • Form: This is the form that is used when creating/editing the block.
  • View: This is the code used to render the block.

First we create the shell for the class.

/**
 * @file
 * Listing Plugin
 */
class

listing_bean

extends

bean_plugin

{
}

Here are some more detailed requirements for this block type.

  • Ability to define the number of records in each of the three display types.
  • Filter by article type, topic and intended audience.
  • Integration with OG
  • More link with custom URL and text.

The content we are displaying is of the "article" content type. This content type has an "article_type" vocabulary that defines if it's a blog article, news article, etc. Topics and audience are a vocabularies used to categorize articles. This gives us three different option filters for vocabularies.

Setting Up Properties and Values

Let's define what properties our block type will have.:

class listing_bean extends bean_plugin {
  public function values() {
    return array(
      'filters' => array(
        'term' => FALSE,
        'topic' => FALSE,
        'audience' => FALSE,
      ),
      'items_per_page' => array(
        'large_image' => 0,
        'small_image' => 0,
        'listing' => 0,
      ),
      'more_link' => array(
        'text' => '',
        'path' => '',
      ),
    );
  }
}

Building The Form

Each of the properties are set via the block creation form. Let's define the form.

class

listing_bean

extends

bean_plugin

{
public function

form

($bean) {
$form = array();

    $options = array();
    if ($vocabulary =
      taxonomy_vocabulary_machine_name_load('article_type')) {
      if ($terms = taxonomy_get_tree($vocabulary->vid)) {
        foreach ($terms as $term) {
          $options[$term->tid] =
            str_repeat('-', $term->depth) . $term->name;x
        }
      }
    }

    $topic_options = array();
    if ($vocabulary =
      taxonomy_vocabulary_machine_name_load('topics')) {
      if ($terms = taxonomy_get_tree($vocabulary->vid)) {
        foreach ($terms as $term) {
          $topic_options[$term->tid] =
            str_repeat('-', $term->depth) . $term->name;
        }
      }
    }

    $audience_options = array();
    if ($vocabulary =
      taxonomy_vocabulary_machine_name_load('audience')) {
      if ($terms = taxonomy_get_tree($vocabulary->vid)) {
        foreach ($terms as $term) {
          $audience_options[$term->tid] =
            str_repeat('-', $term->depth) . $term->name;
        }
      }
    }

    $form['filters'] = array(
      '#type' => 'fieldset',
      '#tree' => 1,
      '#title' => t('Filters'),
    );
    $form['filters']['term'] = array(
      '#type' => 'select',
      '#title' => t('Article Type'),
      '#options' => $options,
      '#default_value' => $bean->filters['term'],
      '#multiple' => TRUE,
      '#size' => 5,
    );
    $form['filters']['topic'] = array(
      '#type' => 'select',
      '#title' => t('Topic'),
      '#options' => $topic_options,
      '#default_value' => $bean->filters['topic'],
      '#multiple' => TRUE,
      '#size' => 10,
    );
    $form['filters']['audience'] = array(
      '#type' => 'select',
      '#title' => t('Audience'),
      '#options' => $audience_options,
      '#default_value' => $bean->filters['audience']
      '#multiple' => TRUE,
      '#size' => 10,
    );

    $form['items_per_page'] = array(
      '#type' => 'fieldset',
      '#tree' => 1,
      '#title' => t('Items per page'),
    );
    $form['items_per_page']['large_image'] = array(
      '#type' => 'textfield',
      '#title' => t('Number of items with a large image'),
      '#description' => t('These items will be displayed
        first in the list and will include a large image,
        title, a short teaser and a read more link.'
),
      '#default_value' => $bean->items_per_page['large_image'],
      '#size' => 5,
    );
    $form['items_per_page']['small_image'] = array(
      '#type' => 'textfield',
      '#title' => t('Number of items with a small image'),
      '#description' => t('These items will be displayed
        second in the list and will include a small picture,
        title and short teaser.'
),
      '#default_value' => $bean->items_per_page['small_image'],
      '#size' => 5,
    );
    $form['items_per_page']['listing'] = array(
      '#type' => 'textfield',
      '#title' => t('Number of items in the listing'),
      '#description' => t('These items will be displayed
        last in the list and will include only a title.'
),
      '#default_value' => $bean->items_per_page['listing'],
      '#size' => 5,
    );

    $form['more_link'] = array(
      '#type' => 'fieldset',
      '#tree' => 1,
      '#title' => t('More link'),
    );
    $form['more_link']['text'] = array(
      '#type' => 'textfield',
      '#title' => t('Link text'),
      '#default_value' => $bean->more_link['text'],
    );
    $form['more_link']['path'] = array(
      '#type' => 'textfield',
      '#title' => t('Link path'),
      '#default_value' => $bean->more_link['path'],
    );

return $form;
}
}

The form method is based on the Bean object at its current state. New beans are initiated with the defaults defined in the values method. This is very useful because you know that value will always be there; there is no need to check for its existence. You can access the properties defined in the values method as properties of the object. Since we defined arrays as our properties, they are arrays on our object. This makes organizing data simple.

It also integrates very nicely with fieldsets. When the form is saved, it matched the name of the property in the values method against the $form_state['values], then stores the settings. When you define #tree=1 in the fieldset, it matches up with the array properties we setup in the values method,

Here is another useful example of a form method that integrates with the media module to provide the media selection UI. If you use the media module, you do not have to worry about setting the file entity status in the validation method.

public function

form

($bean) {
$form = array();

  $form['image_fid'] = array(
    '#title' => t('Image'),
    '#type' => 'media',
    '#description' =>
      t('The Uploaded image will be displayed.'),
    '#default_value' => isset($bean->fid) ?
      (array)file_load($bean->fid) : '',
    '#media_options' => array(
      'global' => array(
        'types' => array('image'),
        'schemes' => array('public', 'http'),
      ),
    ),
  );

  $options = array(
    'imagelink_150' => 'Scale to 150px wide',
    'imagelink_235' => 'Scale to 235px wide',
    'imagelink_320' => 'Scale to 320px wide',
  );

  $form['image_style'] = array(
    '#type' => 'select',
    '#title' => t('Choose an Image size'),
    '#options' => $options,
    '#default_value' => $bean->image_style
  );

  $form['image_text'] = array(
    '#title' => t('Text'),
    '#type' => 'textarea',
    '#default_value' => $bean->image_text,
  );

  $form['more_link'] = array(
    '#title' => t('More Link'),
    '#type' => 'textfield',
    '#default_value' => $bean->more_link,
  );

return $form;
}

Render and Display

Finally we tackle the render of the block. The view method in the plugin should only take care of the data aspect of the rendering. Be sure to use proper theme functions. The default view method expects you to return an render array.

Here is what we are using for the listing block type:

class

listing_bean

extends

bean_plugin

{
public function

view

($bean, $content, $view_mode = 'full', $langcode = NULL) {
$count = $bean->items_per_page['large_image'] + $bean->items_per_page['small_image'] + $bean->items_per_page['listing'];

    $query = new EntityFieldQuery();
    $query
      ->entityCondition('entity_type', 'node')
      ->entityCondition('bundle', 'article')
      ->propertyCondition('status', 1)
      ->propertyOrderBy('created', 'DESC');
      ->range(0, $count);

    if (!empty($bean->filters['term'])) {
      $query->fieldCondition('field_article_type', 'tid', $$bean->filters['term']);
    }

    if (!empty($bean->filters['topic'])) {
      $query->fieldCondition('field_topic_term', 'tid', $bean->filters['topic']);
    }

    if (!empty($bean->filters['audience'])) {
      $query->fieldCondition('field_audience_term', 'tid', $bean->filters['audience']);
    }

    $result = $query->execute();

    if (empty($result)) {
      $content['nodes'] = array();
    }
    else {
      $content['nodes'] = node_load_multiple(array_keys($result['node']));
    }

    $content['#theme'] = 'bean_list';
    $content['more_link'] = array(
      'text' => $bean->more_link['text'],
      'path' => $bean->more_link['path'],
    );
    $content['items_per_page'] = array(
      'large_image' => $bean->items_per_page['large_image'],
      'small_image' => $bean->items_per_page['small_image'],
      'listing' => $bean->items_per_page['listing'],
    );

return $content;
}
}

The view method is passed 4 arguments.

  • bean: The bean object that you are rendering.
  • content: The current content array. If you have fields attached this block type (bundle) then the rendered field will be in this array.
  • view mode: The bean entity view mode that is being rendered.
  • langcode: The language being rendered.

For this block type we want total control over what is rendered so we ignore the current content array. We use EntityFieldQuery to query entities. It's very easy for us query fields that are attached to entities without knowing the table structure. For more information about this, see the documentation for the execute method.

We aren't doing a bunch in this method. Basically just loading the nodes based upon the selected criteria and passing on other settings to the theme layer. The details of the theme functions are beyond the scope of this post, but we will look at them in a future post.

Giving Power to the Editor

All this gives the editor power to create her own blocks for querying content, without needing to deal with a relatively complicated interface.

This is just one scenerio for the use of Beans. For Energy.gov, we created more then ten different block types. Each of these types have a different purpose and an individual instance can be placed anywhere a block can. Stay tuned for how we created the layout of the blocks on the individual pages, and how we themed the output without resorting to custom template files.

Sep 07 2011
Sep 07

Views is an amazing module. The power it provides to build lists of content from within the UI is amazing. The plugin architecture is complicated but extraordinarily powerful. There are currently 125 Drupal 7 modules that extend the functionality of Views. So why didn't we use it for development of Energy.gov?

When we started development on Energy.gov, we took a step back. This would be our first Drupal 7 project. We took this opportunity to reconsider our development practices, and looked at every module we had been accustomed to using and asked two questions:

  • Does it provide a robust API (i.e. can we use it without the UI)?
  • Do we really need it? Are we using the module just because that's what you use when you create a Drupal site? Are we we pushing the module beyond it's original intent? Does this module try to solve too many problems?

Ok, so the second question is really 4 questions. In the case of Views, we could answer "yes" for the first question, we couldn't for the second set of questions. There was no specific requirement for a visual query builder. In past projects, we often spent as much time investigating quirks about how Views (or any other large module) works as we do in custom development.

In the end, one argument against using Views for our content queries overrode all others: we wanted our client to use Views. Our client had specified that once they received the sites, their own developers would be using Views to build blocks and pages themselves. We knew that if we worked in Views for our own work, the Views we created would eventually be exposed to them, which leads to possibilities of regression and error. We wanted our core querying functionality to continue to function without concern that it might be tampered with. We explained our concerns and our proposed approach to the client, and they agreed to it.

So, what would be used in its place? In short, the answer is EntityFieldQuery. EntityFieldQuery is a class, new to Drupal 7, that allows retrieval of a set of entities based on specified conditions. It allows finding of entities based on entity properties, field values, and other generic entity metadata. The syntax is really compact and easy to follow, as well. And, best of all, it's core Drupal; no additional modules are necessary to use it.
A typical EntityFieldQuery that looks up the 5 most recent nodes of types article, page, or blog created by the current node's author, that are published (a typical "more by this author" query) might look like the following:

// get the current node; we're assuming for this example we know we are on a node.
$node =

menu_get_object

();$query = new

EntityFieldQuery

();
$query->entityCondition('entity_type', 'node')
->propertyCondition('status', 1)
->propertyCondition('type', array('article', 'page', 'blog'))
->propertyCondition('uid', $node->uid)
->propertyOrderBy('created', 'DESC')
->range(0, 5);
$result = $query->execute();

Note a few things here:

  • All methods of the EntityFieldQuery class generally chain; that is, they return the modified EntityFieldQuery object itself. You may be familiar with this sort of construct from jQuery.
  • Like Drupal 7's query building in general, the methods of EntityFieldQuery have default operators that they assume, and are fairly agnostic in terms of what sorts of values they'll accept. So for example, propertyCondition() has either '=' or 'IN' as its default operator, depending on whether you pass it a string/number or an array as a comparison value. Of course, if you want a different comparison, i.e. '<>' or 'NOT IN' or such, you can pass that in explicitly.
  • Note that we're not querying fields just yet. EntityFieldQuery really starts to shine when you start querying field values, because it takes care of finding the appropriate field table and doing joins for you.

EntityFieldQuery is powerful, but it didn't do everything we wanted it to. We used Organic Groups on this site, and we wanted our queries to be aware of groups without adding that each time. Also, in almost all cases we were querying for nodes which were published, and we often wanted a reverse chronological ordering. Fortunately, EntityFieldQuery is a PHP class, so it is very easy to extend. We created EnergyEntityFieldQuery.

class

EnergyEntityFieldQuery

extends

EntityFieldQuery

{
/**
   * apply some defaults to all instances of this object
   */

public function

__construct

() {
$this
// we're interested in nodes
->entityCondition('entity_type', 'node')
// Default to published
->propertyCondition('status', 1)
// default to reverse chronological order
->propertyOrderBy('created', 'DESC');

    /* make assumption that we want group content; see method below */
    $this->setPrimaryAudienceCondition();
  }

  /**
   * Helper function for querying by topic vocabulary terms.
   * Will do lookup from term names as a convenience; tids are also recognized.
   *
   * @param $topics
   *    String, numeric or array; converts to array if necessary
   */

  public function setTopicCondition($topics) {
    $topics = !is_array($topics) ? array($topics) : $topics;
    if (count($topics)) {
      // if a term is not numeric, do a lookup for each term and replace it with its tid
      foreach ($topics as $idx => $topic) {
        // try to find a tid for non-numeric terms
        if (!is_numeric($topic)) {
          // look it up
          $vocab = taxonomy_vocabulary_machine_name_load('topics');
          $candidate_terms = taxonomy_get_term_by_name($topic);
          foreach ($candidate_terms as $candidate) {
            if ($candidate->vid == $vocab->vid) {
              $topics[$idx] = $candidate->tid;
            }
          }
        }
      }
      // field_topic_term is our term reference field for the Topics vocabulary
      // once we have converted all our terms to tids, we set them as a field condition for our search
      $this->fieldCondition('field_topic_term', 'tid', $topics);
    }
    return $this;
  }

  /**
   * Add the field condition to search by the primary audience field.
   * EnergyEntityFieldQuery makes the assumption that we want content that matches the current group.
   * The class will provide an undo method
   *
   * @param $gid
   *   An array or integer for the gid(s) to search the primary audience field
   *   based on. If empty, will try to pull current group from the page context.
   */

  public function setPrimaryAudienceCondition($gid = NULL) {
    if (empty($gid)) {
      $current_group = og_context_determine_context();
      $gid = $current_group->gid;
    }

    if (!empty($gid)) {
      $this->fieldCondition('group_audience', 'gid', $gid);
    }
    return $this;
  }

  /**
   * Unset group content conditions
         *
         * Use this method if you do not want to filter by group content.
   */

  public function clearAudienceConditions() {
    foreach ($this->fieldConditions as $idx => $fieldCondition) {
      $field_name = $fieldCondition['field']['field_name'];
      if (($field_name === 'group_audience') || ($field_name === 'group_audience_other')) {
        unset($this->fieldConditions[$idx]);
      }
    }
    return $this;
  }

  /**
   * If we're currently on a node, and if the entity_type is node, exclude the local node from the query.
   * This prevents the node the user is viewing from showing up in queries.
   */

  public function excludeNode($nid) {
    if (!$nid) {
      $object = menu_get_object();
      $nid = $object->nid;
    }
    if (!empty($nid) && $this->entityConditions['entity_type']['value'] === 'node') {
      $this->propertyCondition('nid', $nid, '<>');
    }
    return $this;
  }

}

Note that it is also possible to override the protected methods of EntityFieldQuery itself. This may be useful, for example, if you have more complex propertyCondition needs than EntityFieldQuery itself provides.

We found that we were able to solve around 90% of our content listing use cases with EnergyEntityFieldQuery. We build a simple UI to allow users to pass parameters to the class, which allowed them to easily create dynamic query.

Let's run through a simple example of displaying a list of nodes. Remember that this is not sample code. This is real code we are using on Energy.gov.

The easy part. Remember using EnergyEntityFieldQuery allows us to restrict our queries to OG group content and sets the default entity type to node.

$query = new EnergyEntityFieldQuery();

We only want nodes that are rebates.

$query->entityCondition('bundle', 'rebate');

EntityFeildQuery has built in paging and table functionality which is trivial to add.

$query->pager(10);

We are pull conditions from the query string to create taxonomy filters.

function energy_rebate_savings_search($filters = array()) {
// This maps the vocabulary machine names to the field names for term references to that vocabulary.
$term_field_map = array(
  'rebate_provider' => 'field_rebate_provider',
  'rebate_savings_for' => 'field_rebate_savings_for_short',
  'rebate_eligibility' => 'field_rebate_eligibility_short',
);
// Get the non 'q' parameters
$params = drupal_get_query_parameters();
$param_filters = array();
foreach (array_keys($term_field_map) as $vocab) {
  if (isset($params[$vocab])) {
    $param_filters[$vocab] = $params[$vocab];
  }
}
$filters = array_merge($param_filters, $filters);

Set the condition for the terms if there are any

foreach ($filters as $filter => $value) {
  if ($value != 0) {
    $query->fieldCondition($term_field_map[$filter], 'tid', $value);
  }
}

Run the query

$result = $query->execute();
// process and theme the results, not part of this code example
}

This returns to us an array of entity ids (nids in this case) that match the conditions we've given it. Once we have those, we can process them any way we like. Our preferred method is to use Drupal 7's greatly expanded concept of view modes, which we will talk about in a future post in this series.

So, this is all well and good. But how do we get these to display? How do we place them? One of the advantages of Views is that it can provide blocks of your constructed query which become available to the system immediately.

The answer to that, for Energy.gov, is Beans. Bean is a contributed module we developed in the course of this project which creates blocks as entities. In the next post, you will learn how we used the Bean module to replace View blocks, and how Beans can be used to do much more.

Aug 05 2011
Aug 05

It's been an exciting week for us here at Treehouse Agency.  We've been hopping up and down for weeks wanting to tell everyone about our work with Energy.gov, and now that it's finally launched, the response has been exhilarating.

Yesterday, our CEO Michael Caccavano posted his thoughts on the launch and noted some of the site's features.  Today I've put out our official press release, sharing the news from our perspective as a company, including some more hints at the Drupal coolness our team used. 

I'd love it if you read the whole release :), but in case you've actually got code to write today, here's the fun part:

Development highlights of the relaunched Energy.gov include:

  • Data Visualization API. Historically, data rendering libraries have required specialized data handling to work with disparate data structures. Treehouse developed the Data Visualization API to define a standardized data structure for visualizations, creating an abstraction layer that allows data to be rendered in any library for which an adapter has been written. This API is further combined with the Drupal 7 Entity API to create a very flexible system that converts these visualizations to Drupal entities.

  • Sophisticated workflow. Using the State Machine module, the Treehouse team created a sophisticated content workflow pattern customized to the Department of Energy's editorial review process. (State Machine provides a flexible system for building rules and reaction associated with changes to the status of an entity.) With the implemented workflow content can be written, edited, and reviewed by multiple staff members prior to being publicly visible. Writers can draft new articles or prepare revisions for existing articles, then send the article to an editor for review and a decision to approve for publication or return for revisions.

  • Drupal Bean module. Bean provides a means of producing block types with custom fields (much like custom content types). Using code-based configuration, it bypasses the user interface and feature management issues that accompany UI-based configurations. The module has been contributed back to the Drupal community and is presently available as an alpha release; the project page for Bean can be found at http://drupal.org/project/bean

Additional project highlights include easy content sharing between sites on the platform, stunning data mapping via MapBox integration, and substantial editor empowerment; the new system allows editors to create and deploy new sites on the platform without relying on developers or development hours.

Indytechcook (we call him "Neil") and zroger (aka "Roger") tell me that you'll be seeing a bunch of code from this project released to the community.  In the meantime, our devs are getting ready to put out some of their awesome blog posts giving you a deeper look at some of the tech behind this project.

Stay tuned for more on the Energy.gov Drupal 7 relaunch!

PS - Here's the slicky little screenshot reel I threw together for the press release. The site looks pretty cool, right?

Aug 04 2011
Aug 04

I’ve been waiting a long time to tell you something.

Several months ago, Treehouse was signed on by (Top 100 Federal Prime Contractor) Energy Enterpise Solutions as the development partner for Energy.gov’s massive, eleven-site Drupal relaunch.

Today, I am ecstatic to announce that the platform is live!

The Energy.gov relaunch represents a huge step in the ongoing progression of major government agencies moving to Drupal; these agencies have included the US House of Representatives, The White House, and the Department of Commerce, among others. As with other federal and state projects, the Department of Energy has proven to be an enthusiastic supporter of the Drupal community, with the project yielding numerous patches and even a new module or two to be contributed back to the community.

New Energy.gov homepage

The project was executed by an all-star team that included not only Treehouse but also the design and usability gurus at  HUGE and Drupal support mavens at Acquia, all brought together by Energy Enterprise Solutions. Cloud-based hosting is provided by BlackMesh.

Running on Drupal 7, the multisite platform gives the Department of Energy the tools they need to both provide a more engaging, enjoyable user experience and realize operational efficiencies.

Highlights of the project include:

  • Easy sharing of content between sites on the platform. Editors can now write content that is easily deployed to multiple sites, rather than each site having its own completely distinct resources.

  • Data visualizations and mapping. Our devs cooked up some very cool things in rendering data visualizations and maps (using the super-slick MapBox). You’ll see more on this in the next week or so as they put together blog content about the technology implemented.

  • Major editor empowerment. Along with creating content that can be applied anywhere within the platform’s network, it is now actually possible for editors to create new sites without relying on the development team.

  • Drupal breakthroughs. Innovations in using Drupal include sophisticated workflows using a state machine, and the new Bean module (currently available as an alpha release). As with the data renderings, you’ll hear more about these innovations in the coming weeks.

  • Independence. The Department of Energy now owns their site platform; its open source code means that they will no longer have to rely on a single vendor for support or site updates.

Finally, a big thank you and congratulations to Cammie Croft and Liz Meckes at the Department of Energy. It has truly been a pleasure working with the DOE team on this project.

As the CEO of Treehouse, I couldn’t be more proud of our team, nor more awestruck by what they have achieved. This project is as much of a milestone for us as I hope it is for the Department of Energy, and I am very hopeful that it will lead to more such projects with our public agencies in the future.

Keep an eye out for tech details from our team in the coming weeks. Trust me, it will be worth the read. ;) In the meantime, check out Energy.gov and join us on Twitter (@treehouseagency)!
Jul 25 2011
Jul 25

This past weekend was CapitalCamp, Washington DC's first DrupalCamp, and Treehouse presented three exciting sessions.

Treehouse CEO, Michael Caccavano, was on a panel with fellow CEOs of web technology companies in Growing a Drupal Agency Beyond the Garage.

Steven Merrill and I talked about guidelines for web performance optimization, techniques for improving front-end performance, and ways to automate the testing and monitoring of front-end functionality in Pages on a Silver Platter: Automated Front-End Testing and Tuning. If you saw this talk, we'd love your feedback! Please leave your quick review here: http://tha.cm/capitalcamp-front-end-testing

Then, Patrick Macom and I presented on Raphaël JS, an interactive drawing library for vector graphics on the web. The session covered the basics of the Raphaël JS API and then moved into integration strategies with Drupal in the talk, "Outside the Garden: Intro to Raphaël JS." If you saw this talk, we'd love your feedback! Please leave your quick review here: http://tha.cm/capitalcamp-raphael

Below are our presentations from the event, unfortunately the event was not video recorded so there won't be videos of the talks.

If you happened to be at CapitalCamp, we'd love your feedback on our sessions! Please fill out a quick review on SpeakerRate here: http://tha.cm/rate-drupal-and-raphael

If you missed us at CapitalCamp, don't worry! Treehouse Agency will be making appearances again this Friday, July 29th, 2011 at DrupalCamp Philadelphia. Sessions haven't been announced yet but our own JR Wilson has proposed, Node.js Integration with Drupal 7 and Steven Merrill has proposed Coat Your Website with Varnish.

Pages on a Silver Platter: Automated Front-End Testing and Tuning:

Outside the Garden: Intro to Raphaël JS:

Don't Forget! Treehouse Agency will be making appearances again this Friday, July 29th, 2011 at DrupalCamp Philadelphia. Sessions haven't been announced yet but JR Wilson has proposed, Node.js Integration with Drupal 7 and Steven Merrill has proposed Coat Your Website with Varnish. We'll see you there!

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web