Drupal 7 "Sample Request" feature using Flag, Views, Webform, and a few hooks

Today @welly in #drupal-support asked about how to have Druapl 7 "users to be able to flag a bunch of nodes and then submit their list of flagged nodes to an admin user some how."

I just happened to do that exact thing for a client project just a few weeks ago.

So here's my approach.

Using Flag (and Session API for anonymous flagging) with Views, Webform, and a few webform hooks, I was able to allow users to flag items for which they could request samples in a webform.

There are four components:

  1. Flagging
  2. Requesting
  3. Unflagging
  4. Rendering

Flagging

The flagging component should be self-explanatory to anyone who has used Flag -- a flag link is provided.  In my case, I provided a link to the Request (Step 2) within the Flag's confirmation message, to allow the user to quickly jump to their request (list) after flagging (or unflagging) any item.

Flag links for anonymous Drupal users

sample request flag contains link in flag notification message

Requesting

I created a webform with a few fields, one of them being a select list.  Webform now supports select list options to be populated from pre built lists.  These are implementations of hook_webform_select_options().  Webform provides a few for US states, etc.

My implementation just used a view.  (See @mconnerton's webform_views_select sandbox for the Views plugin that can provide the options implementation without writing the hook manually.).

Webform select options generated by views-powered callback

Here is the code for this feature:

t("Sample Requests (User Flag list)"), 'options callback' => '_MODULE_user_flag_sample_requests', ); } return $items; } function _MODULE_user_flag_sample_requests() { $view = views_get_view('user_flag_sample_request', TRUE); $view->set_display('webform_options'); $view->execute(); $options = array(); if ($view->result) { $row_class = $view->style_options['row_class']; $attributes = $row_class ? " class='$row_class'" : ''; foreach($view->result as $item) { $options[$item->nid] = '' . render($item->field_field_img[0]['rendered']) . ' ' . $item->field_ref_colorway_node_title . ' (' . $item->node_title . ')' . ''; } } else { $options[0] = t("Nothing"); } return $options; } ?>

Here the hook registers a callback; the callback builds a one-dimensional array with keys of node ids and values of HTML strings including a few of the view fields -- image, title, and in my case the title of the node's "owner" (a node reference field)

Then, after choosing this source of options for my webform component, I have a list of any items which the active user of the site has flagged.

Webform select options, altered a bit.

Now that the flagged items are options in a webform select list, I need to unflag them once the webform is submitted.

Unflagging

To unflag each item requested in a webform submission, I implement a second webform hook -- hook_webform_submission_insert():

data[1]['value'] as $k => $nid) { flag_flag('unflag', 'sample_request', $nid, $user); $flag = flag_get_flag('sample_request') or die('no "sample request" flag defined'); $flag->flag('unflag', $nid, $user); } } ?>

Rendering

And since the submitted webform will hold the values of each flagged node id, I used a third hook to display a little more about the node than just its id -- hook_webform_submission_render_alter():

nid] = l('[' . $node->type . '] ' . $node->title, 'node/' . $node->nid); } } ?>

This prints a text link in both the webform results displayed on the site and returned in the webform submission email(s).

flagged items are rendered as links to their nodes

....That's about it.  Comment with questions or ideas on how you approach a problem like this.

-Steve