The Drupal FormAPI Rapid Development Quickstart

This article is based on a presentation I gave to help a friend who is new to FormsAPI.  It will introduce you to a few concepts using Drupal's command line tools.
 
For the better part of the past year I have been working with Drupal's FormAPI rather intensely.  The projects have now spanned a number of different applications.  Some interacting with the user system, some with sessions and others with the database.
 
FormAPI can be notoriously cryptic for developers new to Drupal but once mastered they are a dream to work with.  
 
Surfing online documentation was difficult for mastring this.  Much of my knowledge did not click until I read Pro Drupal Development published by Apress.
 
Today's Project
 
We need to build a simple webform using Drupal's FormsAPI. It will take an action based on the user's selection and send them to another page when complete.
 
It could be done with webform or CCK, but we want to do this faster than with those tools... in code!  
 
Our Assumptions
 
I prefer working at the command line, so that's all we're going to cover here.  I'm assuming you're at your shell now and that Drush is installed.
 
Let's Prototype Faster
 
Make sure you're working in a site that isn't live... your scripting could bring the site down otherwise.
 
Then get over to your website's filesystem and begin:
drush dl module_builder
drush pm-enable module_builder
drush mbdl
Woah! What just happened?  You just downloaded and installed the module builder and then ran it from Drush to get it to fetch all the known hooks that you can tap into with your programming.  The command "mbdl" is an extention to Drush that came with module_builder.
 
Before we begin coding the module we should know what hooks we want to use/modify with the Drupal framework.
drush hooks
You might want to use more or less to scroll through that list if it is too big:
drush hooks|less
If you are new to this you probably don't have a clue of what you need.  I'll give you a list of starter options in a second, but first let's see what happens when we choose just one of the hooks:
drush mb my_custom_module menu
You just asked module builder to prototype a new module called my_custom_module and to add Drupal's init hook.  Init runs on every page so if you need to do some processing on every single page this is the way to do it.
 
The result should quickly scroll onto your screen:
Proposed my_custom_module.module:
<?php
// $Id$

/**
 * @file my_custom_module.module
 * TODO: Enter file description here.
 */

/**
 * Implementation of hook_init().
 */

function my_custom_module_init() {
}
 
That should be enough to get you doing Drupal-style code rather than putting random PHP everywhere.  
 
Usually I also need Drupal's "menu" hook, it notifies the system of any pages I am creating.  It isn't necessary for what we are doing but I plan on extending the module later so I put it in anyway. I also like to include the "perm" hook and add my own permissions settings.  
 
We're ready to build the prototype now.
drush mb my_custom_module init menu perm --write
Note that last parameter. It creates the module folder in (probably) your sites/all/modules folder, complete with a pre-crafted .module and .info file.
 
I commented out menu in the my_custom_module.module file until I have a page function ready to go.  I use init for some debugging on occasion so I'm keeping that in the mix.  Permissions will be rolled out later but we already know who needs access so we put the control into the module early.  We will implement the settings function at a later date.
function my_custom_module_perm() {
  return array('manage my custom module stuff');
}
This adds an entry to our permissions page.  Note: we are not implementing anything that refers on it in this article.
 
We're ready to create a form now.
 
First create some functions.  As of this writing module_builder doesn't seem to have any draft FormAPI forms for us to use and the form hook has the wrong parameters (they apply to nodes, I just want a form!).
 
function my_custom_module_form($form_state) {
  return $form;
}

function my_custom_module_form_validate($form, &$form_state) {
}

function my_custom_module_form_submit($form, &$form_state) {
}
That should be enough to get us started.
 
Now we just need to put something into each function to make our form. I have consulted the Drupal FormAPI Reference to learn that "radios" are the type of form field I want.  That page also lists a bunch of options that apply to the "radios" type. 
 
Let's define the form function with a single field with two radio buttons:
 
function my_custom_module_form($form_state) {
  $form['choices'] = array(
    '#title' => t('Choose the item that is right for you'),
    '#type' => 'radios',
    '#options' => array('1' => 'Product 1', '2' => 'Product 2'),
    '#required' => TRUE,
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => 'Seriously submit!',
  );

  return $form;
}
 
That "#options" parameter contains all of the options that will make up our radio button list.  When processing, selecting "Product 1" will appear to us as simply "1" on our end to save us some typing in the processing stages.
 
I left the validation function blank in my rapid development project.  I usually use it with multi-page forms where I need to store data between pages.  It is also a great place to do error checking.  Is that phone number numeric?  Yeah, you'd check that here.
 
The submit function has a few neat tricks in my final version:
 
function my_custom_module_form_submit($form, &$form_state) {
  if($form_state['values']['choices'] == '1') {
    uc_cart_add_item('5713', 1, NULL, NULL, FALSE);
  }
  if($form_state['values']['choices'] == '2') {
    uc_cart_add_item('5686', 1, NULL, NULL, FALSE);
  }

  $form_state['redirect'] = 'cart/checkout';
}
You Übercart junkies will appreciate this one.  If you picked "Product 1" you will get the site's product (nid) #5713 added to your shopping cart.  It adds 1 item without any messaging.
 
If you pick "Product 2" you will get item #5686 added to your cart.
 
Those product numbers are the node ID (nid) for each product.  If you aren't sure number to use to add to your cart, go find the product on the site and click edit.  See the number in the URL?  That's the node id.
 
Once it's in your cart the form sends you to the checkout page by setting the form redirect.    
 
Last Step - Use the Form
 
You have to put the form somewhere.  For this demonstration I used a "page" template and changed the input format to PHP to run it.  You could also put it in a block using the PHP input filter, or alternatively just add an entry in the "menu" function we talked about earlier.
<?php
print drupal_get_form('my_custom_module_form');
?>

 

Notes from Drupal Camp Victoria

I finally made it out to a real Drupal event!  Not just a local meetup... so much more than that.

The two-day educational networking event took place at North Studio - a Victoria web development firm which also does a lot of work in the computer training department.  Our schedule started with a focus on the basics: installation of Drupal, SEO techniques, a dash of Information Architecture and more.  That was all really valuable stuff, but for me it was a bit redundant.  If you've been following my threads for the past year you will probably understand what I mean.

What I Learned

The presentation on Actions, Triggers and Rules by Dave Tarc was insightful.  I have worked with Actions and Triggers on Drupal 6 and the Workflow-NG module for Drupal 5.  Rules takes over where Workflow-NG left off.  Essentially this module just lets you add some rudimentary logic to your Triggers so you can automate things on the site.  What could be better than automating things?  In my world: not much.

Learning about Panels3 was a valuable experience. I support sites using Panels3 but I haven't had the need to get much further than that.  The presenter showed us how to use the mini-panel component with the Tabs Panel Style module to produce a nice AJAX tabbed block, the kind that newspapers and magazines like to use to add contextual content to pages.  That has already worked it's way into one of my projects.

The Geo module with OpenLayers presentation blew my mind.  Want to map out things in three dimensional space?  Geo can get you there.  Want to use a map that behaves like Google's Map service but without the branding?  Geo can get you there too.  There are so many base maps to choose from it is really quite amazing at what can be done with Drupal, a custom MySQL configuration and a little know-how (PS, you may not need the MySQL customization if you rely on WKT - it does not allow you to do as much though).  This one will be my next pet project for sure.  Just too cool not to find a reason to do it

Giving Back

My take away from Drupal Camp was that people should give back to the community.  What that means in practice for me is that I should contribute documentation and code where I can. For a time I thought doing some occasional pro-bono (ie, free) work for people was another route to go... I have since moved on.  That often takes an immense amount of time and is not necessarily as valuable as supporting the Drupal community directly.

I have agreement with one of my clients to publish a module I recently upgraded from D5 to D6 and I am hoping to contribute themes in the near future.  Hopefully that will assist others with their projects.  Be sure to subscribe to my RSS feed if you want to keep up on my Drupal code and documentation efforts.  Thanks!

 

A New Desk in Gastown

Great news today.  I have an office!

Since November 2008 I have been working with Geist Magazine helping to build out new functionality on the website and relaunch the site after upgrading it from Drupal 5 to Drupal 6.  For nearly all of this work I have been coding from my home in East Van and/or nearby cafés.  I love cafés, a lot, but after awhile you just feel like you need your own space.  So here I am.

Geist occupies a shared office space with other arts groups at 341 Water Street.  There is at least one book publisher, a performing arts group and others.  My desk fits into the web corner, bounded by archives of the magazine and a window from which you can see the sun adorn the red brick of the building next door.

Managing all of your images with Drupal

Are you tired of resizing your images, saving them, uploading them, only to find out that you need to do the whole process over again because you forgot to size the images correctly?

That was editing for the web a decade ago but it doesn't have to be that way any longer.  With Drupal you can manage your images online and this tutorial will show you one way that it can be done.

This howto is based on a Surrey Drupal User's group discussion where Ryan Dempsey talked about his favourite recipe for managing images.  This howto extends that discussion with the addition of some additional "nice to have" modules.

The good news? You can do most anything you need/want to do to images with Drupal.  The bad news?  Some fringe coding might be necessary to make it totally awesome. 

Drupal can make your website fully capable of managing images in a sane and logical way so we don't have to manually resize our images ever again.

Using Core Components aka. The Old Way

Out of the box Drupal supports images by way of the core upload module.  It is not pretty, users must first upload the image by attaching it to a posting.  After the upload the user must then take the URL it creates  and paste it into the HTML code within the body or, if using a WYSIWYG editor, into an image attach dialog box (if you're looking for one, I prefer FCK).  For a web editor this process is familiar and approachable but to end users it is a nightmare.

Also consider that if you go this route your users will probably have to resize and upload their images as web-ready graphics this may not be the best method: it requires knowledge of Photoshop or the Gimp to do most of the handy work. 

At the end of this guide you will have two methods that will allow you to manage images, using a WYSIWYG editor and using a CCK field.  Our goal is to manage all of the images through a centralized interface.  If you are uploading an image using the core upload module, or a CCK ImageField your uploads are categorized in the system based on the node.  That is extremely annoying when users want to re-use imagery on the site.  Under the "upload" paradigm a user might try to re-upload the file each time.  What a hassle!

A Central Gallery

We want to have everything go into a central image gallery.  Once an image is uploaded it can be re-used with other postings.  We no longer have to worry about accidentally deleting an old posting and having our images disappear.  You can customize where users can put things, permission agains what they can access and prevent users from deleting (just in case!).  That is all really great stuff.

Keep in mind that different users are still going to have different needs.  Some need to edit in WYSIWYG editors, while in other circumstances you simply want someone to choose an image from a pre-set location.  Both of these options are possible.

The Image Management Modules

This recipe should cover everything under the sun - so long as you want to keep everything in a centralized gallery.  That is my preferred route because I know a lot of my clients travel a lot and/or wipe their laptops clean every few months.  They don't want their images on their hard drive.  They want them online.  

The idea is that once you put something into the website it should be available for use... always.  From anywhere. 

Here are the modules you will need:

  • ImageCache - To manage the resizing, cropping and other basic tasks.
  • ImageCache Actions - An expansion pack which allows you to mod the images at display time. 
  • Image Resize Filter - Make your WYSIWYG editing awesome(r) no matter which one you use
  • IMCE - A gallery to store everything
  • IMCE CCK Image - A module which allows you to select an image in a CCK field

This probably goes without saying these days, but you will probably also want:

 The Image Gallery Component - IMCE

To get a gallery users can browse through and upload their images into we turn to the IMCE module.  Enabling it will give you a new tab on your "/user" page (right after logging in).  It will also pop up the gallery whenever requested.  Users can upload any time they visit that screen if you have given them permission to do so.

When you configure the settings you will need to ensure that where your uploads are going on the server has appropriate permissions.  This can be frustrating, but keep this in mind, you will have the same issue when you get to ImageCache below. 

I highly recommend setting a maximum image size.  I would like to make a note that with some work on the CCK IMCE Imagefield module we talk about later this requirement will go away so long as you have the disk space available.

I disable resizing at this point and I do not create thumbnails at this stage either - there are better ways of doing both of those things.  Keep reading.

One note about IMCE before we get any further.  It produces a listing of images, not a thumbnail gallery as I was hoping for.  I would imagine this is possible with some clever hacking, but I'll leave that task for someone else to conquer.  It would be nice to have...

Using the images within a WYSIWYG editor

This is one of those steps that "should" be easy but in practice can take some time to configure correctly.  Since I use the FCKeditor module most of the time (and not the Wysiwyg API) this is a little more complex than I expected.  If both FCKeditor and IMCE are installed you should see the option to integrate IMCE with FCK on the IMCE settings page.  Then after doing that, you will want to go to the FCK settings page and set the default file browser to IMCE.  

If you use the Wysiwyig API, you will want to get the IMCE bridge module.  Make sure after enabling IMCE that you go into your toolbar settings and add the IMCE image attach field.  Otherwise you cannot browse the server for images (except, that sometimes you can by editing a file after drafting it... weird).

To test your configuration you will need to completely clear the cache on your desktop machine because the editor(s) often load the settings in JavaScript which is usually archived for a long while on most desktops.  If you have Firefox web developer toolbar installed I recommend clicking "miscellaneous -> clear private data -> caches".  After doing so, you should be able to click the "insert image" icon in FCK (or your favourite editor) and see a new "browse" button next to the URL field when you click it.  

If you let people insert images this way they will eventually want to resize the images.  When working within FCK Editor I think the best method is to let users use the tools most obvious... and I believe that is when the FCK image dialog box pops up.  They see the controls, they use them, and they get results.  Makes sense right?

The Image Resize Filter does the rest.  It searches the body text created by FCK (or any other another editor) to see if the user changed the image dimensions while they were editing.  If your users changed the settings it will automatically create a version of the file with those new dimensions.  What a delight.

Using the images as a CCK field

This part of the equation is still being built but it is one of the most interesting extensions of the IMCE module that I have yet to happen upon.  The IMCE Image Field CCK module allows you to insert an image selector on any existing content type. 

That will let you define places on a page where images must go.  Users can't disrupt the order of things with the WYSIWYG editor so they can get down to business blogging (or whatever it is they do).

After the user selects the image the field allows the user to see the image (which currently renders at full size - ouch) and also type in a caption.  We can turn the caption on or off when you render the images.  That setting is managed by ImageCache.

Formatting images using ImageCache

At display time you can have image presets that render.  Essentially all you need to do is set some presets in your ImageCache settings and then choose places where you want the image to display at that size.  You can specify the size for CCK fields defined in your content type pages.  If you are using views, just choose the one you want from the options provided for each image field.

Images in your preset sizes will not generate immediately.  They are generated as they are needed.  Every time thereafter it will be cached.

Slicing and Dicing the Photos

You can go a step further with image processing on Drupal.  Using ImageCache Actions you can overlay text onto the image, put custom borders on, and even watermark the images.  If you want a sequel to this blog posting that talks about ImageCache options send me an email.  It took a long time to get this posting out the door so it helps if people know it is useful.

A Quick Final Touch

While you're working with Drupal, don't forget to consider using Lightbox as a quick route to making galleries by attaching multiple images to a page.