Enhancing the Anonymous User Experience: Adventures with Flags
Part I: The Case Study
At Promet Source, I was approached with the following User Story in need of a technical solution:
As an anonymous user, I can select a location of my choice and complete forms on the site that will send requests to representatives of that selected location throughout my visit and filter all of my search results according to my location selection.
The technical challenge of this user story was to provide a light-weight contextual experience for the anonymous user without duplicating functionality. Almost out-of-the-box Drupal can provide such an experience for authenticated users. But to do the same for the anonymous user would take some creativity and stretch Drupal beyond its comfort zone.
We ruled out the obvious almost immediately. Sub-domains would not be an adequate solution since the content would not be location-specific but needed merely to be ordered or reacted to based on a selection. Organic Groups was also not a candidate for the same reason. Locations did not need to be a group with a subset of data but a context for ordering and reacting to data. We needed an unobtrusive strategy for reacting to a user's selection that would neither create technical debt via custom code nor off-load the responsibility to a content editor.
The Flag module was an appealing candidate because it provides integration with:
- Views via Relationships.
Sessions API, opening its functionality available to the anonymous user.
In its simplest form, Flags allow users to set a boolean value on an entity. A node, a taxonomy, a user, can be set as a thing ("spam", for example, or a "favorite"). Such a selection is what this user story demanded. We needed Drupal to be able to ask "What location has the user selected?" and return that location. If we allowed users to "flag" a location, we could then respond to it throughout their visit.
Because of Flag's integration with Views, Drupal could use the location selection to sort the search results without requiring the user to set this search parameter with every filter. Moreover, since Rules could take a flag as an argument, Entity Form submissions could be processed by first checking the location they had selected, then fetching the corresponding representatives from that location and sending the submission to the appropriate individuals.
Flag was never intended to be leveraged in this way. It is a tool originally intended to help moderators allowing users to "flag" comments, content, or users as abusive. Soon implementations extended to provide users a means to select and order content.
Our implementation necessitated that users only flagged one entity at any given time. The flag was mostly a function on the backend: something that the user would not necessarily be conscious that they were doing. Implementing Flag as a solution to this particular use case was treading into new territory.
Despite the unforeseeable challenges, the possibilities to extend the anonymous user experience had applications far beyond this project alone and was therefore worth pursuing.
Here's how we did it:
Step One: Install Flag, Rules, Views, Session API, and Entityforms.
$drush dl -y flag rules views entityform session_api
$drush en -y flag rules views entityform session_api
Step Two: Create a Location Vocabulary.
Add values to set the contact information and other recipient data to each location. Add each location as a Location term. We wanted to import the location information with every build so we added a feed for this taxonomy so that the data was easily editable by the client.
Just your basic site-building here.
Step Three: Create the Flag on the Location Vocabulary.
As the Flag module configuration helpfully reminds us, the Session API is needed so that we can make this flag available to the anonymous user, as we are want to do.
Step Four: Create a Rule so that only one location can be selected at a time.
We do not want users to be able to choose multiple locations. If the user selects one location and then selected another, the first selection must be unflagged first otherwise both locations would be notified of a submission and the search sort could reflect a prior choice rather than the current preference.
This Rule fires in the event that a user flags a location. It loops through the current list of flags on the user and unflags them first before flagging the just-selected flag.
Step Five: Add the Flag as a relationship to the view and sort by it.
We want all of the lists of content on the site to be ordered according to the locations to which they pertain. To do this we:
Add the Flag as a relationship:
Add the Flag to the Sort Criteria:
Step Six: Create a jump menu of the flags.
Now we need to offer a means by which the user can select a location and, in so doing, flag it. To do this we created a jump menu that listed all Location terms that linked to their flag link.*
*In order to use the flag on a term as a link, Flag needed to be patched. See "Credits" below.
Step Seven: Get location from Rule.
Now we need to react to the locations selected when a user completes a form. We need to send the submitted form to the appropriate personnel at the location the user selected. To do this, we need to fetch the taxonomy term flagged by the user:
Having fetched the location, we can get the value of any field from the entity and send an email to that value.
With the User Story reliably in place, the site launched.
We ran into a number of issues with Flags that Allan addressed by contributing back the following patches:
In Part II, we will consider how such an approach could be used to further enhance to Anonymous user experience and perhaps address the contextual problem Dries brought to attention at DrupalCon Portland.