Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough
Oct 09 2014
Oct 09

Field collections is a nice contributed module that extends the default Drupal entity functionality by creating a new entity field that can be composed by other fields. With this module we solve problems like creating complex entities where we want to store multiple different values into one single field. This works because Drupal lets us assign unlimited values for a field, which links to the field collection entity where we have multiple fields, thats how we "grouped" multiple fields into one field.

At a basic usage, field collections is not difficult to add to our entity, but when we want to do some advanced stuff, this module can be very complex. Here I will show how to do some magic with it, programatically.

Obtain the values of the child fields

How to access the field collection values of an entity? Field collections store field collection items, which are the values of this kind of field. The field collection items use an ID, and with this ID we can find other fields. It also defines a new entity type called “field_collection_item”.

  1. Access to all values of a field collection on an existing entity, using Field API // Get the items of a field collection, from a node
    $field_node_example_values = field_get_items('node', $node, 'field_node_example');

    // Use a field collection helper function to get all the field collection item ids
    $field_node_example_ids = field_collection_field_item_to_ids($field_node_example_values);
    // By default, Drupal don't load the field collection items
    $field_node_example_fc_items = field_collection_item_load_multiple($field_node_example_ids);

    // Loop over every field collection item and get the values for each field
    foreach ( field_node_example_fc_items as $item) {
        $fc_field_values = field_get_items('field_collection_item', $item, 'field_inside_fc');

  2. Access to all values of a field collection on an existing entity, using an entity wrapper

    $node_wrapper = entity_metadata_wrapper('node', $nid);
    // Loop over the collections until we find which we have to modify
    foreach ($node_wrapper->field_example->value() as $field_example_value) {
       // Wrap it with Entity API
       $fc_wrapper = entity_metadata_wrapper('field_collection_item', $field_example_value);
       $fc_field_value = $fc_wrapper->field_example_child->value();

Alter the child fields

Now let's see how we can modify or remove items from an existing field collection of an entity. To do things easily, I prefer to use here an entity wrapper.

  1. Alter the value of one of the fields of a concrete field collection item $fc_item = field_collection_item_load($item_id);

    // Check there's no problem with the item
    if (!$fc_item) {

    // Wrap it before modifying
    $fc_item_wrapper = entity_metadata_wrapper('field_collection_item', $fc_item);
    $field_value = $fc_item_wrapper->field_example->value();
       ... change $field_value ...

  2. Delete a field collection item

    $fc_item_wrapper = entity_metadata_wrapper('field_collection_item', $item_id);

Create a field_collection_item

Field collection items use a “entity host”, which is the entity to whom the field collection is attached. To add a new field collection item to an existing field collection field, we have to create it and set its entity host, then we can assing values to every single field of the field collection item and save it.

// Create a field_collection_item entity
$fc_item = entity_create('field_collection_item', array('field_name' => 'field_example_is_a_field_collection'));

// Attach it to the node
$fc_item->setHostEntity('node', $node);

// Check there's no problem with the item
if (!$fc_item) {

// Wrap it with Entity API
$fc_item_wrapper = entity_metadata_wrapper('field_collection_item', $fc_item);

// Assign values to its fields
foreach($fc_item_values as $field_name => $field_value){


Alter a field_collection field into a form

When we are creating or editing a node (or any other entity) we use a form, and to modify it usually the hook_form_alter is used. Here are some interesting examples of how to alter the field collection widget.

  1. Change the options for a select box on all the field collection items and don't display the remove button for the first item. $field_name = 'field_collection_example';
    $fp_langcode = $form[$field_name]['#language'];
    $options = array( … );
    foreach (element_children($form[$field_name][$fp_langcode]) as $child) {
      // All the unlimited values fields have the “Add more” button as a child, but it's not a field collection item
      if (is_numeric($child)) {
        $form[$field_name][$fp_langcode][$child]['field_selectable'][$fp_langcode]['#options'] = $options;

        // Don't display the remove button for the first field collection item
        if ($child == 0) {

  2. The field collection field stores some state information about it on $form_state. With this information we can know, realtime, how many items we have on the form and take decisions about it. For example, we have an unlimited values field collection field, but we want to limit the number of items depending on some certain circunstances. With this example, we can control a field to not display more than 3 field collection items on the form. $field_name = 'field_collection_example';
    $fp_langcode = $form[$field_name]['#language'];

    // Get state information about this field
    $fp_parents = $form[$field_name][$fp_langcode]['#field_parents'];
    $field_state = field_form_get_state($fp_parents, $field_name, $fp_langcode, $form_state);

    // The field state will change everytime we click on “Add more”, after the ajax call is triggered and the form is re-rendered to display the changes.

    // The user can see a maximum of 3 field collection items
    $items_limit = 3;
    if ($field_state['items_count'] < $items_limit) {
      // I want to change the “Add more” button title
      $form[$field_name][$fp_langcode]['add_more']['#value'] = t('Add another');
    else {

Clone field_collections from an existing node to a new node

Finally, here is an example of how to assign some default values to a field collection on a node creation form. As you can suspect, this is tricky if the value to clone is only one field collection item, but if we want to clone more than one, it's a problem because Drupal by default will only provide the form widget to enter the first item values (and wait for you to click on “Add more”).

The technique is to reproduce what Drupal does when the Field API attaches the fields to the node form. Using this way, Drupal will provide all the form structure we need to see all the values that come from the node we want to use as origin, but this has a problem and is that doing it this way we are assigning the same field collection items to two different nodes and if one of the nodes changes the values of one of the items, this will be reflected on the other node. The solution is to reset the item_id and revision_id values for the items on the new node, so we get the default values we want and they will be stored as new field collection items attached to the new node.

    $original_node = node_load(123);
    $node_type = 'my_custom_type';
    // Get the fields defined for this node type;
    $node_fields =  field_info_instances('node', $node_type);

    // Re-create the fields for the original node, like when editing it
    $tmpform = array();
    $tmpform['#node'] = $original_node;
    $tmpform['type']['#value'] = $node_type;
    $tmpform_state = array( 'build_info' => array('form_id' => $form['#form_id']) );
    field_attach_form('node', $original_node, $tmpform, $tmpform_state, entity_language('node', $original_node));

    // Here we have on $tmpform the form structure we need and with the default values.
    // We can choose what fields to clone, but in this example we will loop over all the node fields and clone all of them
    foreach($node_fields as $field_name => $field_settings) {
      // Copy the form structure
      $form[$field_name] = $tmpform[$field_name];
      // Copy state information about this field
      $form_state['field'][$field_name] = $tmpform_state['field'][$field_name];

      // When copying the field_collection structure, reset the id of the entities and
      // they will be created again with a new id.
      $langcode = field_language('node', $original_node, $field_name);
      if ($form_state['field'][$field_name][$langcode]['field']['type'] == 'field_collection') {
        $field_childs = &$form_state['field'][$field_name][$langcode]['entity'];
        foreach(element_children($field_childs) as $idx => $fc_entity) {
          $field_childs[$idx]->item_id = NULL;
          $field_childs[$idx]->revision_id = NULL;

Jun 27 2013
Jun 27

In a workshop we did at FUxCon 2013, the grand meetup of Frankfurt users' groups, we presented a comparison of four web frameworks, CakePHP 2, Django 1.5, Drupal 7, and Symfony 2. Our goal was to compare the different approaches in the four frameworks and to introduce Behavior Driven Development at the same time. Drupal stands out as the one system that requires the least custom code to implement our example behavior.

We based our comparison on a simple portfolio website, consisting of a start page with marketing text and a project list below it. From the start page, more pages with project lists can be reached, using a paginator at the bottom of the page. Each project entry shows title, teaser text, and a small picture. Project detail pages with a big photo, list of topics, title, and description can be reached by clicking on the teaser titles or pictures. The descriptions are entered via multiple editors, using Markdown. Pictures are automatically scaled to 200x200px on list pages and 380x380px on project detail pages. Nothing special really. Here is a product canvas for this site:

Product canvas of a portfolio site

For our comparison, this portfolio page should be created in all four frameworks. We captured the behavior of the site formally, using the Domain Specific Language (DSL), Gherkin. Behavior definitions in Gherkin are nothing but user stories, well-known from Agile software development, which, in an Agile project, are typically maintained by the product owner. This role is often filled by non-technical people. So the Gherkin language constructs strive to read like natural language.

User stories captured in Gherkin are called features. Features are made concrete by means of scenarios. Scenarios, in turn, are made executable by step definitions, which are nothing more than functional tests we know from classic test-driven development (TDD). The fact that scenarios are written in Gherkin first, and thus are readable by non-technicians, makes Behavior Driven Development (BDD) special. This closes the gap between requirements definition and software development.

BDD, the approach of formally defining behavior for a software project, which can then be validated, has been developed since 2003 by Dan North. It was inspired by test-driven development, which was still in its infancy at that time. Dan North first developed a Java-based tool for his methods, but since then, the Ruby-based Cucumber has become quite popular. These tools also first implemented Gherkin.

Behat is the corresponding tool for the PHP world. We used Behat to define our behaviors and tests. All four implementations already fulfill the behavior as described above. However, they are different in a few minor aspects, thus, we have not yet totally reached our goal.

All four implementations are extensively documented with their aspects installation, configuration, modeling, business logic, appearance, user accounts and permissions, topic lists, picture upload and scaling, edit forms, text formatting, and test data generation. They are licensed under the General Public License (GPL) and are available as repositories on Github and for free download as virtual appliances. The behavior definitions, scenarios, and step definitions are available on Github for free as well. Point of entry for this project is the web site cocomore.github.io/fuxcon2013. We look forward to your comments, suggestions, pull requests, and alternative implementations in other frameworks.

Oct 10 2012
Oct 10

So, you’ve got a brand spanking new MacBook and you want to configure the default apps to connect to your Gmail account and other Google services like calendar and contacts? Step this way, I’m going to show you how.

iPhone & iPad Users

: You might be tempted to try the Microsoft Exchange trick that your iOS device can do which seamlessly connects Mail, Calendar and Contacts with Google automatically. This will not work on a MacBook!

Getting Started

Ready? Let’s get started. Follow these steps:

  1. Fire up your System Preferences app and browse to Mail, Calendar and Contacts (it’s under Internet & Wireless)
  2. You may already have an entry for iCloud. That’s fine. Click the little + button at the bottom to add a new account.
  3. Click Gmail to continue, then fill in your Gmail account details. Also, make sure your display name is configured properly, because that’s what’ll appear on your emails when you send them out.
  4. Click OK to continue and you’ll be prompted to select the Google service you want to synch with. I chose Mail, Calendars and Reminders and Messages (I mean, what the hell is notes anyway?)

Verifying It

You’ll want to verify that this has worked. Open up the Mail app and the Calendar app and you should see messages start to filter through. On your calendar you should see an entry for your Google calendar. Cool.

Power user tip: If you’ve got access to other people’s calendars in Google Calendars, you can also view those in the MacBook calendar app. Simply browse to the app’s Preferences dialog, select Accounts, then Google and click on the Delegation tab. You should see any extra calendars you have access to and be able to switch them on.

Syncing your contacts

Finally, for whatever reason, Contacts don’t seem to automatically be synchronised. Never mind. There’s a quick fix for this.

  1. Open your Contacts app.
  2. Browse to Preferences, then click Accounts. Click the option that says On My Mac.
  3. Check the box that says Synchronize with Google, then enter your Gmail address and password when prompted.
  4. Synchronization will take a few seconds, then you’re done. Hooray!

Et voila! You should now have all your major apps synchronized to Google, giving you the power of local apps with the resilience of Google’s cloud-based data storage!

Jul 05 2012
Jul 05
New Spammer Registered

Rules is an especially useful Drupal module for all kinds of tasks. One use you might want to put it to is providing admin notifications of certain events on your site, e.g. user registrations and the creation of new comments and content by these “untrusted” users (assuming your use case allows them to create any content at all). I recently created such rules to help monitor the creation of users, content, and comments on drupal.cocomore.com/.de. Since we use the Project module (and supporting code) to host and track issues on some Drupal modules, we allow users to create accounts and “Issue” nodes. But there hasn’t been much recent change to the modules we host, so most of the “users” turn out to be spamming scumbags who post “issues” with links to questionable sites (you know the type). Since we allow anonymous users to comment on our blog posts, we also get our fair share of comment spam, but a tricky Captcha (we’re using Riddler, these days, to filter out visitors who don’t know or can’t take the time to search the answers to simple Drupal trivia questions) helps keep comment spam to a minimum. Keeping vigilant about stomping out spam is important since leaving spam published looks unprofessional and is bad for SEO… and since it also attracts more spam (spammers see that your site leaves spammy links in place); but of course it’s also important to keep an eye on the valid posts, too, and to respond to them in a timely fashion.

So we will assume that you have a site without a massive flow of new user registrations or new content and that you want to be alerted with some useful information whenever these events occur so that you can take appropriate action (block users and clean out the spam… or respond to valid content/comments). This article will lead you, step-by-step, through the creation of three different rules on both Drupal 6 and Drupal 7 -based sites, identifying particular set-up differences between these versions of Drupal/Rules. The three events we want to create Rules for are:

  1. New user registered
  2. New comment posted (by non-staff user or “untrusted” user)
  3. New content posted (again, by some kind of “untrusted” user)
In each case, we simply want to send an HTML email* to notify at least one member of staff (anyone with the admin role and, in the case of comments on blog posts, we want to also email the article’s author.) This article does not get into the various particulars of configuring your server to be able to send mail; there are a number of factors which might differ from server to server and it’s not really within the scope of a Drupal-related article.
*Note: This article also does not cover setting up HTML mail, but some modules, such as Mime Mail help make this a relatively pain-free process and provide a “send HTML mail” action for Rules. Adding specialized modules is probably not justifiable if you don’t plan to use HTML mail for anything more than admin notifications, but if you want to email users, such modules can help you create much more attractive and useful emails.

In every case, creating a new rule starts by going to the “add rule” page:
D6: admin/rules/trigger/add
D7: admin/config/workflow/rules/reaction/add

Notify admin when a new user registers

This is a simple rule which sends an HTML email with a link to a new user’s profile, along with their username. If you allow users to register themselves on your site, you will likely notice patterns that persistent spammers follow and be alert enough to just block the most suspicious user accounts before they even start spamming your site. I won’t specify the suspicious patterns I’ve been reacting to here (I don’t want to teach spammers how to be sneakier or more effective), but if you have a spam problem, you probably already know the patterns or will quickly recognize them.


Adding a new rule to react to user registrations

The first step is virtually the same on both Drupal 6 and Drupal 7. Pick a name for the rule that you will not be confused by later, add tags (categories) if you have lots of rules, or plan to make lots, and select the event, “After saving a new user account” (“User account has been created” on Drupal 6).

Drupal 6 - Rules Event is a “new user account”Drupal 7 - Rules Event is a “new user account”

Click “Add action”

The “Add action” pre-step is also almost identical in Drupal 6 and Drupal 7 and we will skip over illustrating this in the following rules:

Drupal 6 - Rules “Add Action” buttonDrupal 7 - Rules “Add Action” button

Add action “Send mail” or “Send HTML mail”

In Drupal 6, the options for sending an HTML mail (provided you have Mime Mail installed) are separate from the rest of the “Send mail…” options. In Drupal 7 they all appear together in the “System” options. Either way, you can choose to send an email to an appropriate person or group of people. The “arbitrary email address” options can be useful if you want to send email to more than one person, regardless of their site roles. You could, for example, send a mail to the author of content that received a comment (using a “replacement pattern”, i.e. “token”) and/or to a particular person. More than one email address (or token substitute) can be added to recipient fields as long as the addresses are comma-separated.

drupal6-rules-send_html_mail.pngDrupal 7 option - Send HTML mail with Rules

Complete the email subject and body

The email “subject” and “body” fields can accept a variety of tokens or “replacement patterns”. In Drupal 7, these are apparently separate from “Tokens” provided by the Drupal contrib module, Token, which of course you also have installed; the Rules replacement patterns are available even if Token is missing or disabled, but in Drupal 6, you need the Token module enabled in order to have “replacement patterns” available for use.

Drupal 6 - Configure Rules message for new user registrations drupal7-configure-message-new-user-4.png

Download import code for this rule

Rules allows you to export and import rules from saved code, so I have attached an export of this rule. Even if you don’t want exactly the same action, this could be useful as a starting point for a new rule on your site. There are four versions of the import code attached, two each for Drupal 6 and Drupal 7 (for each version of Drupal, there is one version of the rule which uses HTML mail for the admin notification email, and one which just sends a plain mail.)


I suggest initially triggering a very simple rule to send a very simple email (to test that you get your message) and then build up the complexity a small step at a time, testing as you go.

AttachmentSize 1 KB 666 bytes 1.99 KB 2.76 KB
Jun 04 2012
Jun 04

It’s considered “best practice”, if a module creates any variables, to delete those variables in the module’s uninstall function. Before Drupal 7, this was done in a call to db_query(). But with the “new” DBTNG (Drupal 7 Database API), using db_query() is no longer recommended. See the documentation for db_query():

Do not use this function for INSERT, UPDATE, or DELETE queries. Those should be handled via db_insert(), db_update() and db_delete() respectively.

However, browsing through (a relatively small set of) Drupal modules I have for my local Drupal 7 installations, I still see a number of modules which are using db_query to delete variables, typically something like this:

("DELETE FROM {variable} WHERE name LIKE 'some_module_%'");

Let’s rewrite this fictional module’s call to db_query() to instead use db_delete():

condition('name', "some_module_%", "LIKE")

But there is another problem with this code. Which is? That’s right… it uses a wildcard that might apply to another module’s variables. What if someone else writes a “some_module_extended”, “some_module_plus”, or “some_module_rethunk”*? Then our db_delete() function call will delete variables from those modules, too. This is not at all a stretch of the imagination with modules that provide an API or which are widely used and might get forked. So what is better practice? Well, of course if your code is aware of the names of all your variables, you could easily loop through an array to remove them. Or, if your module dynamically creates variables based on user input, you could store the names of these user-created variables in another variable, then do the looping. Or, of course, you could also create your own table to store your module’s “variables” and that table would automatically be dropped whenever your module is uninstalled.

Another point worth bearing in mind—I don’t see all modules taking care of this—is that when you delete variables it is good to also clear the variable cache. Only some of the modules which delete variables using db_delete() seem to remember this:

('variables', 'cache_bootstrap');

But the best practice is to use variable_del()

Of course you would be best off looping through all your variable names using variable_del(), which also automatically clears the variable cache. If you know the names of all your variables (or can store them as dynamic variables are created), this would most certainly be “best practice”. It will also remove the deleted variable from the global $conf array.


To clarify and correct code, as suggested in comments, below.

* The former English (as a foreign language) trainer in me requires me to add the following caveat: 'thunk' is not a word in proper English (nor is 'rethunk'), but Oxford English Dictionary defines it: “informal or humorous past and past participle of think” : : who would've thunk it? (Just so's yah knows I ain't totally illegiterate. ;-) )

May 18 2012
May 18
Share this

The following transcript is for the video at http://www.youtube.com/watch?v=XfPKKisE88w :

[embedded content]

Hello, my name is Aaron Winborn. I am a developer for Advomatic, the author of Drupal Multimedia, and a contributor and a co-maintainer of several Drupal modules, including the Media suite of modules.

Today, I will demonstrate a new feature of the Media: YouTube module: browsing and searching videos directly from YouTube, in the media browser itself. So first, let’s set up our environment.

We are assuming that you already know how to install Drupal. If not, you can find information at Drupal.org.

So right now we are at the modules administration page. We are interested in the modules under the Media package. You will need to install and enable the File Entity module (version 7.x-2.x), and the same version of the Media module.

We will not enable the included Media Field module; it is there for legacy purposes, and has been deprecated in favor of core’s File Field.

The Media Internet Sources module, included with the Media module, is a dependency of the Media: YouTube module, so we will enable that.

Next will be the Media: YouTube module, also version 7.x-2.x.

Finally, we will install the WYSIWYG module.

Let’s start by configuring WYSIWYG. We do that by going to Configuration > Content Authoring > WYSIWYG profiles. Note that I have also installed and enabled the Admin Menu module and the Admin Menu Toolbar module, which gives us the fancy drop-down menus for administration that you see here.

Now in order to use WYSIWYG, you need to have also installed a third-party WYSIWYG library, such as CKEditor or TinyMCE. You need to follow the instructions with the WYSIWYG module to install that, although it is quite simple actually. You just download and unpack the file into the sites/all/libraries folder. You can see that I am using CKEditor here.

The WYSIWYG module allows us to set up profiles for the various text formats on our site; in this demo, we will edit the Filtered HTML format.

Open up the buttons and plug-ins field set next. Then check the Media Browser check box. That will add the media browser button to our WYSIWYG editor, which we will see soon.

In order to use that however, we need to configure the filter in question. In fact, I believe that if we do not do this step 1st, we will get an error message, complete with a link to the format configuration page.

On this page, we need to check the box next to “Convert Media tags to markup”. That is the answer to the number 1 support question that we get in the Media queue, which is, “Why is there bracketed goobly gook instead of my images?”

So now, as we will see, everything should be working now. So let’s test it.

Here on the create article page, we see a fancy button on the body text area! Let’s click it.

And there we go.

These are thumbnails being pulled directly from YouTube. How about that?

And there is even a ghetto pager, or at least previous/next links.

And you can also search YouTube directly from our browser.

So now we will select a video and submit it. Add a title and save the node. And there we go.

And that’s it really. Well, almost.

There are some more settings, specifically here to control which tabs show up for WYSIWYG. Note that at the time of this demonstration, you will not have this functionality unless you install the patch over at node 1434118.

To complete the demo, we will also do the same for fields. Let’s add a field to hold YouTube videos. We will call it Media, and it will be a file field with a Media file selector widget.

Here, let’s reorder it as well for the demo.

We leave everything at their default settings.

Hold on, I forgot that we need to allow the YouTube URI scheme. And the video file type.

So now we will create a new article, and select the media.

And here we have all the tabs available to our browser, including the new and improved YouTube tab.

And also, let us look at another new feature of the media module: My files!

This has been a long-awaited feature for the Media module as well.

Now here comes the 2nd most asked question in the support queue: “How come there is a link to my file, rather than the file itself?”

Let’s just fix that now.

Now we are in the file type administration page, where we can configure the display for each of our file types. Note that we can also add fields to our files, although we are not going to do that in this demo.

We will jump to the video display...

No, we want to make sure that our large formatter is set up properly for YouTube. And it is, so let’s set up that as the formatter for our Media file field.

And there it is, as a generic file, which is simply a link to the file stream itself. We will change that to rendered file. And then we set the view mode to large.

While we are in there, we can do the same for our teasers. We will just set that to the preview view mode, which by default will display a thumbnail.

Whoops, I forgot to save it. Let’s just do that again.

And there is the video.

And there is the thumbnail.

Well done!

Apr 20 2012
Apr 20

How do I allow a user to create other users?

It’s a pretty common use case which requires a non-admin user role that can create other users for a Drupal site and I’ve frequently seen questions about how to best implement this. I recently also saw the suggestion to simply create a role with the 'Administer users' permission. At first blush, it might seem to work; if that’s the only “administer” permission they have, users with this role can only create basic users with the role “Authenticated user”, they cannot edit the user to add any other roles or upgrade their own role directly. In limited situations, this might even be appropriate.

Drupal’s “administer users” permissionUsers with the administer users permission can edit any other user on admin/people

What might not be immediately apparent, however, is that a user with this permission can edit any other user’s account… and I do mean any. This means that, if their intentions are not pure, a user with this role could easily change the password (or any other fields) on a more privileged user, even user/1, and then log into that account. Once they’ve done that, there is really no limit to what they could do to your site. Even if they have no means to add modules, ones which might be used for particularly nefarious purposes, if you have a module like Backup and migrate available, they could download your database with all sensitive user data; and even if this module is not available to them, you most likely have Views, which they could also use to harvest all user email addresses or other private data fields. And then they could easily cover their tracks, too. If they don’t do anything obvious (like deface your site or start sending spam from it), and only change the password on the admin account, you might be puzzled by why you cannot log in with your normal password, and follow the normal procedure to reset your forgotten password, then forget all about it. Meanwhile, your “user moderator” has collected lots of sensitive data from your site and still has the means to do it again one day.

There’s a module (or a few) for that!

Depending on your actual use case, which might include requirements a bit more complex than just creation of a basic “Authenticated user”, there are a number of modules which might be useful for a “user moderator” role. Some of these modules do nothing about actual creation of users, after all, probably most Drupal sites allow users to just register themselves, but deal with the related need to delegate the responsibility of giving some users additional roles beyond the “Authenticated user” role. But for sites which don’t provide self-registration, there are a couple of modules which allow non-Administrators to create new users, as well.

  • Role Delegation is one of the most popular such modules, used by over 8000 sites with stable releases for both Drupal 6 and Drupal 7, but it’s limited to role assignment and does not allow users without additional permissions to actually create new users.
  • Administer Users by Role has stable releases for Drupal 5 and Drupal 6 and allows users of a particular role to create, edit, and delete other users. In theory, it should provide limits to the “administer users” permission by allowing them to administer users with roles that you select. There is no version for Drupal 7 yet available, but a port is in progress. It’s a fairly popular module with about 2,500 users.
  • RoleAssign is a module with a stable release for Drupal 6 and a “release candidate” for Drupal 7 and is used by about 2,800 sites. It allows users with appropriate permissions to assign pre-defined roles to other users.
  • Subuser is a module available for Drupal 6 (stable) and Drupal 7 (currently in -alpha2). It is, perhaps, the most advanced and interesting of these modules, although it is not used by the most sites (currently only 282 sites). It allows for a user to be given permission to create users which that user then has permission to manage. Users not created by this “parent” user are not available for management. “Child” users can be given any of the roles which the “parent” is allowed to assign, and the role assignment can be automatic. In other words, an “editor” might create “author” users or a primary “site moderator” might create “forum moderator” users, etc. I think this module shows a lot of promise, especially since it’s written and maintained by the highly esteemed boombatower, a true “Drupal rockstar”.
  • User Creator is a module which will not be ported to Drupal 7 (they suggest using the aforementioned Subuser module). It allows users with particular roles to create other users with particular role limitations. The example is provided that, for a school website, a “Principal” could create other users with the roles “Teacher” or “Student” and a Teacher could create only “Student” accounts. Site administrators can determine which roles are allowed to create accounts for which other roles.
  • Control Access to User Settings is a module which seeks to increase the granularity for the “administer users” permission, so that user settings and user administration are separated under this permission and a site administrator can assign just a part of this permission. It has a “stable” release for Drupal 6 and a development snapshot for Drupal 7.

In short: Beware of granting excessive permissions

Be very careful (and generally avoid) granting any kind of “administer” permissions to non-Admin-role users. This article should make it clear that the “administer users” permission is one that could lead to disastrous results if given to the wrong user. While it might even be appropriate to give this permission to a very trusted “admin helper” (e.g. if you want to hide some of Drupal’s administration complexity from your partner—so want to give them some admin permissions—to avoid having them be overwhelmed by the full admin interface), you must absolutely trust such a user not to do anything to abuse the power. And then, it’s probably still best to use one of the appropriate modules, just in case you might forget and grant the same role to someone you trust less than your partner, just to allow this other person to add some new user accounts.

Hopes for the future of Drupal

As I see it, something like the Subuser modules could well be a part of Drupal core. There is almost no reason for any non-admin user to be granted the full power of “administer users”, but there are many reasons you might want to allow for a role that can at least create users and provide limited management of other users (with fewer permissions than their own). I believe some degree of this functionality would be a good thing to include in Drupal core and hope to see that in the future.

Mar 08 2012
Mar 08

At the latest Rhein-Main region Drupal meetup, and then at the recent DrupalCamp in Essen, I gave presentations about installing and configuring the latest version of the Eclipse IDE (Eclipse Indigo / 3.7.x) for Drupal development, with coverage of commonly-used plug-ins, and identified some features which enhance a Drupal development workflow. This article is related to that presentation and covers some tips for the PDT (PHP Development Toolkit), productivity shortcuts, and configuration requirements for coding in accordance with Drupal standards. The Aptana Studio (as an Eclipse plugin) is not covered in great depth; much of it differs from PDT and PDT-supporting plugins, so it may be covered in a separate post.

Disclaimer: I am not an expert Eclipse power user, nor am I a Drupal development expert, however I have taken the time to look at the latest developments and to experiment, so while this material might be especially helpful for people new to Drupal development who are just starting to use Eclipse and configuring it for the first time, I hope that even experienced Eclipse users may find some useful tips in this article. Before I started my internship for Cocomore, back in October 2010, I think I’d used Eclipse for one course in Java development, but it had been some years, and since then I’d used NetBeans in other courses, but whenever possible I still used my editor of choice, TextMate, which I still find most useful for those times when I just want to make a few simple edits. But there are times when a full-fledged IDE is the right tool for the job and when that’s the case, it helps to have your IDE properly configured and to be proficient in its use. Writing this guide has helped improve my knowledge of Eclipse and I hope reading it will help yours.

Eclipse configured for Drupal development

Some benefits of the Eclipse IDE:

  • Free / Open-source
  • Multi-Platform (Java-based)
  • Powerful debugging features
  • Syntax highlighting and error flagging
  • Lots of plugins to extend features
  • Drupal templates available

We may first want to talk about the pros and cons of Eclipse. Among the benefits of using Eclipse are that it is free and open source, it’s a Java application which has builds for all major platforms (Mac, Linux, Windows), has powerful debugging features, is a full-featured IDE with syntax highlighting and error flagging, and that it has a very active developer community, which means that there are loads of plug-ins to extend the features of the basic Eclipse environment, and there are even Drupal templates available to help streamline your development workflow.

Downsides of Eclipse include:

  • Resource hog
  • Java-based, not a native app
  • Some pricey IDEs may be better, especially if you already use them

Some of the benefits also include the drawbacks: complex Java applications like Eclipse tend to consume a rather high amount of resources; since it is not a native application, its interface and some of the ways that it works may be a bit less polished than, for instance, a native Mac application. Because of the great number of plug-ins available, and the complexity of the system, even without considering plug-ins, Eclipse has a steep learning curve. Finally, some commercial IDE’s may be better, especially if you already own and use them, in which case there might be little reason to experiment with Eclipse.

Downloading and Installing Eclipse

This article was written using the latest version of Eclipse, so-called Indigo or version 3.7.x, but I have also used earlier versions and much of the material should apply to other Eclipse versions, both past (and likely) future versions. To best follow along with this article, please download the latest version of Eclipse Classic appropriate for your operating system. There are some bundled versions (Eclipse plus PHP plugins) available, but at least at this time, neither the Eclipse package nor the PHP Developer Tools in recent bundles are very up-to-date, so you’re probably best off just getting all of the most recent stable releases separately.

I’m using Mac OS X, (10.6.x, aka “Snow Leopard”), so chose to download the 64-bit version of Eclipse classic for OS X, which should also be appropriate for Macs already running OS X “Lion” (10.7.x). If you have a suitable system, the 64-bit builds should be your best choice. In any case, installation is simple. You merely download a gzip tar ball, unzip it, and put the resulting directory under applications, or wherever makes most sense on your system. This probably goes without saying, but of course you do need a Java virtual machine (JRE or JDK). If you think you might be doing some Java development, you’ll want to install the JDK, otherwise at least a JRE. Modern versions of Mac OS X already have a suitable Java runtime environment, by default, so if you are on a Mac, you should probably be okay. Please note that Linux users may want to use the Oracle JDK if they plan to use the latest Aptana studio as the OpenJDK is apparently not supported. If you are on Windows or Linux, you may want to look over the installation notes for Eclipse and/or download the Oracle JDK or JRE.

Select WorkspaceWhen you first start Eclipse, you’ll be asked to select a location for your workspace. This is where Eclipse stores your projects and preferences. Note that your source code does not need to be in this location, and indeed, if you do put source code in this directory, e.g. a Drupal installation, it will not allow you to create a new project from it. It’s also a good idea to create projects with the directory structure which includes the Drupal root level; that way, core functions will be recognized within your code, and documentation and auto completion hints will show up. When the IDE opens, you’ll see a welcome page. You may want to take a little time to browse the overview or tutorials sections before going to the workbench view.

The Eclipse Welcome Screen

Plugins in Eclipse, like modules in Drupal, are the source of the power

There are a number of Eclipse plugins which you might find useful for your PHP/Drupal development workflow. Following are some of the plugins you are most likely to use. If you already know how to install plugins into Eclipse, the list below provides the general installation info for several plugins; but I will also provide more detailed information about others. Note: Trying to browse to the links given for “Location” will result in an error; those URLs point to the repository Eclipse uses to install and update plugin software. If an “Info” link is provided, that is the place to read more about the plugin.

PHP Development Toolkit (PDT)

The PDT has long been the primary plugin for PHP development in Eclipse, although now the Aptana Studio 3 plugin also supports PHP development, so it’s possible you could do without the PDT (there are certainly some benefits to both, but which you prefer and whether you want both installed is a personal decision). (More about the PDT, below.)

  • Name: PDT
Eclipse Marketplace Client

The Eclipse Marketplace client plugin helps find and install popular plugins directly into Eclipse with an interface to browse the selection of available plugins on the Eclipse Marketplace website. It’s actually a part of the distribution in most other packages of Eclipse, but is not included in the “Classic” package.

  • Name: Marketplace Client (MPC)
Drupal for Eclipse PDT

The XTND.US Drupal for Eclipse plugin adds support for the common Drupal filename extensions so that they are recognized and opened as PHP files (or text files in the case of .info, etc). More about the Drupal for Eclipse PDT plugin, below.


The EGit plugin provides a GUI for exploring your Git repository. You can add an existing Git repository to Eclipse, clone one from the EGit GUI, or create a new Git repository from Eclipse. Note that you already need to have Git installed; neither EGit nor Eclipse include a Git client. (More about EGit, below.)

  • Name: Indigo

This is actually included in Indigo, but needs to be added to earlier versions of Eclipse. At least part of this is probably already included in your Eclipse Indigo download, even if it may not be installed/active.

Subclipse for Subversion (SVN) Integration with Eclipse

While Subversive is the default Eclipse SVN-integration plugin, you probably want to use Subclipse, instead. Subversive is the one you will find if you search for “SVN” or “Subversion” in the general “Install new Software…” panel. But Subclipse has about triple the user satisfaction ratings on the Eclipse Marketplace, is much more recently updated, and is maintained by Tigris.org, the makers of Subversion. If you are using a different version of the Subversion client from 1.6.x, you will want to check the Tigris website for the correct version of Subclipse. More about Subclipse, below.

  • Name: Subclipse 1.6.x (Eclipse 3.2+)
Ahtik - Eclipse Word Wrap

Note: This plugin is only for the PDT PHP editor, the Aptana Studio 3 “PHP Source” editor already includes a Word Wrap function. If you have both PDT (with the Ahtik Word Wrap plugin) and Aptana Studio 3 installed, you will see two different Word Wrap options in the contextual menu, even though only one of them will apply to the editor you are currently using. Note: Since the word wrap feature does not maintain line numbes, when turned on, you should only turn this on when you need to (or should remember to turn it off when debugging by line number.) This is a known bug, but can at least prevent the need for horizontal scrolling through long lines, like unbroken translatable strings.

  • Name: Ahtik Word Wrap
JavaScript Development Tools (JSDT)
  • Name: Webtools
PHP Tools Integration (PTI)

Allows you to integrate PHP CodeSniffer and PHPUnit, among other useful features. This is required for the Drupal Code Sniffer library, which you can load as a support library for PHP CodeSniffer (helps identify issues with Drupal coding standards compliance). Drupal.org documentation has information about how to set up the PHP CodeSniffer to work with the Drupal Code Sniffer library.

  • Name: PTI
Aptana Studio 3

If you want to still work with the PDT and plugins which extend it, without conflicts, you are probably better off using the standalone version of Aptana Studio, but if you install Aptana Studio 3 and don’t mind doing without PDT and some of the PDT-related plugins, this has some cool features which will likely be covered in a separate post since Aptana Studio is a topic worthy of separate discussion and since so many of the plugins and techniques outlined in this article are really PDT-centric. The Aptana Studio is available as an Eclipse plugin, however, which, if you are happy with how you have Eclipse configured, can be a nicer way to work; some people may prefer the Aptana PHP editor to what is provided by the PDT. More info on the official Aptana Studio 3 downloads page. Note: Aptana Studio 3 will not support your PHP templates from PDT, but has a set of Drupal 7 templates available which include more than the Drupal Eclipse (hook templates); you also have theme function templates, among some other cool features, but nothing for Drupal 6.

  • Name: Aptana Studio 3
Aptana Studio 2

Perhaps still a good compromise, which allows you to continue to work with the PDT, Aptana Studio 2 does not include its own PHP editor, so is more compatible and has some excellent Javascript debugging features and other handy stuff which can be installed (e.g. browser plugins which allow you to open your project in a built-in Firefox-based browser tab). This might be a better way to work with Eclipse and is recommended by many.

  • Name: Aptana Studio 2
Eclipse Color Theme

Eclipse Prefs - Color ThemeThe Eclipse Color theme plugin provides a set of extra color themes you can choose from rather than the limited appearance configuration available in Eclipse, by default. I don’t mind Eclipse’s default theme, but some users find this a “must-have” plugin. Note: If you are using the Aptana Studio plugin, the Eclipse Color Theme plugin won’t be used in Aptana-provided editors; Aptana has its own appearance settings.
Settings for the color theme are found in the preferences panel under:
General > Appearance > Color Theme

  • Name: Eclipse Color Theme

The PHP Developer Toolkit (PDT) Plugin

Eclipse Menu: Help - Install New Software…The first plug-in you will probably want to install is the PDT (PHP Developer Toolkit). To install it, simply go to the Help menu, and at the bottom, you should see the option to “Install new software…”. If you select from the list of available software sites, you’ll see downloads for Eclipse.org releases for Indigo, and among the available downloads, in the programming languages section, you’ll see the PHP development tools are available for download, however these are not the latest version, at least not at this time of this writing.

Add PDT from Indigo repository if you want, but it’s not the latest

Add PDT from Milestone repositoryInstead copy the linked URL (do not follow this link as it is only used for the repository and does not contain HTML) into the field labeled “work with”, and click on the “Add…” button. The “Add Repository” dialog will prompt you for the a name and location. I just used “PDT” as the name; the “Location” has already been filled in with the URL added in the “Work with” field. I installed the full development tools all-in-one SDK, but at the very least, you’ll probably want the “PHP Development Tools (PDT) SDK Feature”. Select the components you want, and continue to the next step, where it should confirm which packages will be installed. At the next screen, you’ll need to accept the terms of the license agreements before you’ll be able to click on the finish button and complete installing your software.

Add PDT -- Installer Running Add PDT -- Restart Dialog You should see a progress bar as the installation is completed, and when it’s done there will be a prompt to restart Eclipse. When you are installing a number of packages in a row, it may be just fine to click on the “Not now” button, to continue installing other packages, but since the proper installation of the PDT is critical for some of the next steps, restarting now is probably a good idea.

The Drupal for Eclipse PDT plugin

The XTND.US Drupal plugin associates content types with Drupal file extensionsThe Drupal for Eclipse plug-in, available from XTND.US, is a useful extension to the PDT. Note that if you read through this guide and decide that you want to use the Aptana Studio plug-in for Eclipse for your PHP code, you might skip installing most other plugins. It’s possible to use both the Aptana Studio 3 plugin and PDT, but you may encounter some conflicts and things can get more confusing. In any case the XTND.US Drupal Eclipse plugin is only for PDT and it doesn't do much more than associate the PDT-provided PHP Editor to files with the .module, .test, .install, .engine, and .theme file extensions so that these files are recognized as being PHP code. In my experience, the repository where this plugin is located is sometimes not available, so you may get an error when attempting to add this plugin.

The EGit plugin as a Git GUI within Eclipse

Eclipse Open PerspectiveEGit offers an Eclipse-integrated front-end for Git. Once the plugin has been installed, to work with it, click on the Open Perspective button, up near the top right of the Eclipse window (look for the little yellow plus sign). Select “Other…” from the drop-down menu, and then select “Git Repository Exploring” and click “OK”.

Eclipse Open Perspective-2

From the Git perspective, you can create a new repository, clone a repository, or add an existing already-cloned repository. In the illustration, I chose to open a repository I already had in my file system. Since the .git file won’t show up, assuming you are using Mac or Linux, when browsing the main repository directory, you should check the “Look for nested repositories” checkbox, then click on the “Search” button, and the.git file should show up in the results below, where you can select the checkbox next to it and finally click on the “OK” button.The Git Repository Exploring perspective might be especially useful for those of us who are not already proficient with Git from the command-line, especially if you don’t already have another GUI for Git.

The EGit Repository perspective

Subclipse for Subversion (SVN) integration

Subclipse Vs Subversive: Subclipse has about triple the ratings of Subversive on the Eclipse MarketplaceAt Cocomore, all of our projects are in Subversion. If you also need to be able to check out and commit to SVN repositories, you will want to add a supporting plugin to Eclipse. I should point out that for Git support and SVN support, all the Eclipse plugins do is add a front-end. You will need to install the appropriate SVN and/or Git client(s) for your operating system, just as you needed Java (a JVM) in order to run Eclipse, itself. There are two plugins which are commonly used for SVN support. The default one, Subversive, which is from Eclipse, is not the more popular of the two and I strongly recommend using Subclipse. At this time, if you look at the Eclipse Marketplace website, you’ll see that the latest version of Subversive is about two years old, whereas Subclipse’s latest release is July of last year — and you will also see that it has about triple the ratings. Subclipse is from Tigris, the developers of Subversion, so this also lends some credibility to their version. In any case, your experience may differ, but I accidentally got Subversive when I first set up Eclipse, soon after I started my internship with Cocomore, and couldn't get things properly configured for our "svnexternals" repository until I replaced it with Subclipse, at which point it was just simple. So assuming you take my advice and use Subclipse, you will need to add the repository using the "Work with…" option when you go to Help > Install New Software…. And you first might want to go to the official Subclipse website to check which version to use with the actual Subversion client you have installed. I’m using Subversion 1.6, so used the corresponding plug-in. After installing, you’ll find new preferences for SVN and should be able to create new projects by checking them out from SVN, compare diffs of versions, commit new or edited files, etc.

Uninstalling plugins

eclipse_plugins-already_installed_link.pngEclipse plugins - Uninstall optionSometimes you experiment with a plugin, only to find that it doesn’t do what you want it to do or that it conflicts with other plugins you’re more interested in keeping. Luckily uninstalling plugins is simple, but the interface for uninstalling plugins is not as easily found as for installing them. It’s actually the same interface. Simply open the Help > Install New Software… window and look for the link near the bottom “What is already installed?”. Clicking that opens a new window showing installed plugins. If you select a plugin, you will see the option to uninstall it becomes active. This is useful if you want to optimize your Eclipse installation to trim extra cruft you aren’t using or if you accidentally install a full package when you only wanted one component. Unfortunately, some plugins, such as the Aptana Studio 3 plugin, can only be installed as a whole package, not by sub-components. You can also uninstall plugins to troubleshoot issues. It will be easier to reinstall the package if uninstalling it does not resolve a possible conflict issue since the repository will still be available for easy selection and, even better, any associated preferences are not removed along with the plugin.

Most critical… Preferences for configuring to Drupal coding standards

Set Eclipse Prefs to UTF8 - UNIX Line endingsThe preferences window in Eclipse has many critical panels where you will need to configure settings for proper support of Drupal coding standards. Especially important: you’ll want to make sure that your text file encoding is set to UTF-8 and that the line delimiter is set to “UNIX”. Don’t ask me why these settings are stored under “Workspace”.

Set tabs to two spaces

Eclipse Prefs - Tabwidth 2 Spaces

General > Editors > Text editors
Perhaps of greatest importance is making sure you use spaces instead of tabs and a tab-width of ‘2’. In the illustration shown above, I also turned on a print margin of 80, since we normally want to keep lines to a maximum of 80 characters long. Getting into the details of Drupal coding standards is outside of the scope of this article, but if you are not familiar with the Drupal coding standards, you’ll want to read them; the Drupal coding standards are well documented. You’ll find similar settings which you need to change in just about every text editor for each language: PHP, CSS, XML, etc; set the tab policy to spaces and the indentation size to two in each.
Other places to fix tabs/spaces/indentation settings:

  • Preferences > PHP > Code Style > Formatter
  • Preferences > Web > CSS Files > Editor
  • Preferences > Web > HTML Files > Editor
  • Preferences > XML > XML Files > Editor

Note: If you install other language support or add plugins like the Aptana Studio plugin for Eclipse, you will likely have additional editors you need to configure in this way.

Remove trailing whitespace

Preferences > PHP > Editor > Save Actions
Eclipse Prefs PHP Save Actions - Remove Trailing WhitespaceBe sure to set the PHP editor save actions to remove trailing whitespace from all lines.

Importing Drupal hook templates

Import templates for Drupal hooks. Another useful thing we can do from the preferences panel is import templates for Drupal hooks. The templates, themselves, can be downloaded from the Eclipse project on Drupal.org which, although listed among modules, is not a module at all, but instead contains an XML file with definitions for templates for each of the Drupal core hooks. Both the Drupal 6 and the Drupal 7 versions extract to the same directory name, “eclipse”, and you can install both of them if you code for both Drupal 6 and Drupal 7. To use them, simply go to the PHP editor templates section in your preferences panel and click on the button labeled “Import” at right.
Preferences > PHP > Editor > Templates

Eclipse Drupal Hooks - Auto Suggest featureNavigate to where you saved your downloaded Drupal Eclipse project, and select the XML file within, and almost instantly you’ll have hundreds of templates for all of the core hooks. To use the new hook templates, all you have to do is start typing the name of a hook, for instance “hook_action” and you should see an auto-complete helper which displays the signatures for each of the different hooks available starting with whatever you typed in. Replace the selected module_name part with the short name of your module and hit ⇥ (tab) to continue to the template’s default cursor position. If your module name appears more than once in the hook function, you only need to type it once.

Creating your own templates

You can also create your own templates. For instance, if you are writing a module which makes heavy use of hooks provided by the Views module, and anticipate writing other such modules, you can create templates for these hooks easily enough, simply by looking up the API, copying the hook function definitions, pasting them in the area for the pattern of your new template, and making a few minor modifications. To see how the patterns are created, it can help to look at an existing template in "Edit mode".

Look at an existing hook template

Here, I’ve opened up the template for hook_action_info_alter(). Note that the "hook_" part has been replaced with "${module_name}", a template variable, and all of the dollar signs in the function have been doubled in the template. The ${cursor} variable is a special placeholder for the cursor insertion point after replacing the ${module_name} variable. If you click on the button labeled “Insert variable…”, you can experiment and see how other variables (replaced when using the template) can be used. Also pay attention to naming conventions since your name and description will appear in your code hints. Ideally, you should aim to keep non-core API hook code separate from the core API hook code by appropriate naming and descriptions.

Creating a new PHP templateThe drupalcontrib.org website is a good place to find API documentation for contrib modules. Let’s assume we want to add a hook for a Views API function, like the Drupal 7 version of hook_views_form_submit. All we need to do is click on the “New…” button and copy and paste code and descriptions, then make the appropriate changes, doubling all existing dollar signs, replacing the hook_ with ${module_name} and put the ${cursor} in an appropriate position. Of course we might want to add an appropriate comment block above the function part of the template, perhaps indicating that the function implements hook_views_form_submit and/or that the function is a Views form submit handler.

Use keyboard shortcuts for a productive Eclipse workflow

Eclipse Key Bindings */ Preferences > General > Keys
Eclipse’s keyboard shortcuts are awesome and help perform a number of actions which would take much longer with the mouse or trackpad, not to mention that they keep your hands on the keyboard, where they belong if you want to develop a productive coding workflow. Like many other useful parts of Eclipse, the hotkey bindings are hidden in a Preferences panel, but can easily be reached with a keyboard shortcut. To see all shortcuts without opening the preferences, you can hit ⇧⌘L (Ctrl-Shift-L on Windows or Linux), which pops up the Key Assist selector. This can be useful for learning the keystrokes, but may not be much quicker than finding the command from the menu (and looking at any keystroke shown there).

If you hit the same keystroke combination again, i.e. ⇧⌘L twice, the preferences panel for keystrokes will pop up. By the way, opening the main preferences window is most easily done with a keyboard shortcut, too: ⌘, (i.e. Command + Comma on Mac, or Ctrl-, on Windows and Linux.)

The Keys Preferences panel

The Keys preferences is where you can get a bit more information, quickly search for the keystroke you are looking for, or assign new keystrokes to commands. By default, the panel opens with all keystrokes sorted by command name. Clicking on the column header labeled “When” or “Category” to see shortcuts sorted by context (editing, navigating, Git, window, …) can make it simpler to see commands related to your current needs. You can also type in a word you are looking for, e.g. in the illustration below, we are looking at all commands related to “move” and can now see that the very handy keystroke for moving selected lines up and down in the document is ⌥-↓ / (alt-↓ / ). So holding down the alt/option key while using the up/down arrows can move a block of code around. This can be very handy and it’s worth mentioning a related keystroke: if you add (Ctrl) to this (i.e. ⇧⌘-↓ / -↑), the current line (or selected lines) are duplicated above or below, which can be useful for adding a similar line (e.g. initializing an array with key/value pairs). It’s much quicker than selecting the line, copying it, moving up/down, then pasting.

Of course some of the default keystrokes assigned by Eclipse may conflict with keystrokes used by your operating system. If it’s an obscure task you never perform in your OS, of course you can go into the appropriate preferences panel for your operating system and remove the keystroke or reassign it to something more complex (less likely to conflict with any other application-specific keystroke). Or you can reassign the keystrokes (binding) used by Eclipse, quite simply, by typing your desired keystroke combination in the “Binding” field of the Keys preferences panel. And, of course, if there are commands that you commonly use, but which are not assigned to keystrokes, you can find the commands and add a new binding. Very cool!

Eclipse Preferences - General - Keys

A few of my other favorite keyboard shortcuts in Eclipse


⌘D (Ctrl-D)

Delete (current line or selection). Of course you can use many methods to delete a selection, but to delete a line without first having to select it is handy.

Go to function declaration


If the function declaration is in the same file, it just takes you to the function declaration, but where this is really handy is when the function is declared in another file, e.g. if you have selected a call to drupal_attributes() in a contrib module and hit F3, you’ll see a new editor tab open with (Drupal core’s) common.inc scrolled right to the function declaration on line 2295.

Last Edit Location

⌃Q (Ctrl-Q)

Return the cursor to the last edit location. Very useful if you have been browsing around a lengthy document, perhaps copied something from somewhere else, and now want to return to where you were editing.

Maximize/Restore view

⌃M (Ctrl-M)

Maximizes the currently-active “view” (section of the Eclipse window), especially useful when working on a smaller monitor when you want to have the editor area at its maximum width. Hitting the keystroke again restores the normal perspective (e.g. shows the Project Explorer at left, etc). Note: Double-clicking a tab performs the same function.

Next / Previous

⌘K / ⇧⌘K (Ctrl-K / ⇧-Ctrl-K)

Jump forward and backward in the editor between, for example, function calls in a document (or next/previous occurrences of whatever you have selected.)

Outline - Look up functions in document

⌘O (Ctrl-O)

Eclipse - Outline - Look up function in documentDisplays a quick contextual menu to navigate to any function in the current document (or quickly view the function signatures). For classes, it displays a class outline.



The Eclipse Search windowMuch more refined search options than the simple “find” function (




). Both “search” and “find” support replacement, but “search” also supports searching/replacing within the whole project or workspace rather than just within the document or selection, as well as searching within different document types, regular expressions, etc

Switch Editors

⌘F6 (Ctrl-F6)

Eclipse - Switch Editors

Switch between open editor tabs; works similarly to the “Switch Views” pop-up menu in that repeating the key sequence steps you through available editor tabs in the menu. Enter or mouse-click selects the editor tab.

Switch Editor (filtered)

⌘E (Ctrl-E)

Eclipse - Switch Editors (filtered)Works similarly to the normal “switch editor” menu, but has a filter field at the top where you can start to type non-case-sensitive file-names to filter the list of available editor tabs. This one is especially handy if you have a lot of editor tabs open.

Switch Perspective

⌘F8 (Ctrl-F8)

Eclipse  - Switch Perspective

Provides a menu similar to the Switch Views / Editors pop-up, which instead allows you to switch perspectives.

Switch Views

⌘F7 (Ctrl-F7)

Eclipse - Switch Views

Pops up a switcher to move between views. Hitting the same key sequence moves you through the available (open) views. Enter or mouse-click selects the view.

Toggle line comments

⌘-/ (Ctrl-/)

Toggle // line comments: Adds or removes // line comments in front of each selected line. Useful when debugging code.

There are lots of great plugins for Eclipse; this guide only scratches the surface. Do you have any favorite plugins, keyboard shortcuts, or other hot tips for a productive workflow with the Eclipse PDT? We’d love to hear them, so please do share your knowledge in the comments, below.

Jan 25 2012
Jan 25
Apache Solr logoTomcat logo

Installation procedure

While there are several ways to install Tomcat 6 and Apache Solr, we will use the repository version to gain the benefit of automatic updates.

What is needed:

  1. Tomcat6 as Servlet container

    sudo apt-get install tomcat6
    sudo apt-get install tomcat6-admin

  2. Apache Solr Search Server

    sudo apt-get install solr-tomcat

Once everything has been correctly installed, you should see the message, “It works!” at http://localhost:8080 and “Welcome to Solr!” at http://localhost:8080/solr/

Configuring Tomcat 6

In the default Tomcat installation, no privileges are created for the Tomcat Manager, so in order to make use of the Tomcat Manager GUI, we still have to create the proper role and a corresponding user.

Add the following lines to your Apache Tomcat 6 configuration file

We’ll edit it with Vim:
sudo vim etc/tomcat6/tomcat-users.xml

<role rolename="manager-gui"/>
<user username="tomcat" password="tomcat" roles="manager-gui"/>

The Tomcat6 managerNow we need to restart Tomcat:

sudo service tomcat6 restart

Now we can start the manager at http://localhost:8080/manager/html. At this point, you could also deploy Java applets, but for our use case, the manager simply provides an overview of our Solr installations.

Drupal-specific customization

In order to use Solr with Drupal, we still need to make some adjustments to our Solr configuration.
We’ll start by creating a new directory in the file-system:

We’ll use Vim to create a new file (sudo vim solr_cocomore_drupal_demo.xml) with the following content:

<Context path="/solr" docBase="/usr/share/solr" debug="0" privileged="true" allowLinking="true" crossContext="true">
   <!-- make symlinks work in Tomcat -->
   <Resources className="org.apache.naming.resources.FileDirContext" allowLinking="true" />
   <Environment name="solr/home" type="java.lang.String" value="/opt/solr-tomcat/solr_cocomore_drupal_demo/" override="true" />

And we need another file (sudo vim solr.xml), containing the following code:

<solr persistent="false" sharedLib="lib">
  <cores adminPath="/admin/cores">
    <core name="cocomore_drupal_demo" instanceDir="cocomore_drupal_demo">
      <property name="dataDir" value="cocomore_drupal_demo/data" />

Next we need to create a sub-directory…
sudo mkdir /opt/solr-tomcat/solr_cocomore_drupal_demo/cocomore_drupal_demo

Now we copy the current standard Solr configuration files
cp -R /etc/solr/conf /opt/solr-tomcat/solr_cocomore_drupal_demo/cocomore_drupal_demo/conf

The following files must be present in the conf directory for Apache Solr to function:

  • solrconfig.xml
  • schema.xml
  • elevate.xml
  • mapping-ISOLatin1Accent.txt
  • protwords.txt
  • stopwords.txt
  • synonyms.txt

The files marked in red will be replaced with those included in the ApacheSolr Drupal module.

Now we need to tell Tomcat where our Solr configuration can be found. We will create a symlink for this purpose:

sudo ln -s /opt/solr-tomcat/solr_cocomore_drupal_demo/solr_cocomore_drupal_demo.xml 

Important: Set correct privileges!

chmod -R 775 /opt/solr-tomcat/solr_cocomore_drupal_demo/
chown -R root:tomcat6 /opt/solr-tomcat/solr_cocomore_drupal_demo/

Tomcat manager with DrupalNow, when we visit http://localhost:8080/manager/html, we should see our Solr installation (solr_cocomore_drupal_demo).

Adding and activating the Drupal 6 Apache Solr Modules

The following modules must be installed and activated in our Drupal project:

Before we can activate the Apache Solr Search module, we must first install the SolrPhpClient in the ApacheSolr module’s path, ideally with:

Note: If you do not have SVN installed, you can download the SolrPhpClient from a variety of sources. See the readme file included with the ApacheSolr module for further information.

Now we should activate the following modules:

  • Search (if not already activated)
  • Apache Solr framework
  • Apache Solr search
  • Apache Solr multilingual

Configuring the Apache Solr module in Drupal 6


Apache Solr configuration in Drupal 6; starting pointSince the default settings should match the module, a connection can already be made to Solr. However, we do not want to use the default Solr index, but our own core, so we have some adjustments to make.

Now we set our Apache Solr core

Apache Solr configuration in Drupal 6; step 1The core consists of Corename and Instance, entered in the Solr path field:

Other settings - Apache Solr module (Drupal 6)

Apache Solr configuration in Drupal 6; step 2“Number of items to index per cron run:” A reasonable value is 50. Higher values can result in problems during Cron runs.

In the “Advanced configuration” fieldset: we should now set Apache Solr Search as the default (“Enabled”).

Search Index (before Cron has been run)

Apache Solr configuration in Drupal 6; search index before cron runIn the “Search index” tab, we can now see how many documents are in the index. Of course we don’t have any yet. We can also see the number of items to be sent for indexing. Content is only indexed during Cron runs. So let’s run Cron!

Search Index (before Cron has been run)

drupal6-apache-solr-settings4.pngAfter Cron has run, we should see that there are no items left to send and that documents have been indexed. Please note: There is a delay time after adding new content before it is indexed. In this example, the delay is set to two minutes.

Search fields - “Field biases” fieldset

Apache Solr configuration in Drupal 6; Search fields / Field biasesIn the “Search fields” tab, we can specify index “weights” for different fields. Higher weight values for a field give it a greater importance in the search results (not to be confused with “weight” in the context of menus or taxonomies where a higher value makes an item appear lower, i.e. “sink”).

Content bias settings

Apache Solr configuration in Drupal 6; Content bias settingsUnder “Type biasing and exclusing”, we can also apply weights to different content types or exclude a content type from the search index, altogether.

Congratulations! Apache Solr is now ready for use!

Oct 28 2011
Oct 28

Some time back, I promised another short article in the WYSIWYG set-up series for Drupal 7, one which covers BUEditor. First, we should note that the BUEditor is not actually “WYSIWYG”, but it offers some nice features which might make it a bit better than the WYSIWYG options, depending on your use case. It also does not integrate with the Wysiwyg module. You add it separately (and instead of Wysiwyg), but it does have some great supporting modules and code libraries. This article covers some of the basics about use and installation of the BUEditor on a Drupal 7 site (most of the information applies equally to Drupal 6, where the BUEditor module is also available). I’ve also got some good tips for some ways to extend the default button-set. (And you can download my modified button code here to easily import the buttons into a new editor profile.)

The BUEditor is now used on Drupal.org

The BUEditor is now in use on Drupal.org. Nice!

Since the time I planned to write this post, BUEditor has surely got some additional attention; it’s now what you’ll use if you leave a comment on an issue or forum post on Drupal.org. It’s refreshing to have more than just a plain text area for HTML entry; I think this is a great step for the Drupal community’s official site. In addition to basic set-up of the BUEditor, as it’s used on Drupal.org, I’d like to cover some nice features of the BUEditor which are not being used on Drupal.org.

The BUEditor integrates nicely with IMCE

The default BUEditor Insert/edit image pop-up dialog is very limited but can be modified.The most recent article in this series covered the set-up of IMCE and supporting modules for inserting images. You’ll be glad to know that the BUEditor supports adding images with IMCE. I’m sure you could also simply “attach” images and add them to content with the Insert module. The BUEditor’s default image popup is very limited, which was at first a deal-breaker for me to suggest using it on this website, however I learned it’s not so difficult to adjust the pop-up dialog to add more fields; by default it only has the URL, dimensions, and alt text fields. I also wanted fields for title text, class, and style (though it is probably best to avoid using inline styling, it’s sometimes handy and nice to have a GUI for adding this to your tag when you create it).

Remaining downsides to the image dialogue

At first it wasn’t obvious how to add new fields to BUEditor Image dialogue for Title, Class, and Style…Proportional dimension calculations: It would be nice if there were support for automatic adjustment of the “Width” and “Height” fields to adjust one automatically (by ratio) if the other is modified. I tend to place images that are often wider than 600 pixels, which means they would not display nicely in the layout of this site. The Image Resize Filter automatically creates a smaller image, to be displayed in the content, if I adjust the size when placing the image. And it’s easy to link the full-size original image to appear in either a Lightbox2 or Colorbox overlay. If I have an image which is 938 pixels wide by 529 pixels high, and I change “Width” to 600 in the BUEditor image popup, I’d like a default option for the “Height” to automatically be proportionally re-scaled, as it works in CKEditor and TinyMCE. It’s not hard to calculate what the height should be, but I’d rather not have to, and I’m not the only one who would have to. The easy solution to this is to just leave the height field empty and the Image Resize Filter will still proportionally resize the image, but if the image doesn’t load, for some reason, the page layout will be affected.

Longer fields: Even after working out how to add the new fields, I couldn't figure out how to make the whole dialogue larger (assuming physical display size is large enough) so that these fields could all be a bit wider. The default size of the text fields is pretty darn short, but my initial attempts to modify this just ended up breaking the whole editor. Well, it’s not a big deal, but perhaps I'll figure out how to further customize the image pop-up. (If anyone knows how to make such modifications through simple button code (or an add-on module), please share your tips.)

Quibbles aside, the BUEditor really is awesome!

BUEditor has a great preview function available with the Ajax markup moduleThe BUEditor provides a nice, clean user interface with what could be a perfect balance between simplicity and usefulness, provided users are comfortable with working directly with HTML code. It’s easy to apply formatting to selected text and it’s also simple to modify the values of the editor buttons or add your own. Converting unstyled lines of text into lists (ordered or unordered), blockquote, or heading, is as simple as the click of a button and if anything gets mucked up, you have the code right there to adjust, but it’s less likely to get messy than with a WYSIWYG editor since you have complete control over what you select before you click the button. This might be a good time to mention that there is also a Drupal-free version of the BUEditor, though the BUEditor was first created specially for Drupal, unlike so many of those other (“WYSIWYG”) editors.

It’s especially awesome with the Ajax Preview option enabled

The Ajax markup module, also written by the author of the BUEditor, allows you to simply modify the “Preview” button to provide a display which closely matches the end result (taking the text format settings into consideration). Awesome! I’d like to see them add this integration on Drupal.org, too, since it’s much faster and nicer than reloading the page with the main “Preview” button. It would be great to also have this functionality in a Wysiwyg-integrated editor. Bear in mind, once you have this configured and working, clicking the “Preview” button displays the preview and by dragging on the handle at the bottom of the editor pane, you can see the corresponding code… but you won't be able to edit that code till you click the “Preview” button again.

BUEditor keyboard shortcut support

Not all browsers support the shortcuts equally, but when you are actively using the text editor, you should be able to use keyboard shortcuts to add tags, snippets you’ve configured, or perform other editor functions. In Safari, I found that I had to use the control and option keys in combination with the configured key. In Firefox it was just the control key. More than one editor in a page can mess this up and the shortcuts may not work at all in some browsers, but they can certainly be useful. You can also remove the key codes if you think it might cause more problems for your users than it’s worth.

Keep in mind: The button-set is based on role

One potential issue is that the editor button set is based on the user role, not on the Text format. So it’s still easy to create content (as a privileged user) in the “Filtered HTML” format, and include images or other tags… that are then stripped on output. With Wysiwyg-integrated editors, the button sets are per text format. There are pros and cons to this, but I’d prefer to have a default editor configuration for each text format. There is a BUEditor Plus add-on module which aims to take care of this issue, but it’s still in beta (actually still in the developer’s “sandbox”). Since it’s likely to be released soon as a full project, I won’t link to the sandbox. Instead look for the module on HollyIT’s user page on Drupal.org. Caveat: I can not confirm that the sandbox beta is ready for production use. Use or experiment at your own risk.

Another cool feature: BUEditor supports BBCode output

Many forums and sites support BBCode, which is easier for users to enter, a bit simpler than HTML, and removes many of the potential hazards of allowing users to enter HTML. If you want to have commenters post using BBCode, it’s as simple as configuring the button-set for the included BBCode editor. The defaults might already be everything you need. Of course you’ll also have to add and enable a BBCode text filter.

Are you ready to install and configure the BUEditor?

Add and activate the modules

Add and activate the BUEditor module (plus optional modules if you like)admin/modules
You will want to download the appropriate modules. The BUEditor module is required, of course, and I also recommend the Ajax markup module. Add them both to your sites/all/modules folder, then activate them, as usual. If you want to use IMCE, IMCE Mkdir, and/or the Image Resize Filter, be sure to add and activate them, too (see previous lesson about using IMCE with images in a Wysiwyg-integrated module and just follow the steps for configuring IMCE and the Image Resize Filter.)

Note: The BUEditor Plus module is also shown here, but may not yet be ready for production use. And, in case you were wondering, Internal links is a module I’m working on in my free time, so you see an “unversioned” copy in the screenshot from my D7 development installation. I will be committing some improvements to that in the near future.

Configure the BUEditor

Click the “Import editor” link to easily start from exported button code, like the modified button-set I’ve attached here. By the way, I’ve tested this button code in both BUEditor 6.x-2.x and 7.x versions. The same button definitions work in both.

To import a previously exported button-set, paste the code in the “Editor code” text area

You can simply paste the code from the file I attached if you have the Ajax markup moduleYou can create a new editor based on the Default profile (as I did originally) or you can import the code I’ve already modified to provide what might be a better starting point. Simply add a name for your new editor and paste the code from that file into the text area labeled “Editor code (PHP)

If you choose to start from the “Default” set, and want to enable Ajax preview, look for the “Preview” button in the “Buttons” rows and paste over the existing “Content” for that button with the following code:
js: E.prvAjax();

Note: This is already done in the attached version of the button-code.

You might want to remove the H1 tag from the “Headings” selector button. Another change I made was to remove the “Heading1” selector from the default Headings button code. Why? A page should really only have one <h1> tag, and it’s already provided by the node’s Title field, so except in unusual use cases, you’d never want to have this in your editor. That tag can be removed from the button very simply by dragging the “Content” text area for Headings open to see the full contents and selecting the second line for deletion. I also added a few new text buttons for <p>, <br />, and <?php tags; adding or customizing such tags is super-easy, especially if you simply add a plain text “Icon” to represent the button in the editor, as I did. If you want to add custom icons, you can supply the path to find your custom icons (e.g. a copy of the default icons, plus a few more that you create yourself; or you could replace the original icons with ones you like better. The original icons are all 20 pixels high, so match the height and your icons should look fine; for my paragraph and line-break buttons, a plain-text button suits me, but I’ve created a few alternate icons, included with the attached code.) If you choose to use alternate or extra icons, be sure to follow the directions included in the readme.txt file within the BUEditor module’s “icons” subfolder and copy all default and custom icons that you want to use to a new sub-directory under your files path.

Additional simple toolbar configurations

Export your modified button code.You can drag rows up and down to adjust the order of icons in the BUEditor toolbar. You can also modify the keyboard shortcuts linked to the buttons and make other simple modifications before saving. I would suggest that you actually save periodically and make sure that everything is still working in the “Demo” text area, especially if you are editing button code or adding new buttons.

It’s a good idea to periodically select all buttons and export your button set. To export all buttons, simply select the checkbox at the top right of the “Buttons” configuration table, which will select all checkboxes, then at the bottom of the list, select the “Export” action and click “Go”. You can also use this action selector to remove buttons with the “Delete”, copy selected buttons to another configuration of BUEditor, etc. If you don't do this, you might not notice when your custom code gets broken until you just have no icons showing up above the Demo textarea after clicking Save configuration. If you’ve got the buttons backed up, you can easily revert to a working set.

Assign the new editor(s) to suitable role(s)

Don’t forget to assign your editors to roles.admin/config/content/bueditor
Be sure to go back to the main BUEditor configuration page and select your new editor configuration to use for the appropriate roles. In this case, the “advanced” editor I set up for “Staff” needs to be assigned to all trusted roles who have access to “Full HTML”. You may also want to assign an editor for basic “Anonymous” and “Authenticated” users. Untrusted roles should probably not be allowed to add images, headings or other special tags. The included “Commenter” editor configuration is likely appropriate for your “Filtered HTML” needs, but if you add any additional buttons to be used by roles who only have access to “Filtered HTML”, be sure to make corresponding changes to the “Allowed HTML tags” in your “Text formats” configuration:
As mentioned before, you could also configure the BBCode editor or set up an editor for other special markup languages. Exploring these use cases is outside the scope of this article.

More advanced configuration

Super light-weight HTML syntax highlighting; how cool is that?!You can do a lot more with the BUEditor… this article might be a bit long, but the official documentation is even longer. There are loads of API functions for doing really cool stuff with BUEditor buttons; and don’t overlook all the pages of contributed button code. Before you spend a lot of time trying to figure out how to implement a particular button for your needs, take a look at what’s already tested and available; even if you don’t want the exact functionality offered by a contributed button, it may offer an example you can more easily adapt to achieve your particular needs.

Some of my favorite contributed buttons include the:

  • Special Characters button, which provides a nice pop-up panel for just about any “special character” a user might want to insert
  • Text Color button, which provides a pop-up selector with color swatches which can be applied to text or backgrounds
  • Class Attribute Library; allows easy addition of a particular class to selected HTML
  • Smileys Button, which provides reasonable integration with the Smileys module (unfortunately not yet officially available for Drupal 7, though it does look like the port is reasonably complete.)
  • Remove Formatting button (perhaps less risky than the one I created since it only strips designated tags, so won’t remove everything inside of IMG tags or between the php tagset used for longer blocks of syntax-highlighted code.)
  • BUEditor Imagecache button (similar to the standard insert image button, but also allows selection of a pre-set image style)
  • BUEditor Quick Table (okay, HTML tables are worth avoiding, but sometimes data content really is best displayed in a table; this makes it easy)

Good luck and have fun…

We wish you every success configuring your Drupal site for maximum productivity and a user experience to write home about. The BUEditor might not be for everyone or every use case, but its light-weight code-base is amazingly efficient and with the proper configuration it can be better than a WYSIWYG editor for offering a friendly way to add and edit content. If you come up with some interesting things to do with it or have comments about this article, or any of our others, we look forward to hearing from you. Please feel free to leave a comment, below.

Thank you for visiting us on the Cocomore Drupal blog.

Oct 20 2011
Oct 20

As part of the Knowledge Lab at Cocomore on 2 September, 2011, I outlined how to use Jenkins to implement Continuous Integration for a Drupal-based project. The basic building blocks for the demonstration included Jenkins, a demo Drupal project, and its automatic update from the SVN version control system, which triggers each run of SimpleTest. I will describe the primary steps to this process here.

Basic Installation

There are fully prepared Jenkins packages available for download for the most popular Linux distributions, including the Debian server version I used. The Jenkins project even maintains a repository which blends seamlessly into the software management. All the necessary commands are described on the Jenkins download page; with them, installing the base system is simply an “apt-get install jenkins” away.

Of course, since we want to test a Drupal system, our machine also needs a Web server (Apache), PHP, and a database (MySQL). Installing Ant is also useful, since it provides a good way to automate tasks and is very well supported by Jenkins.


The next step is to create a new Job in Jenkins for testing our project. The “Build a free-style software project” option is the recommended setting for an Ant-based project like this.

Create a new job in Jenkins

On the configuration page, there are numerous settings to customize the job, according to your needs. For example, you can configure which version control system to use. CVS and SVN support are included in the Jenkins default installation, but there are easily-installed Jenkins plugins available for numerous other systems such as Git, Mercurial, or Bazaar. In this example, we are using SVN.

We need to add the repository URL (including path) to allow Jenkins to find our source code. We also need to configure what should trigger a build. For easy setup, it makes sense to either start the build at fixed intervals, or have Jenkins make periodic checks for changes in the repository and begin build execution after changes. We’ll choose the latter option and, for this example, the code in the “Schedule” field is set to check the repository every two minutes. It’s also useful that Jenkins examines not only the version of the “direct” project code, but also any linked dependencies via svn:externals.

Configure SVN in Jenkins

Now that Jenkins can find the source code, we need to configure what should happen during a build. We have chosen to create an Ant-based target, so we create a series of steps for our build, as Ant targets, which are executed in sequence.

Build processes in Selenium configured with Ant targets

We have yet to define what to do with the results of the build. We want to configure SimpleTest to write its output in JUnit XML format, so we select the JUnit plugin and enter the location our SimpleTest output will be found.

Post-build actions in Jenkins.

Here’s the relevant excerpt of the Ant target which runs the SimpleTests. In this example, it runs all tests in the group, “Demo”:

   target name="run_simpletests">
   <exec dir="${basedir}/htdocs"
    arg line="scripts/run-tests.sh --php /usr/bin/php --url http://demo.debiserv/
      $--xml {basedir}/build/logs/simpletest Demo"/>

The example below shows a very simple “Dummy” SimpleTest, which can be switched back and forth between returning “test successful” and “test failed”.

class DemoModuleTestCase extends DrupalWebTestCase {      public static function getInfo() {
       return array(
'name' => 'Demo Module tests',
'description' => 'Test the demo module.',
'group' => 'Demo',
      public function
testDummyTest() {
$value = true;
//$value = false;
$this->assertTrue($value, 'The value is true.');

Jenkins build historyHere, if we look at the “Build History” block, we can see the results summary for recent test commits. The first was set to trigger a SimpleTest failure. Jenkins recognized a change in SVN, which triggered running build #34. The yellow ball indicates a build which completed, but a test failed. A subsequent commit to SVN triggered build #35; the blue ball indicates that the build and test were successful.

You can easily configure Jenkins to send notifications, via RSS or email, whenever the status of a project changes, so your development team is immediately made aware whenever any code changes cause a test to fail.

Conclusion: Continuous integration with Jenkins is useful

In conclusion, continuous integration can be implemented with Jenkins for a Drupal project. It is useful to formulate the steps as Ant targets; these targets can also be used for setting up development environments, so the time taken to write the automation is spent once, with continuous dividends throughout the life of your project. Drupal’s built-in SimpleTest framework can be a step in the project build process and further integrate its output with JUnit-compatible post-processing plug-ins. Jenkins notification features help keep developers up-to-date on the status of the project. With a little configuration, this can produce a complete solution.

PS: This basic demonstration of Jenkins did not cover the issues of security and authentication. As you’d expect, Jenkins supports users and access rights. On real installations such configuration should be considered mandatory, even if the system is in your internal network.

Oct 06 2011
Oct 06

This article covers the configuration and use of IMCE (and related modules) to integrate uploading and inserting images within your Drupal content. We assume you are using either TinyMCE or CKEditor with the Wysiwyg integration module, but in a separate post we will cover using IMCE with the BUEditor, a simpler text editor which also works well with Drupal. Note: This article uses Drupal 7, but most of the tips should also be helpful if you are configuring a Drupal 6 site for the same functionality. Indeed, this site is still running on Drupal 6 and also uses a Wysiwyg-integrated CKEditor, IMCE, the Image resize filter, and Lightbox2.

Add necessary modules to sites/all/modules

Add necessary IMCE-related modules to your sites/all/modules directory

If you’ve been following along, you’ve already added the IMCE and IMCE-Wysiwyg Bridge modules; otherwise this is the first step you’ll want to take. In addition to these required modules, this post also covers using the Image Resize Filter and Lightbox2 modules, which work together with IMCE and Wysiwyg to allow you to automatically create smaller images embedded in your content, which are linked to the full-size images and can optionally be viewed in a Lightbox overlay. This is very cool, especially if your original images are wider than the content area and you wish to give users a closer look without actually opening a new window for the image or forcing the user to click the back arrow to return from a linked image to your Drupal content. The IMCE Mkdir module allows you to add directories to your file hierarchy so that you can keep uploaded media nicely sorted.

Activate the modules you’ve added

Activate all the modules you’ve added

You’ll find the Image Resize Filter in the “Input filters” fieldset. IMCE and IMCE Mkdir should be in the “Media” fieldset. And the IMCE-Wysiwyg Bridge and Lightbox2 modules are activated in the “User interface” fieldset. Click on the “Save configuration” button and you’re ready to move on. Note: Using Drush to add and activate modules is outside the scope of this article, but is a nice time-saving trick.

Configure IMCE

After saving your configuration, you can go through and click on the “Configure” links beside the modules which have additional configuration. If you only intend to allow your “User-1” (initial admin account) to upload and insert images into content, then the defaults for IMCE might already be suitable. But assuming you have other roles who you trust enough, you’ll probably want to adjust the default configuration and permissions.

Start by taking a look at the IMCE configuration for your “User-1” profile (we have made no changes to this profile, so will not display a screenshot, but if you don’t have IMCE installed yet, you can see a screenshot of this configuration here).

For the sake of this example use case, we have created additional “staff” roles for “Editor” and “Author” users, who will be allowed to use the “Full HTML” text format (or a custom text format which allows image uploads) and will be allowed to upload images using IMCE and perform various levels of file administration. Our standard anonymous and authenticated users will not be allowed to use a text format with <img> tags at all, so we will not need corresponding IMCE profiles (allowing untrusted users to use <img> tags is a potential security issue which is best avoided; using BBCode or Markdown for these roles can help mitigate the risk if you really want to allow new users to display images on your site — but further discussion of this matter should be considered outside the scope of this article). If you do add new roles, be sure to give them appropriate permissions. For this use case, we’ve given our Author and Editor roles permission to “Use the Full HTML text format” and permissions to create and edit various content types. (admin/people/permissions/list)

Add a new IMCE profile

Import settings from User-1 profile then tweak a bit for our Staff profile.You will probably wish to add a new profile for any new roles. For my simple use case, I will create one “Staff” profile which will have almost the same defaults as the “User-1” profile. We can save time by clicking the “Import settings from other profiles:” link labeled “User-1” and then tweaking the profile a bit more. We might want to cap the directory quota a bit, but not nearly as much as the 2MB, which is the default for a new profile. We also want to allow our staff to create directories in the main files area rather than within a subdirectory with their user-ID. Be sure your settings are a good fit to your use case.

Make your Role-Profile assignments

Make sure appropriate roles are assigned to a profileAssign the “Staff” profile to appropriate roles and make sure the “weight” of each role has them in correct order of their importance (descending order). We aren’t going to allow authenticated or anonymous users to upload images, so we won’t assign any profile to them.

Make sure applicable Wysiwyg profiles include IMCE in “Buttons and plugins”

Make sure you check the IMCE checkbox in your Wysiwyg profile configuration.admin/config/content/wysiwyg/profile
Assuming you have followed the steps in the previous article in this series, you have already configured Wysiwyg profiles for your editor(s) of choice. The IMCE-Wysiwyg Bridge module which you've activated in this lesson adds another checkbox (to at least some of the editors which you can integrate via Wysiwyg, e.g. CKEditor and TinyMCE), labeled IMCE, which you'll probably see down in the bottom row of your “Buttons and plugins” section for each applicable profile. Check the IMCE box and the “Image” (and “Advanced image” for more features in TinyMCE) checkboxes. The IMCE checkbox does not actually add a button to the editor’s menu bar (you just see the normal image button). The pop-up box for adding images should now include a link to “Browse server”.

Configure the Image resize filter module and Lightbox for your text formats

Configure your Full HTML (and/or appropriate) text format(s) for Image resize filter and Lightbox filteradmin/config/content/formats
The only settings for the Image resize filter are found in Text formats. It is a filter which you can turn on and configure individually for each text format. What we want is to configure the Image resize filter to link a resized image to the original and display the full-sized original image in a Lightbox overlay. In my simple use case, I'm giving all “staff” roles access to Full HTML; you may wish to create and configure an additional text format, e.g. one somewhat more restrictive. We need to make sure the Lightbox filter is active, as well as the Image resize filter. I have had good success with the Filter processing order displayed (with the Image resize filter running before the Lightbox filter). Note: There are several other Lightbox-related filters available if you want to use Lightbox for other special purposes (e.g. video, slideshows, etc), but for the basic needs of our use case, we only need the “Lightbox filter”.

When the Image resize filter is active, there is a tab at the bottom of the Text format configuration screen to adjust its settings. Click on that tab and at least select the option to resize locally stored images. Check the box next to “If resized, add a link to the original image.” We can see, from looking at the help text for the Lightbox filter that “Image links with rel="lightbox" in the <a> tag will appear in a Lightbox when clicked on.” So we put if we put “lightbox” in the text field for adding a rel attribute, everything should work correctly. Note: The JavaScript degrades gracefully — even if JavaScript is unavailable or inactive, the link will still work; it just won’t open the full-size image in a Lightbox overlay, but in the current window.

There are additional settings available for Lightbox2

Assuming you only want Lightbox for the purpose of giving visitors a better look at images resized and embedded in your content, the default settings should suffice. Lightbox will even add captions to images if you add a title attribute to the images. This is default behavior. But if you want to use Lightbox to view galleries of images (e.g. a group of images attached to a node), adjust settings for displaying video content in a Lightbox, prevent Lightbox from being active on certain pages or sections of your site, or want to configure Lightbox for Flickr content, Gallery2, Image assist, or other possible integrations, there are some settings you may wish to adjust. Note: there are four tabs at the top of the Lightbox configuration page, so in addition to all the settings hiding in the individual collapsed fieldsets on the “General” tab, there are dozens more settings there for you to tweak. Digging into everything you can do with Lightbox2 is well outside the scope of this article, but may be covered at a later time.

You are now ready to start uploading and inserting images

There are a few steps to the process of adding an image into your content:

Click on the Image button in your editor…

Put your cursor at the beginning of a paragraph and click the “Image” buttonBe sure your cursor is at the beginning of the paragraph where you want your image to appear (especially if you want text to flow around your image). The “Insert/edit image” button is similar in both TinyMCE and CKEditor:

Click on the “Browse server” button…

Click on the “Browse server” button to select an image You should get a pop-up window for inserting an image, which should look something like this. Note: This illustration shows the basic Image popup option for TinyMCE, further below we also show what the popup looks like if you've selected the “Advanced image” option in TinyMCE (recommended, if using TinyMCE) or if you are using CKEditor (similar to TinyMCE with the “Advanced image” plugin option.)

Click on the “Upload” button in the IMCE window…

Click on the “Upload” button in the IMCE windowIn addition to the “Upload” button, which pops up a “browse” window to files on your local operating system, this window is where you can create a directory structure for your files. You may want to create directories for your content types and/or for individual nodes, if each article has many images. If you’ve set permissions in your IMCE profile for non-admin users (e.g. the “Staff” profile we created) to upload, create, and/or delete directories, you will see the corresponding buttons in this window. Note that if you use IMCE’s “Resize” function, this will create a resized version of your file as the “original” passed to your editor, which would be viewed in your Lightbox overlay. I do not usually use the “Resize” or “Crop” buttons in the IMCE window. Resizing an image, especially one in a stored in a file-type with “lossy” compression, e.g. JPEG, is best done as few times as possible, so we should preferably only upload files which are already cropped and sized the way we wish them to appear in the Lightbox overlay. That said, there may still be times when these functions are useful.

Click on the “Insert file” button…

Click the “Insert file” button to pass the file details to your editor’s image popup

After you select a file to upload (you can upload several and then just select them from your server directory as you insert them), click on the “Insert file” button.

Adjust options in your editor’s Image popup window

Selecting left or right alignment translates into inline CSS styling: style="float: left; ", for example. This is a good time to talk about some of the differences between the image popup panels provided by TinyMCE and CKEditor. The “Advanced image” plugin for TinyMCE provides a field for “class”, which can be a better way of styling image placement since the class can also include padding or margin settings, etc. It also allows you to resize an image, automatically adjusting the second dimension (width or height) to keep the same ratio, and provides a field for the image title, which is used by Lightbox2 to provide a caption below the image. To get the advanced options, select both the “Image” and “Advanced image” options when configuring “Buttons and plugins” for the applicable Wysiwyg text format(s). Be sure to enter something useful in the “Image description” field; this will be your alt text; it is displayed if the image does not load or if a visitor is using assistive technologies (i.e. alt tags are required for better accessibility); alt tags are also required if you want pages to pass HTML validation on W3C and are useful for providing search engines more information about an image (so are good for SEO). In CKEditor, the standard “Image” button yields a popup with all the features of the “Advanced image” version in TinyMCE. The fields and buttons are labeled somewhat differently, but each has three tabs which include fields which provide basically the same end result.

These are the most important two tabs in the TinyMCE Image popup

The “General” tab in TinyMCE’s advance image popup includes both “Image description” (alt) and “Title” fields.The TinyMCE “Advanced image” popup has a tab for “Appearance”, where you can set alignment, dimensions, and other styling.

The “General” tab in TinyMCE’s “Advanced image” popup includes both “Image description” (alt) and “Title” fields. The popup also has a tab for “Appearance”, where you can set alignment (i.e. “float” left or right), dimensions, and other styling.

The corresponding tabs and fields provided by CKEditor…

In CKEditor, the “Image Info” tab provides your basic Alt text and size options, as well as the “Alignment” for floating an image left or right in your content.The “Advanced” tab in CKEditor’s image popup provides a field for the HTML title attribute (labeled “Advisory title”) and allows you to tweak the inline CSS styling.

In CKEditor, the “Image Info” tab provides your basic alt text and size options, as well as the “Alignment” for “floating” an image left or right in your content. The “Advanced” tab provides a field for the HTML title attribute (labeled “Advisory title”) and allows you to tweak the inline CSS styling.

Resized images appear in the Lightbox overlay when clicked

And this is what your Lightbox overlay will look like (if you adjust the width and/or height in the editor's popup window or in the HTML source code, the original is displayed in the Lightbox overlay when you click on the resized version).

What your Lightbox overlay will look like

Sep 18 2011
Sep 18

In Drupal, there are actually a number of ways to add a WYSIWYG editor to a text area. The new “Drupal way”, used on over 150,000 Drupal sites and arguably not so “new” anymore, is to use the Wysiwyg integration module, which has support for several of the editor libraries. I would personally suggest using it, if your needs can be met by it, since it's becoming more and more powerful and offers a fair bit of flexibility to easily change the configuration or editor used. That said, there may still be reason, in Drupal 7, to use one of the single-library integration modules, such as the still-popular CKEditor project. The TinyMCE integration module development has already been abandoned in favor of Wysiwyg, but it's good to have alternatives. Note: In this post, we assume you already know your way around Text formats. Text format configuration can be one of the most tricky parts of properly setting up your WYSIWYG experience, so if you don't already feel you know your way around this common stumbling block, be sure to read our recent post about Text formats / Text filters, too. This article is a companion-post to that one, but it also includes some degree of overlap, since when we turn on the Lightbox and Image Resize Filter modules, we have new filters we'll want to use in some text formats and we will want to pay attention to the order in which they are applied, so we will briefly revisit this topic here.

If you happen to like the more “minimalist” editors, and your site's users won't be freaked out by having to see actual HTML code, you may wish to consider using BUEditor instead of any of those which integrate with Wysiwyg. We will cover using it in another post, since I've personally been convinced that it's maybe even more awesome than a “WYSIWYG” editor. This post will simply cover setting up TinyMCE or CKEditor with the Wysiwyg integration module.

First we'll add and activate the Wysiwyg module

Put all contributed modules in the sites/all/modules directory

Please start by downloading the latest stable release of the Wysiwyg module. Contributed modules like this are usually added to the sites/all/modules directory. It's activated on the Drupal admin/modules page in the “User interface” fieldset, which you should see near the bottom of the page. Since it's common to want images within posted content, we are also going to demonstrate using IMCE and the IMCE-Wysiwyg Bridge to upload and insert images and we'll also add Lightbox2 and the Image resize filter module for improved display of images. You can add all four of these image-related modules to your sites/all/modules directory and also activate them at admin/modules. This blog post will not delve deeply into configuring images or associated modules — this can be a rather complicated topic, so it should also be covered in a separate post. So, while we'll be integrating IMCE and Lightbox2 for display of embedded images in this tutorial post, the full configuration of these modules will be covered separately.

Add editor library(ies) to your sites/all/libraries directory

If you aren't already sure that you want to use TinyMCE or CKEditor, it's a good idea to take a little time to experiment with at least some of the different alternatives to determine which you like the best for your use case. CKEditor and TinyMCE are the two editors which integrate best with the Wysiwyg module and IMCE (to allow uploading and adding images to text areas). You could try some of the others, but bear in mind that, at least at this stage, many of the editors are not very fully supported by the Wysiwyg integration and may not have support for the full button sets available, nor for integrated image upload, etc.

Editor code, once downloaded, is extracted in sites/all/librariesEditor libraries are added to Drupal by extracting their code files and putting them in the sites/all/libraries directory, which you should create if it doesn't already exist. In a few odd cases you may need to rename a directory or add/remove a level of hierarchy, so it's best to read the installation directions on the Wysiwyg profile page, which you'll find at the bottom in a collapsed field-set. Click it open and find the directions that apply to your editor of choice. For TinyMCE or CKEditor, you should be able to simply download the latest stable version of the Javascript libraries for the editor and extract the archive (.zip file) into the sites/all/libraries directory, as per the directions:Download and extract the TinyMCE javascript library in the sites/all/libraries directory

One of my long-time personal favorite editors for embedding in browsers is the Markitup editor. It's simple, light-weight, and technically is not truly “Wysiwyg”, but it offers some nice features you won't normally see in the really fancy-looking editors. If your target audience could be described as “HTML-savvy” (or BB-Code-savvy / Markdown-savvy, etc), they may prefer such an editor since they always have access both to editor buttons and to a view of the generated code. And where other editors may add a dozen lines of in-line CSS styling when you paste text from a styled document, Markitup will only copy the text, not the styling. Even better, you can wrap a tag-set around selected text with just a simple keyboard shortcut.

What the Markitup editor normally looks like

I like the nice combination of simplicity and power that Markitup offers, but you can't get it with Wysiwyg module and Drupal 7.

As you can see it offers most tags you'd want for any HTML content and you'd only need to hand-code a few tags, here and there. You can experiment with this configuration on the Markitup "examples" page.

This is Markitup integrated by Drupal 7's latest version of the Wysiwyg module

Markitup has very limited support with Wysiwyg integration in Drupal 7.And that's with ALL available buttons selected… so Wysiwyg integration supports only a fraction of Markitup's standard Filtered HTML button set. Most disappointing! Well, having read up on the topic, I believe that it's just some work, currently, to integrate each button, so some editors that many people use are much better supported while others may still be more of a “development stub” example that the community can build on. It may just be that those of us interested in Markitup will have to help complete its integration with Wysiwyg (or help complete the port of the Markitup module to Drupal 7). But the search for a better “non-WYSIWYG” editor (text editor) did lead me to the BUEditor, a nice alternative which can be used with Drupal 7.

The safest solution is to use one editor

It's probably best to adopt just one editor for all text formats. Otherwise if you have privileged users with access to more than one format, that will mean two different libraries of Javascript code are added to the text areas and you can start to run into weird conflicts… like no editor showing up for a text format which is assigned to an editor… or no ability to properly switch between “rich text” and “code” views. It also means a lot more Javascript is added to each page, so it can delay initial page loads. So we strongly suggest choosing one editor which is sufficient for all your needs. To my knowledge, since only TinyMCE and CKEditor are supported by the IMCE-Wysiwyg bridge (which you may want if you'd like to add images to posts), it might be worth trying out both, before selecting one. From this point on, in this article, we are assuming you have settled on TinyMCE (or CKEditor), so some steps will include tips or screenshots which apply only to TinyMCE (but CKEditor is very similar in terms of the configuration).

We'll start by configuring the Filtered HTML button-set

Select TinyMCE as editor for Filtered HTML and saveFirst we need to select an editor to use for “Filtered HTML”. Select “TinyMCE” (or CKEditor, or whatever) from the select list for “Editor” and then click “Save”.

But wait… you still have to select the buttons

If we stopped now, we would only have an empty editor, one with no buttons — which would be much like no editor at all. Don't make the mistake of stopping now and thinking the defaults are probably good enough. Unfortunately, they aren't. Be sure to click on the “Edit” link which is now active for Filtered HTML and TinyMCE in the “Operations” column.

Select appropriate buttons when configuring your editor for a "filtered" text format

Select appropriate buttons when configuring TinyMCE for the Filtered HTML text formatJust select buttons which will be useful and appropriate for the limits of the text format. In this image, you can see what should be an appropriate selection of buttons for a Filtered HTML text format. I would be sure to add the <p>, and to be safe, both <br> and <br /> tags to the allowed list of tags for your Filtered HTML text format. (See related article for more info about configuring text formats). Why? Now you can turn off the “turn line breaks into HTML” filter (which turns double line-breaks into <p> tags and single ones into <br />). You will probably find that any WYSIWYG editor is going to add those tags, anyway. And people will try to add them (in code view) and be annoyed by having them stripped out on output. Plus, you'll probably find that your code gets re-formatted, no matter what settings you use in configuring the editor.

Tweak the settings for “Cleanup and Output” (optional)

Adjust settings for Cleanup and Output of HTML code from TinyMCEI personally can normally accept all the other default settings, but change the Cleanup and Output settings, as shown. Verify HTML should be good, but I don't like the editor to add lots of styling when people paste. Let's try to keep that in the CSS files. I also don't like all the linebreaks removed, since I tend to look at the code, and I'm sure many others are like me and will also want to see or adjust the code. Assuming you don't have the “convert line breaks to HTML” turned on (you shouldn't if using a WYSIWYG editor), it’s safe to leave “Apply source formatting” on. It will give you some appropriate line breaks (hopefully) so that it’s easier to read through the code. The “Force cleanup on standard paste” option helps clear out some of the garbage that people might attempt to paste in. I’ve seen no reason to disable that feature.

Now your editor should look something like this

Your TinyMCE editor should now look something like this.

Provided you are configuring TinyMCE and selected the same buttons I did, your editor should look something like this, at least to your regular users who probably will only have a limited set of HTML tags they are using.

Make sure selected buttons correspond to allowed tags

To make sure that your editor and corresponding text formats are properly configured, you should test the different buttons and pay attention to the preview. Here, we can see that the <strike> tag is not allowed by the current format (Filtered HTML) and should be added to the list of allowed tags for Filtered HTML if we want to have that button available for use*. Nothing is much more confusing and annoying to users than when they add the proper code, can see it in the code view, but don't see the same result in the saved output. Look at what tags are output by the editor (for each button used) and either disable the button or add the corresponding tag to the text format's allowed tags. Use the node preview button (at the bottom of the page, next to the “Save” button) to check [*Note: Actually, in the current TinyMCE, the “strikethrough” text treatment is accomplished by wrapping text in <span> tags with a style attribute which achieves the same effect. In the current version of CKEditor, it's <strike> tags. In other editors, you may find the “same” button adds <del> tags. All three achieve the same effect and if you want to include the strike-through button, you may wish to add more than one of these tags to those allowed for your text format.]

Don't enable the “preview” button

Don't add the preview button to your editor for a filtered HTML text format. It will render tags that are removed by Drupal's filter system. There is a common issue across various editors integrated by Wysiwyg. If the editor provides a “preview” button, and most do, the preview will render any HTML, regardless of tag limitations imposed by the current text format. For example, this means that images and <strike> tags used for strikethrough text will work as expected in a preview, but since the tags are not part of the default “Filtered HTML” text format, the <strike> or <img> tags will actually be removed on output instead of displaying an image or the text between the <strike> tags with “strikethrough” styling. You can still preview by clicking on the “Preview” node button, before saving, but the “preview” provided by the editor can be misleading. Hopefully future development of the Wysiwyg module might implement something like Ajax markup, which integrates with BUEditor (but not with the Wysiwyg module) to display text with correct output, i.e., according to active text format filter settings, etc.

Repeat for other text formats, but keep it simple

It should be easier to set up your editor for Full HTML. You may also wish to create a filtered html text format for trusted users, e.g. a “Filtered+” HTML. Just follow the same steps. Add a few more tags to the allowed set (perhaps you trust these users to add images or sub-headings). My only advice is to follow the KISS principle and “keep it super simple”. It's easy to get carried away and add all buttons available for Full HTML. Resist the urge. You are more likely to run into bugs and you'll end up with an overwhelming user experience. I'd suggest keeping the button-set limited to the most useful tags.

This is what the button-set looks like if you select them all:
TinyMCE and CKEditor are overwhelming if all buttons are selected for Full HTML

My recommendation would be to add just a few more buttons to the set you created for Filtered HTML. If you want users to be able to simply add images within their text and you've turned on the IMCE module, be sure to select both the Image and IMCE checkboxes. Working with images and IMCE is complex enough that we'll cover that in the next post.

Make sure the text filters for the format make sense and are in logical order

Minimal filters for Drupal's FIltered HTML text format

Now you should check your filters. Make sure the appropriate filters are enabled and that text is processed by the filters in a reasonable order. The Filtered HTML Text format normally includes the “convert line breaks to HTML” filter, which doesn't make sense if you are using a WYSIWYG editor (just be sure to include the <p> and <br /> / <br> tags in your allowed set). For Full HTML or other text formats with images, you'll probably want to include other filters, such as the Image Resize Filter and/or Lightbox. Again, we'll cover image-related tips in the next post.

Congratulations, you now have a killer WYSIWYG editor configured!

Be sure to test that everything works the way you want it to. Be sure to test that all of your user roles have the expected access to the editors and text formats and that features are working as expected. If you are working on a local development environment, it can be helpful to turn on the Devel module's “switch users” block and give all user roles permissions to use it. This will allow you to easily switch between a user of one role and your user-1 admin to tweak permissions or other configuration.

Be sure to check back for our next post about working with images in a WYSIWYG editor, which should be posted in the next few days.

Apr 28 2009
Apr 28

I just recorded this video showing how to use the Menu Attributes module for Drupal 6. I'm still getting used to recording so please excuse flawed delivery :)

I'll be posting a transcription with some screenshots later this week you prefer that way of learning.

Oct 19 2008
Oct 19

The Ubiquity extension for Firefox is an awesome tool that allows you to download user created mashups to do some really cool things in your browser. The video below shows the power of what it can do.

It was introduced in August this year and there are already quite a few user created commands, including this one by Nils Werner, which will pre-populate a blog entry with the selected text on a web page. The only problem is that is requires your blogging engine to accept text via the GET parameters, which Drupal doesn’t do by default.  No problem, just install the Prepoluate module and away you go.

Here are some step by step instructions on how to set it up.

  1. Install this Ubiquity command (it will display an information bar at the top of the page if you have Ubiquity enabled).
  2. Now that you have the command, you need to tell it where your blog form is. To do this, press CTRL + Space (this is the default hotkey to launch Ubiquity) and type “blog set” and press enter. A popup window should appear, and you will need to enter
    (this assumes that you want to populate the ‘blog’ content type)
  3. Once that is set, simply select the text on any web page and press CTRL + Space and type ‘blog this’ and you will be taken to your Drupal site with that text already entered into the form

It’s pretty simple and makes it much quicker to jot down ideas for your blog while surfing the net.

Also, because Ubiquity commands are written in Javascript there should be a way of doing this without using the Prepopulate module and interacting with the form elements directly, but I’m not familiar enough with Ubiquity yet to work this out. If you have any ideas, let me know in the comments.

Apr 28 2008
Apr 28

I was inspired by this episode of SEOMoz's whiteboard friday where Rand talks about tracking non-traditional conversions (like when someone posts a comment on your blog) so I decided to check out how easy this was to do in Drupal.

By default, when someone submits a comment, they are redirected back to the node to view their comment (even if it needs approval first). This causes a problem because it doesn't really give us a unique URL to use as a goal in Google Analytics (unlike the webform module).

What you'll need to do is grab the great actions module which we will use to redirect the user to a custom thank you page once their comment has been submitted. And while thats downloading and installing, create a new page that will be the thank you page when users submit a comment.

Next, navigate to Site Configuration > Actions and create a new advanced action to redirect to your new thank you page click save. Now, just head over to the Triggers settings (Site Building > Triggers > Comments) and assign your new action to the "After saving a new comment" trigger.

Make a new advanced action available

Now all you need to do is setup the goal in Google Analytics and wait for the data.

Do you have any other non-traditional conversions that you're tracking? If so, what are they and how are you tracking them?

Apr 12 2008
Apr 12

Submitted by リーボック シューズ レディース、リーボッ (not verified) on Thursday, December 12, 2013 at 12:56pm.

{I have|I've} been {surfing|browsing} online more than {three|3|2|4} hours today, yet I never found any
interesting article like yours. {It's|It is}
pretty worth enough for me. {In my opinion|Personally|In my view},
if all {webmasters|site owners|website owners|web owners} and bloggers made good content as you did, the {internet|net|web} will be {much more|a lot more}
useful than ever before.|
I {couldn't|could not} {resist|refrain from} commenting.
{Very well|Perfectly|Well|Exceptionally well} written!|
{I will|I'll} {right away|immediately} {take hold of|grab|clutch|grasp|seize|snatch} your
{rss|rss feed} as I {can not|can't} {in finding|find|to find} your {email|e-mail} subscription {link|hyperlink} or {newsletter|e-newsletter} service.

Do {you have|you've} any? {Please|Kindly} {allow|permit|let} me {realize|recognize|understand|recognise|know} {so that|in order that} I {may just|may|could} subscribe.
{It is|It's} {appropriate|perfect|the best} time to make some plans for the
future and {it is|it's} time to be happy.
{I have|I've} read this post and if I could I {want to|wish to|desire
to} suggest you {few|some} interesting things or {advice|suggestions|tips}.
{Perhaps|Maybe} you {could|can} write next articles referring to this article.
I {want to|wish to|desire to} read {more|even more} things
about it!|
{It is|It's} {appropriate|perfect|the best} time
to make {a few|some} plans for {the future|the longer term|the long run} and {it is|it's} time to be happy.
{I have|I've} {read|learn} this {post|submit|publish|put up} and
if I {may just|may|could} I {want to|wish to|desire to} {suggest|recommend|counsel} you {few|some} {interesting|fascinating|attention-grabbing} {things|issues} or {advice|suggestions|tips}.
{Perhaps|Maybe} you {could|can} write {next|subsequent} articles {relating to|referring to|regarding} this article.
I {want to|wish to|desire to} {read|learn} {more|even more} {things|issues} {approximately|about} it!|
{I have|I've} been {surfing|browsing} {online|on-line} {more than|greater than} {three|3} hours {these days|nowadays|today|lately|as
of late}, {yet|but} I {never|by no means} {found|discovered}
any {interesting|fascinating|attention-grabbing}
article like yours. {It's|It is} {lovely|pretty|beautiful} {worth|value|price} {enough|sufficient} for me.
{In my opinion|Personally|In my view}, if
all {webmasters|site owners|website owners|web owners} and bloggers made {just right|good|excellent} {content|content material} as {you did|you probably did}, the {internet|net|web} {will be|shall be|might be|will probably be|can be|will likely be} {much more|a lot more} {useful|helpful} than ever before.|
Ahaa, its {nice|pleasant|good|fastidious} {discussion|conversation|dialogue} {regarding|concerning|about|on the topic of} this {article|post|piece of writing|paragraph} {here|at this
place} at this {blog|weblog|webpage|website|web site}, I have read
all that, so {now|at this time} me also commenting {here|at this
I am sure this {article|post|piece of writing|paragraph} has touched all the internet {users|people|viewers|visitors}, its
really really {nice|pleasant|good|fastidious} {article|post|piece of writing|paragraph}
on building up new {blog|weblog|webpage|website|web site}.|
Wow, this {article|post|piece of writing|paragraph} is {nice|pleasant|good|fastidious}, my {sister|younger sister} is analyzing {such|these|these kinds of} things, {so|thus|therefore} I am going
to {tell|inform|let know|convey} her.|
{Saved as a favorite|bookmarked!!}, {I really like|I like|I love} {your blog|your
site|your web site|your website}!|
Way cool! Some {very|extremely} valid points!
I appreciate you {writing this|penning this} {article|post|write-up} {and the|and
also the|plus the} rest of the {site is|website is} {also very|extremely|very|also really|really} good.|
Hi, {I do believe|I do think} {this is an
excellent|this is a great} {blog|website|web site|site}.
I stumbledupon it ;) {I will|I am going to|I'm going to|I may} {come back|return|revisit} {once again|yet again}
{since I|since i have} {bookmarked|book marked|book-marked|saved as a favorite} it.
Money and freedom {is the best|is the greatest} way to change, may you be
rich and continue to {help|guide} {other people|others}.|
Woah! I'm really {loving|enjoying|digging} the
template/theme of this {site|website|blog}. It's simple, yet effective.

A lot of times it's {very hard|very difficult|challenging|tough|difficult|hard} to get that
"perfect balance" between {superb usability|user friendliness|usability} and {visual appearance|visual appeal|appearance}.
I must say {that you've|you have|you've} done a {awesome|amazing|very
good|superb|fantastic|excellent|great} job with this.
{In addition|Additionally|Also}, the blog loads {very|extremely|super} {fast|quick} for me on {Safari|Internet
explorer|Chrome|Opera|Firefox}. {Superb|Exceptional|Outstanding|Excellent} Blog!|
These are {really|actually|in fact|truly|genuinely} {great|enormous|impressive|wonderful|fantastic} ideas in {regarding|concerning|about|on the topic of} blogging.

You have touched some {nice|pleasant|good|fastidious} {points|factors|things} here.
Any way keep up wrinting.|
{I love|I really like|I enjoy|I like|Everyone loves} what you guys {are|are
usually|tend to be} up too. {This sort of|This type of|Such|This kind of} clever work
and {exposure|coverage|reporting}! Keep up the {superb|terrific|very
works guys I've {incorporated||added|included} you guys to {|my|our||my personal|my own} blogroll.|
{Howdy|Hi there|Hey there|Hi|Hello|Hey}! Someone in my {Myspace|Facebook} group shared this {site|website} with us so I came
to {give it a look|look it over|take a look|check it out}.

I'm definitely {enjoying|loving} the information. I'm {book-marking|bookmarking} and will be
tweeting this to my followers! {Terrific|Wonderful|Great|Fantastic|Outstanding|Exceptional|Superb|Excellent} blog and {wonderful|terrific|brilliant|amazing|great|excellent|fantastic|outstanding|superb} {style and design|design
and style|design}.|
{I love|I really like|I enjoy|I like|Everyone loves} what you guys {are|are usually|tend to
be} up too. {This sort of|This type of|Such|This kind
of} clever work and {exposure|coverage|reporting}! Keep up
the {superb|terrific|very good|great|good|awesome|fantastic|excellent|amazing|wonderful} works guys I've {incorporated|added|included} you guys to {|my|our|my personal|my
own} blogroll.|
{Howdy|Hi there|Hey there|Hi|Hello|Hey} would you mind {stating|sharing} which
blog platform you're {working with|using}?
I'm {looking|planning|going} to start my own blog {in the near future|soon} but I'm having a {tough|difficult|hard} time
{making a decision|selecting|choosing|deciding} between BlogEngine/Wordpress/B2evolution and Drupal.
The reason I ask is because your {design and
style|design|layout} seems different then most blogs and I'm looking for something {completely unique|unique}.
P.S {My apologies|Apologies|Sorry} for {getting|being} off-topic but I had to ask!|
{Howdy|Hi there|Hi|Hey there|Hello|Hey} would you mind letting me know which {webhost|hosting company|web host} you're {utilizing|working
with|using}? I've loaded your blog in 3 {completely different|different}
{internet browsers|web browsers|browsers} and I must say this blog loads a lot {quicker|faster} then most.

Can you {suggest|recommend} a good {internet hosting|web
hosting|hosting} provider at a {honest|reasonable|fair} price?

{Thanks a lot|Kudos|Cheers|Thank you|Many thanks|Thanks},
I appreciate it!|
{I love|I really like|I like|Everyone loves} it {when people|when individuals|when folks|whenever
people} {come together|get together} and
share {opinions|thoughts|views|ideas}. Great {blog|website|site}, {keep it up|continue the good work|stick with it}!|
Thank you for the {auspicious|good} writeup. It in fact was
a amusement account it. Look advanced to {far|more} added agreeable from you!

{By the way|However}, how {can|could} we communicate?|
{Howdy|Hi there|Hey there|Hello|Hey} just wanted to give you a quick
heads up. The {text|words} in your {content|post|article} seem to be running off the screen in
{Ie|Internet explorer|Chrome|Firefox|Safari|Opera}. I'm not sure if
this is a {format|formatting} issue or something to do with {web browser|internet browser|browser}
compatibility but I {thought|figured} I'd post to let you know.
The {style and design|design and style|layout|design} look great though!

Hope you get the {problem|issue} {solved|resolved|fixed} soon.
{Kudos|Cheers|Many thanks|Thanks}|
This is a topic {that is|that's|which is} {close to|near to} my heart...
{Cheers|Many thanks|Best wishes|Take care|Thank you}! {Where|Exactly where} are your contact details though?|
It's very {easy|simple|trouble-free|straightforward|effortless} to find out any {topic|matter} on {net|web} as compared to {books|textbooks}, as I found this {article|post|piece of writing|paragraph} at this {website|web site|site|web page}.|
Does your {site|website|blog} have a contact page? I'm having {a tough time|problems|trouble} locating it but, I'd like to {send|shoot} you an {e-mail|email}.

I've got some {creative ideas|recommendations|suggestions|ideas} for your blog you might be interested in hearing.
Either way, great {site|website|blog} and I look forward to seeing it {develop|improve|expand|grow} over time.|
{Hola|Hey there|Hi|Hello|Greetings}! I've been {following|reading} your {site|web site|website|weblog|blog} for {a long time|a while|some time} now and finally
got the {bravery|courage} to go ahead and give you a shout out from
{New Caney|Kingwood|Huffman|Porter|Houston|Dallas|Austin|Lubbock|Humble|Atascocita} {Tx|Texas}!
Just wanted to {tell you|mention|say} keep up the {fantastic|excellent|great|good} {job|work}!|
Greetings from {Idaho|Carolina|Ohio|Colorado|Florida|Los angeles|California}!
I'm {bored to tears|bored to death|bored} at work so I decided
to {check out|browse} your {site|website|blog} on my iphone during lunch break.
I {enjoy|really like|love} the {knowledge|info|information} you {present|provide} here and can't wait to take
a look when I get home. I'm {shocked|amazed|surprised} at how {quick|fast} your blog loaded
on my {mobile|cell phone|phone} .. I'm not even using
WIFI, just 3G .. {Anyhow|Anyways}, {awesome|amazing|very good|superb|good|wonderful|fantastic|excellent|great} {site|blog}!|
Its {like you|such as you} {read|learn} my {mind|thoughts}!

You {seem|appear} {to understand|to know|to grasp} {so much|a lot} {approximately|about}
this, {like you|such as you} wrote the {book|e-book|guide|ebook|e book} in it or something.

{I think|I feel|I believe} {that you|that you simply|that you just} {could|can} do with {some|a few} {%|p.c.|percent} to {force|pressure|drive|power} the message
{house|home} {a bit|a little bit}, {however|but} {other than|instead of} that, {this is|that is} {great|wonderful|fantastic|magnificent|excellent} blog.
{A great|An excellent|A fantastic} read. {I'll|I will}
{definitely|certainly} be back.|
I visited {multiple|many|several|various} {websites|sites|web sites|web pages|blogs} {but|except|however} the audio
{quality|feature} for audio songs {current|present|existing} at this {website|web site|site|web page} is {really|actually|in fact|truly|genuinely}
{Howdy|Hi there|Hi|Hello}, i read your blog {occasionally|from time to time} and i own a similar one and
i was just {wondering|curious} if you get a lot of spam {comments|responses|feedback|remarks}?
If so how do you {prevent|reduce|stop|protect against} it, any plugin or anything you can {advise|suggest|recommend}?
I get so much lately it's driving me {mad|insane|crazy} so any {assistance|help|support} is very much appreciated.|
Greetings! {Very helpful|Very useful} advice
{within this|in this particular} {article|post}! {It is the|It's the}
little changes {that make|which will make|that produce|that will make} {the biggest|the largest|the greatest|the most important|the most significant} changes.
{Thanks a lot|Thanks|Many thanks} for sharing!|
{I really|I truly|I seriously|I absolutely} love {your blog|your site|your website}..
{Very nice|Excellent|Pleasant|Great} colors & theme.

Did you {create|develop|make|build} {this website|this site|this
web site|this amazing site} yourself? Please reply back as I'm {looking to|trying
to|planning to|wanting to|hoping to|attempting to} create {my own|my very own|my own personal}
{blog|website|site} and {would like to|want to|would
love to} {know|learn|find out} where you got this
from or {what the|exactly what the|just what the} theme {is called|is named}.
{Thanks|Many thanks|Thank you|Cheers|Appreciate it|Kudos}!|
{Hi there|Hello there|Howdy}! This {post|article|blog post}
{couldn't|could not} be written {any better|much better}!
{Reading through|Looking at|Going through|Looking through} this {post|article} reminds me of my previous
roommate! He {always|constantly|continually} kept {talking
about|preaching about} this. {I will|I'll|I am going to|I
most certainly will} {forward|send} {this article|this information|this
post} to him. {Pretty sure|Fairly certain} {he will|he'll|he's going to} {have a good|have
a very good|have a great} read. {Thank you for|Thanks for|Many thanks for|I appreciate you for} sharing!|
{Wow|Whoa|Incredible|Amazing}! This blog looks {exactly|just} like my old one!

It's on a {completely|entirely|totally} different {topic|subject} but it has
pretty much the same {layout|page layout} and design.

{Excellent|Wonderful|Great|Outstanding|Superb} choice of colors!|
{There is|There's} {definately|certainly} {a lot to|a great deal to} {know about|learn about|find out about} this
{subject|topic|issue}. {I like|I love|I really like}
{all the|all of the} points {you made|you've made|you have
{You made|You've made|You have made} some {decent|good|really good} points there.

I {looked|checked} {on the internet|on the web|on the net} {for more info|for more information|to
find out more|to learn more|for additional information} about the
issue and found {most individuals|most people} will go along with your views on {this website|this site|this web site}.|
{Hi|Hello|Hi there|What's up}, I {log on to|check|read} your {new stuff|blogs|blog} {regularly|like every week|daily|on a regular basis}.
Your {story-telling|writing|humoristic} style is {awesome|witty}, keep {doing
what you're doing|up the good work|it up}!|
I {simply|just} {could not|couldn't} {leave|depart|go away} your {site|web site|website} {prior to|before} suggesting that
I {really|extremely|actually} {enjoyed|loved} {the standard|the usual} {information|info}
{a person|an individual} {supply|provide} {for your|on your|in your|to your} {visitors|guests}?
Is {going to|gonna} be {back|again} {frequently|regularly|incessantly|steadily|ceaselessly|often|continuously} {in order to|to} {check up on|check out|inspect|investigate cross-check} new posts|
{I wanted|I needed|I want to|I need to} to thank you for this {great|excellent|fantastic|wonderful|good|very good} read!!
I {definitely|certainly|absolutely} {enjoyed|loved} every {little bit of|bit of} it.

{I have|I've got|I have got} you {bookmarked|book marked|book-marked|saved as a favorite}
{to check out|to look at} new {stuff you|things you} post…|
{Hi|Hello|Hi there|What's up}, just wanted to {mention|say|tell you},
I {enjoyed|liked|loved} this {article|post|blog post}.
It was {inspiring|funny|practical|helpful}.
Keep on posting!|
I {{leave|drop|{write|create}} a {comment|leave a response}|drop a {comment|leave a response}|{comment|leave a
response}} {each time|when|whenever} I {appreciate|like|especially enjoy} a {post|article} on a {site|{blog|website}|site|website} or {I have|if I have} something to {add|contribute|valuable
to contribute} {to the discussion|to the conversation}.

{It is|Usually it is|Usually it's|It's} {a result of|triggered by|caused by} the {passion|fire|sincerness} {communicated|displayed} in the {post|article} I {read|looked at|browsed}.
And {on|after} this {post|article} Reply to comment | Schoonzie.
I {{was|was actually} moved|{was|was actually} excited} enough to {drop|{leave|drop|{write|create}}|post} a
{thought|{comment|{comment|leave a response}a response}} {:-P|:)|;)|;-)|:-)} I {do have|actually do have} {{some|a few} questions|a couple of questions|2 questions} for you {if
you {don't|do not|usually do not|tend not to} mind|if it's {allright|okay}}.

{Is it|Could it be} {just|only|simply} me or {do|does it
{seem|appear|give the impression|look|look as if|look like} like} {some|a few} of
{the|these} {comments|responses|remarks} {look|appear|come across} {like they are|as
if they are|like} {coming from|written by|left by} brain dead {people|visitors|folks|individuals}?
:-P And, if you are {posting|writing} {on|at} {other|additional} {sites|social sites|online
sites|online social sites|places}, {I'd|I would}
like to {follow|keep up with} {you|{anything|everything} {new|fresh} you have to post}.
{Could|Would} you {list|make a list} {all|every one|the complete urls} of {your|all your} {social|communal|community|public|shared} {pages|sites} like your
{twitter feed, Facebook page or linkedin profile|linkedin profile,
Facebook page or twitter feed|Facebook page, twitter feed, or linkedin profile}?|
{Hi there|Hello}, I enjoy reading {all of|through} your {article|post|article post}.
I {like|wanted} to write a little comment to support you.|
I {always|constantly|every time} spent my half an hour to read this {blog|weblog|webpage|website|web
site}'s {articles|posts|articles or reviews|content} {everyday|daily|every day|all the time} along with a {cup|mug} of coffee.|
I {always|for all time|all the time|constantly|every time} emailed this {blog|weblog|webpage|website|web site}
post page to all my {friends|associates|contacts}, {because|since|as|for
the reason that} if like to read it {then|after that|next|afterward} my {friends|links|contacts} will too.|
My {coder|programmer|developer} is trying to {persuade|convince}
me to move to .net from PHP. I have always disliked the idea
because of the {expenses|costs}. But he's tryiong none the less.
I've been using {Movable-type|WordPress} on {a number of|a variety of|numerous|several|various} websites for about a year and am {nervous|anxious|worried|concerned}
about switching to another platform. I have heard {fantastic|very good|excellent|great|good} things about blogengine.net.

Is there a way I can {transfer|import} all my wordpress {content|posts}
into it? {Any kind of|Any} help would be {really|greatly} appreciated!|
{Hello|Hi|Hello there|Hi there|Howdy|Good day}! I could have sworn I've {been to|visited} {this blog|this web site|this website|this site|your blog}
before but after {browsing through|going through|looking at} {some of the|a few of the|many of the} {posts|articles} I realized
it's new to me. {Anyways|Anyhow|Nonetheless|Regardless}, I'm {definitely|certainly} {happy|pleased|delighted}
{I found|I discovered|I came across|I stumbled upon} it and I'll
be {bookmarking|book-marking} it and checking back
{Terrific|Great|Wonderful} {article|work}! {This is|That is} {the
type of|the kind of} {information|info} {that are meant to|that are supposed to|that should} be shared {around the|across the}
{web|internet|net}. {Disgrace|Shame} on {the {seek|search} engines|Google}
for {now not|not|no longer} positioning this {post|submit|publish|put up} {upper|higher}!
Come on over and {talk over with|discuss with|seek advice from|visit|consult with} my {site|web site|website} .

{Thank you|Thanks} =)|
Heya {i'm|i am} for the first time here. I {came across|found} this board and I find It {truly|really} useful
& it helped me out {a lot|much}. I hope to give something back
and {help|aid} others like you {helped|aided} me.|
{Hi|Hello|Hi there|Hello there|Howdy|Greetings}, {I think|I believe|I do believe|I do
think|There's no doubt that} {your site|your website|your web site|your blog} {might be|may be|could
be|could possibly be} having {browser|internet browser|web browser} compatibility {issues|problems}.
{When I|Whenever I} {look at your|take a look at your} {website|web site|site|blog} in Safari, it looks fine {but when|however when|however,
if|however, when} opening in {Internet Explorer|IE|I.E.},
{it has|it's got} some overlapping issues.
{I just|I simply|I merely} wanted to {give you a|provide you
with a} quick heads up! {Other than that|Apart from that|Besides that|Aside from that}, {fantastic|wonderful|great|excellent} {blog|website|site}!|
{A person|Someone|Somebody} {necessarily|essentially}
{lend a hand|help|assist} to make {seriously|critically|significantly|severely} {articles|posts} {I would|I might|I'd} state.
{This is|That is} the {first|very first} time I frequented your {web page|website page} and {to this point|so far|thus far|up to now}?

I {amazed|surprised} with the {research|analysis} you made to {create|make} {this actual|this particular} {post|submit|publish|put up} {incredible|amazing|extraordinary}.
Heya {i'm|i am} for {the primary|the first} time here.
I {came across|found} this board and I {in finding|find|to find} It
{truly|really} {useful|helpful} & it helped me
out {a lot|much}. {I am hoping|I hope|I'm hoping} {to give|to offer|to provide|to present} {something|one thing} {back|again}
and {help|aid} others {like you|such as you} {helped|aided} me.|
{Hello|Hi|Hello there|Hi there|Howdy|Good day|Hey there}!
{I just|I simply} {would like to|want to|wish to} {give you a|offer you a} {huge|big} thumbs up
{for the|for your} {great|excellent} {info|information} {you have|you've got|you
have got} {here|right here} on this post. {I will be|I'll
be|I am} {coming back to|returning to} {your blog|your site|your
website|your web site} for more soon.|
I {always|all the time|every time} used to {read|study} {article|post|piece of writing|paragraph} in news papers but now as
I am a user of {internet|web|net} {so|thus|therefore} from now I am using net for {articles|posts|articles or reviews|content}, thanks to web.|
Your {way|method|means|mode} of {describing|explaining|telling} {everything|all|the whole thing} in this {article|post|piece of writing|paragraph}
is {really|actually|in fact|truly|genuinely} {nice|pleasant|good|fastidious},
{all|every one} {can|be able to|be capable of} {easily|without difficulty|effortlessly|simply} {understand|know|be aware of} it, Thanks
a lot.|
{Hi|Hello} there, {I found|I discovered} your {blog|website|web site|site} {by means of|via|by the use of|by way of}
Google {at the same time as|whilst|even as|while} {searching for|looking for}
a {similar|comparable|related} {topic|matter|subject},
your {site|web site|website} {got here|came} up, it {looks|appears|seems|seems to be|appears to
be like} {good|great}. {I have|I've} bookmarked it in
my google bookmarks.
{Hello|Hi} there, {simply|just} {turned into|became|was|become|changed into} {aware of|alert to} your {blog|weblog} {thru|through|via} Google, {and found|and located} that {it
is|it's} {really|truly} informative. {I'm|I am} {gonna|going to} {watch out|be careful} for brussels.
{I will|I'll} {appreciate|be grateful} {if you|should you|when you|in the event you|in case you|for those who|if you
happen to} {continue|proceed} this {in future}.

{A lot of|Lots of|Many|Numerous} {other folks|folks|other people|people} {will be|shall be|might be|will probably be|can be|will likely
be} benefited {from your|out of your} writing.

{I am|I'm} curious to find out what blog {system|platform} {you have been|you happen to be|you are|you're} {working with|utilizing|using}?

I'm {experiencing|having} some {minor|small} security {problems|issues} with
my latest {site|website|blog} and {I would|I'd} like to find something more {safe|risk-free|safeguarded|secure}.
Do you have any {solutions|suggestions|recommendations}?|
{I am|I'm} {extremely|really} impressed with your writing skills {and also|as well as} with the layout
on your {blog|weblog}. Is this a paid theme or did you {customize|modify} it yourself?
{Either way|Anyway} keep up the {nice|excellent} quality writing, {it's|it is} rare
to see a {nice|great} blog like this one {these days|nowadays|today}.|
{I am|I'm} {extremely|really} {inspired|impressed} {with your|together with your|along with your} writing {talents|skills|abilities} {and also|as {smartly|well|neatly} as} with the
{layout|format|structure} {for your|on your|in your|to your} {blog|weblog}.

{Is this|Is that this} a paid {subject|topic|subject matter|theme} or did you {customize|modify} it {yourself|your self}?
{Either way|Anyway} {stay|keep} up the {nice|excellent} {quality|high quality} writing, {it's|it is}
{rare|uncommon} {to peer|to see|to look} a {nice|great} {blog|weblog} like this one {these days|nowadays|today}..|
{Hi|Hello}, Neat post. {There is|There's} {a problem|an issue} {with your|together with your|along with your} {site|web site|website}
in {internet|web} explorer, {may|might|could|would} {check|test} this?
IE {still|nonetheless} is the {marketplace|market} {leader|chief} and
{a large|a good|a big|a huge} {part of|section
of|component to|portion of|component of|element of} {other folks|folks|other people|people} will {leave out|omit|miss|pass over} your {great|wonderful|fantastic|magnificent|excellent} writing {due to|because of} this problem.|
{I'm|I am} not sure where {you are|you're} getting your {info|information}, but {good|great} topic.
I needs to spend some time learning {more|much more} or understanding more.
Thanks for {great|wonderful|fantastic|magnificent|excellent} {information|info} I was looking for this {information|info} for my
{Hi|Hello}, i think that i saw you visited my {blog|weblog|website|web site|site} {so|thus} i came to
“return the favor”.{I am|I'm} {trying to|attempting to} find things to {improve|enhance} my {website|site|web site}!I suppose its ok to use {some of|a few of} your i

Feb 29 2008
Feb 29

The sections of this tutorial are as follows:

  • Brief background on ApacheBench and performance of Drupal Anonymous vs Authenticated Users
  • Accessing the cookie containing the Drupal session ID
  • Passing the session cookie to ApacheBench
  • Verifying that the Drupal installation being benchmarked recognizes the cookie and is serving authenticated Pages

    ApacheBench, also known as 'ab', is a command line program bundled with the Apache Web Server that measures the performance of web servers by making HTTP requests to a user-specified URL. ApacheBench displays statistical information, such as the number of requests served per second and the amount of time taken to serve those requests, that is useful for evaluating (benchmarking) and tuning the performance of a webserver. ApacheBench does a decent job of simulating different types and levels of load on a sever.

    Many Drupal websites make use of Drupal's core caching functionality, which typically improves performance for anonymous users (users who are not-logged in) by a factor of 10 times, measured in requests served per second. The cache system achieves this performance improvement by storing the results of certain database queries in designated cache tables, allowing Drupal to avoid repeating expensive or frequently run database queries. Drupal does not cache page content for authenticated users because Drupal's permissions system (and node_access restrictions) can show different content depending on a user's role and because some elements of the page are user-specific. Since authenticated users are typically served non-cached pages, requests served to them are more resource intensive and therefore take longer to conduct than requests served to anonymous users.

   By default, ApacheBench requests pages as one or multiple concurrent anonymous users. Benchmarks of performance for anonymous users can be useful for a website that most users will access anonymously, but these data do not accurately describe the performance of websites that serve a significant percentage of their total pages served to authenticated users (as most community sites tend to do). By providing the Drupal session cookie information, ApacheBench can perform benchmarking tests as an authenticated Drupal user.

Accessing the cookie containing the session ID:

Note: These instructions are for the Mozilla Firefox browser.

1)Log into your Drupal installation using the account that you'd like ApacheBench to use when conducting its tests.

2)Open the Firefox preferences menu and browse to the 'Privacy' Tab

3)Click the 'Show Cookies' button

4)Browse the list of sites to find the site that you have just logged into.

5)Highlight the cookie associated with your site that has a 'Cookie Name' value beginning with 'SESS'. There should be only one of these cookies. If you have two cookies with names beginning with 'SESS' for the same Drupal website, you should clear both of these cookies (which will log you out of your Drupal site) and log back in to ensure that you are specifying the correct cookie in ApacheBench.

6)You'll see that your site's SESS cookie has both 'Name' and 'Content' values. You're going to copy each of these values individually and pass them inside of a command line parameter to ab. Note: Do not log out of your Drupal site before testing with ab, as doing so will invalidate the cookie information you have just copied!

Passing the session cookie to ApacheBench

   The 'C' parameter is used with ApacheBench to specify a cookie. This parameter is case sensitive, and should not be confused with 'c' (lowercase) which specifies the number of concurrent requests ApacheBench will make. You'll describe your site's cookie using the following format:


There is no space between the equals sign. Below is a completed example specifying that 400 requests will be made (-n 400) with 10 simultaneous requests (-c 10):

ab -n 400 -c 10 -C 'SESS5a2cb35dfce80bc682796cc451440ac0=d45b85c5be4c651ea2ad520947082e04' http://example.com/

Verifying that Drupal accepts the cookie and is serving pages to an authenticated user

    It's important to verify that Drupal recognizes and accepts the cookie that corresponds to your session. Once, while performing a set of ab authenticated Drupal user benchmarks, I adjusted a Drupal cache setting that increased the number of requests served per second for authenticated users from 5 to 60. I thought, “Wow! I've made an enormous improvement!” What I had actually done was switched from Drupal core session handling to Memcahe's session handling, which caused Drupal to not recognize my cookie. Drupal was actually serving anonymous pages, which accounted for the dramatic “performance improvement.” To avoid misunderstandings like this one, we'll use ApacheBench's verbose mode to confirm that Drupal is serving authenticated pages.

   In our theme directory, we'll add a PHP snippet to page.tpl.php below the HEAD tag, that will print, inside an HTML comment tag, the username and uid (user id) of the user currently viewing the page.

<?php global $user; print 'This is user: '. $user-??>name .' : UID:'. $user->uid; ?>

Then, we'll run ApacheBench with a verbosity level of 4 (parameter -v4), allowing us to view the HTTP response code and html that the server is sending to ab.

    Viewing the content of pages served to ab is also useful in determining the nature of failed responses and investigating suspiciously high numbers of requests served per second. Apache identifies certain types of failed requests by serving them with an unsuccessful (non-200) HTTP response code. (For more about HTTP response codes\status codes, see the W3's "Status Code Definitions" .) ApacheBench recognizes HTTP response codes and reports the number of responses that are marked as successful and unsuccessful by Apache. A reportedly high number of requests successfully served per second can indicate good server performance, but it's important to distinguish pages served with a 200 response code from those that are truly served successfully. In some situations, Apache is unaware of serious problems that occur in other parts of the server stack and will serve pages with an HTTP response code of 200 (successful) despite the fact these pages contain only a PHP fatal error. In these cases, Apache is “successfully” serving the PHP fatal error message.

    Drupal correctly specifies non-200 HTTP response codes when it is unable to connect to the database. Viewing the content of the benchmarked page allows the person performing the testing to view the specific error message that the site is displaying. This is useful in identifying that the problem is, for example, that the MySQL max_user_connections value has been reached, as opposed to other database connection errors, such as an Access Denied error.

It's worth noting that while ab provides useful information about a server's performance, that it does not predict exactly how a server will perform under load from actual users.

Questions? Comments? Corrections? I'm happy to hear from you.

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