Category Archives: drupal

Setting up your Drupal development environment

People found the 7-minute impromptu braindump I gave at last night’s Drupal Toronto meetup useful enough to vote it the best talk, so I thought I’d write it up. =)

Development

  • Use source code control. No, seriously. Use it. Religiously. It’s going to save you someday. CVS is popular. Subversion is also easy to set up. It’s a must when you’re working with other people, but even if you’re on your own, it’s really handy to be able to go back to previous versions or to check your changes.
  • Get to know your integrated development environment (IDE). For example, I use Eclipse + PHP Development Toolkit (PDT) + Xdebug, and the keyboard shortcuts save me so much time and make it easier for me to think about my code instead of thinking about editing my code. F3 to jump to a function definition, Ctrl-Shift-R to open a file by typing part of the filename, Ctrl-space to complete text… I’ve also gotten XDebug integration to work, so I can step through code and examine variables without putting var_dump everywhere. Know your tools.
  • Add the XDebug extension to your PHP installation. Even if you don’t get XDebug integrated with Eclipse, you’ll like the new and improved var_dump and other debugging functions. I’ve heard good things about Zend Debugger, too.
  • Put your entire Drupal directory into your project in the IDE, not just your site-specific code. You can even check your entire Drupal directory into your source code control repository. This makes it easier to look up functions in Drupal core, not just your sites/all/modules directory. You _could_ version-control just your site-specific code (profiles/, sites/, etc.), but set-up becomes a little more complicated.
  • Use an installation profile to manage your configuration. Experiment with things using the administration interface, then create/update an installation profile that sets up a brand-new Drupal installation. This will save you lots of time and head-scratching when you need to reproduce your setup.
  • Make starting from scratch easier, and do it often. I have a Makefile that drops my database and recreates it, resets my settings.php, and even opens up a Firefox browser that uses my installation profile to set everything up from scratch. Drupal 5: Copy your settings.php to settings-default.php or something like that before you install your system, then copy settings-default.php over settings.php when you want to refresh your setup. Drupal 6: Delete settings.php when you want to refresh your setup.

Firefox

  • Use multiple profiles. You can start completely independent Firefox processes by using profiles. For example, you could have your default profile with all the bells and whistles, and a test profile with just FireBug. You can log in as different users, too. Use this command to start a new Firefox process: firefox -ProfileManager -no-remote
  • Install and use FireBug. Best web development extension ever. I use this to examine the document object model, debug my CSS rules, experiment with my CSS and HTML on the fly, and monitor the network requests.
  • Set up quick searches for drupal.org and php.net. You can define quick searches by creating bookmarks that use keywords and %s placeholders in Location:. Example: Do a quick Google-search of Drupal.org by adding a bookmark with Location: http://google.com/search?q=inurl:drupal.org+%s and Keyword: gd (short for Google Drupal). To use your new quick search, type Ctrl-L or Alt-D to get to the address bar, then type gd keywords. Set up ways to search php.net and drupal.org/project, too. =)

Multi-site / virtual hosts

  • Simplify switching between your computer and your remote server. If you’ve set up your local system for virtual hosts and you have a remote server for testing, you’ll want to be able to switch between testing locally and testing remotely. Set up your local system to respond to all the domain names for the site (ex: example.com, foo.example.com, bar.example.com) and a local subdomain (ex: local.example.com). Set up your remote server to respond to all the domain names for the site (example.com, foo.example.com, bar.example.com) and a remote subdomain (ex: test.example.com). Then set up your /etc/hosts (c:\windows\system32\drivers\etc\hosts on Microsoft Windows) to look like this:

    127.0.0.1 localhost local.example.com
    remote.server.ip.address test.example.com
    
    127.0.0.1 example.com foo.example.com bar.example.com
    #remote.server.ip.address example.com foo.example.com bar.example.com
    

    local.example.com will always resolve to your computer, test.example.com will always resolve to the other computer, and everything else depends on which line is active. # comments out a line. When you want to switch to testing on the remote server, add # to the beginning of the line for 127.0.0.1 example.com… and remove # from the line for your remote server. If you want to automate this even further, write a script that swaps different /etc/hosts files around.

Hope that helps! I’d love to find out what you’ve done to tweak your environment. Feel free to share your tips here!

Programmatically creating CCK nodes in PHP (interaction with the Path module)

I’ve been experimenting with the Drupal Content Construction Kit, which promised to be a more flexible way to deal with custom node types in the Drupal content management system. However, I’m a finicky sort of person who likes being able to reproduce the setup from a fresh start, and CCK isn’t well-known for that. In fact, CCK nodes aren’t well-known for being easy to move from one server to another.

Following the instructions for programmatically creating, inserting, and updating CCK nodes will get you most of the way there. Here are some of the gotchas you’ll want to watch out for.

I was using Path to define aliases for my nodes and the path aliases were just not getting created. After lots and lots of tracing with XDebug, I found out that it was checking for user_access, but no user was active during the installation process. Solution: create the admin user and load it into a global $user variable.

I also spent a fair bit of time being very annoyed with CCK and creating nodes programmatically in my install profile. Turns out that you first need to use install_add_content_type to create the base node type, _then_ follow the instructions to drupal_execute the form that adds custom fields.

Here’s a quick sketch of the code:

function profilename_profile_final() {
  // lots of other code here
  install_add_user('admin', 'admin', '[email protected]', array(), 1);
  global $user;
  $user = user_load(array('uid' => 1));
  install_add_content_type(array(
    // take this from Install Profile Wizard
  ));
  node_get_types('types', NULL, TRUE);  // flush cache
  $content = array();
  $content['type'] = array(
   // take this from content copy export
  );
  $content['fields'] = array(
   // take this from content copy export
  );
    _install_create_content($content);    
}
function _install_create_content($content) {
  global $_install_macro;
  $type_name = $content['type']['type'];
  $_install_macro[$type_name] = $content;
  include_once drupal_get_path('module', 'node') .'/content_types.inc';
  include_once drupal_get_path('module', 'content') .'/content_admin.inc';
  $macro = 'global $_install_macro; $content = $_install_macro['. $type_name .'];';
  drupal_execute('content_copy_import_form', array('type_name' => $type_name, 'macro' => $macro));
  content_clear_type_cache();
}

Now I feel more confident about CCK…

Hooray, figured out Drupal 5 multi-step registration form with validation and invites

The validation of multi-step forms in Drupal 5 was much less scary than I thought it would be, or maybe I lucked out by basing my code on something that already worked. =)

I had spent last Thursday and Friday searching the Internet for people’s blog posts about the topic. I came across exact descriptions of the problems I encountered, but no solutions–only notes about critical bugs in Drupal 5. Asking the developers in #drupal didn’t yield any tips, either. They told me to get ready for a world of pain.

Then I remembered that the password-reset module used a multi-step form, and validation actually worked. I had already incorporated part of the source code into one of my modules, and it was easy to extend that example. I defined a new multi-step form in my PHP code as a replacement for the system form in user/register. There were some complications around the fact that other modules modify the behavior of user_register, but I got the code to validate and submit the data. And it worked!

No code snippet, sorry. Too many interactions for me to explain neatly. Some hints:

  • You can use logic to set the value of $step.
  • Pass information to the next step using hidden fields.
  • Make sure the step field is included in your form template. Watch out for this if you’ve got a form template that specifies all the fields explicitly.
  • Captcha uses hook_form_alter to add a captcha based on the form_id. It doesn’t know about steps. You’ll need your own hook_form_alter to unset the captcha if you don’t need it on the other steps. Make sure your module runs after captcha by changing the weights in the system table.

So now I have a multi-step form that can skip step 1 (employee verification) if a valid invite code is provided, asks for CAPTCHA once, and does all the validation and user registration magic. Hooray!

Configuration management with Drupal – Multiple developers, live site

We’ve moved into the next phase of our development. External users now have access to our testing server, and theylle be busy not only filing bugs but also creating editorial content. This means that we can’t just drop the database and recreate everything from scratch. (Darn!) However, we still need a way to make sure that:

  • developers have a consistent environment for development and testing, so all changes need to be checked into the Subversion source code control system
  • all significant changes are captured so that we can deploy the tested system on the production server

Here’s our current plan:

  • Editorial changes (creating new nodes, editing existing nodes) happen on the testing server. Developers can test their local systems with real data by downloading a data dump from the testing server and loading it locally.
  • Code changes that don’t require database updates can be pushed to the testing server after local testing.
  • Code changes that require database changes or one-time administrative actions can be handled in modules’ .install files as modulename_update_somenumber() and _install() functions. This can be tested locally by loading a data dump from the testing server and then running update.php. It should also be tested from a fresh install. If the change works locally, then the testing server can be updated. (Make sure to run update.php on the testing server as well).

When we deploy the system to production, we’ll review all the content, remove the test data, and push the data and the code to the live site.

See http://heyrocker.com/drupal/content/deployment-and-change-management-problem for more analysis.

Drupal: Testing multisite/domain-access Drupal locally and on a testing server

We’re using Domain Access to manage a number of related subdomains on a single server. I prefer to develop and test locallly, then push my changes out to a testing server so that other people can try the system. I use the same domain names on my computer and on the test server so that I don’t have to make any changes in the database. To switch between them, I comment or uncomment a line like

127.0.0.1 example.com sub1.example.com sub2.example.com

in my /etc/hosts file.

However, Firefox caches the domain name information, and it can be confusing to figure out which server I’m on. The following setup makes this much easier:

  1. Install Domain Details to show the IP address of the server in your status bar. Good for a quick check – am I on 127.0.0.1 or elsewhere? (NOTE: I started with ShowIP, but it looks like ShowIP caches the information.)
  2. Install the Clear Cache Button extension.
  3. Restart your browser and customize your toolbar. Add the Clear Cache button. Click on this before switching sites.

In an ideal world, I’ll have a plugin that automatically twiddles my DNS entries, too. =)

Drupal: Adding a footer to all of your system e-mail

Drupal’s hook system is making me almost as happy as Emacs’ hook system does. =)

There’s a hook_mail_alter function that allows you to modify any message your system sends. Example:

function mymodule_mail_alter($mailkey, &$to, &$subject, &$body, &$from, &$headers) {
   $body .= "\n\n" . t('This is a system-generated email, please do not reply to this message');
}

You gotta love developers who plan for extensibility and put all sorts of hooks into the code…