Skip to content

Handling Application Recommendation Forms

Working in higher education, one of the projects which has been a recurring pain-point has been form and application workflow – specifically, leveraging multiple forms with aggregated data. Until recently, Drupal hasn’t exactly handled this with grace – webform and content types only go so far to providing the optimal user experience and administrative ease-of-use needed for projects.

During a recent project for the SURE program at Georgia Tech, I located and implemented a novel method of creating a three-step form workflow using viewsentityform, uuid, rules, and paragraphs.

Project Overview

The goal of this project was to create a Drupal-based form interface for the SURE program to provide an avenue for current undergraduate students (both at Georgia Tech and other institutions) to be matched with a faculty member for a research project or laboratory.

Our stakeholders and main users for this project include:

  1. Students (who fill out an application and keep check on their status)
  2. Faculty (who select a student to pair themselves with)
  3. Recommenders (who provide a text recommendation for a student)
  4. Administrators (who oversee and administer the pairings)


Before discussing the project’s implementation, it’s very important to highlight the data workflow and modeling for the project.

Screen Shot 2015-12-14 at 9.58.44 AM
The default workflow for applications.

Logically, the process for end-users to have their application marked as complete is as follows:

  1. Completing the SURE Application (an entityform) is the first step to the process (status: complete application).
  2. A completed application is then submitted using a flag on the entityform submission (status: submitted application).
  3. A recommendation (as highlighted in the SURE application) is then sent an email to complete a recommendation, which links back to the original application.
  4. Once the recommender completes their recommendation, the application is marked ‘ready’ for faculty to favorite student applications (status: approved application).

The workhorses for this project, module-wise, are:

  • Rules – Rules are the workhorse for access and actions. Rules will manage access to applications and statuses through the delegation and removal of user-roles. Rules will also manage the workflow by manipulating data into select fields, redirecting users, and sending all application-specific email.
  • UUID – UUIDs are the unique identifier leveraged for recommenders to submit their recommendation. It’s best to describe this in detail later on.
  • Views – Views are just as important as Rules. Views will be providing custom pages and listings for all cases. For students, Views are keeping track of the students’ progress and status for the SURE program. Faculty members select and favorite students using Views. Recommenders use Views to select which application they are submitting to (more on this later – it’s not as scary as it sounds). Administrators, of course, use views to get a 30,000 feet overview of the program status.
  • EntityForm – Entityforms act as the data keepers for all application data. Applications, recommendations, and pairings are made via an entityform entity.
  • Paragraphs – Paragraphs group fields together into logical field objects that can be reused. In this project, paragraphs are used solely for applicants to provide a recommender, in the case that a recommendation needs to be obtained from multiple recommenders.

Step 0 – Prepare for Workflow

To prepare for our multiform workflow, let’s begin by getting some structure in place on Drupal’s end.


Screen Shot 2015-12-14 at 10.19.11 AM

Because roles are crucial for the execution of the project, these should be built as placeholders for access to views, rules, entityforms, and so on.

For this project, roles were created for:

  • No Application – As the name implies, this role is delegated to all logged in users who have not created an application.
  • Application Created – A logged in user of ‘No Application’ which has completed (but not yet submitted) an application.
  • Application Submitted – A logged in user of ‘Application Created’ which has submitted an application.
  • Application Approved – A logged in user of ‘Application Submitted’ who has had their recommendation complete their recommendation.
  • Application Declined – Manually determined by SURE Administrators.
  • Paired – A logged in user of ‘Application Approved’ who has had been paired with a faculty member.
  • Faculty – A faculty member.


In addition to preparing some user roles, I also need to have a good high-level overview of the project. That’s where, honestly, a whiteboard comes in handy.


In this rough and (admittedly) messy overview, an application is manually submitted by the user, which generates access to a status page and then the recommendations. A recommender fills out the form, which on completion updates that status page, and then prompts the application to be approved and allows faculty to favorite the application.

This is essential both to understanding your data and data interaction needs across the project, as well as having a thorough understanding of how users will interact with the system.

From this example, we can easily see where the main stakeholders have theoretical interactions with the system.

Step 1 – Build the Application

The first step on this project is to build the application. The entityform, named SURE Application, contains all the fields for the application, leveraging fieldgroup to properly segment and organize fields into logical groupings.

Screen Shot 2015-12-14 at 12.29.58 PMPro-tip: Naming your machine-names of each field specific to your content type will make your life a lot easier when naming fields and entity relationships in views and rules. One rule I have in place is to begin naming a new field using the content type (in this case, many of these have sure_app because I pre-empted every field name I created with ‘SURE APP:’ when creating them).
Screen Shot 2015-12-14 at 12.32.22 PM

The most important thing to note on this entityform, we need to ensure it’s only visible to logged in users with ‘SURE: No Application’. We haven’t defined when a user gets that role yet, but that’s okay. Speaking just theoretically to set this up, we only want users who have not created an application to be able to create one.

Step 2 – Login & Application Logic

The next step is to begin to build out some basic rules to answer some crucial questions to The design.

These questions include:

  1. What happens when I log in?
  2. What happens when I register?
  3. What happens when I create (save, but not submit – this means to create an entityform of type ‘SURE Application’) an application?

The first two questions can be answered simultaneously.

Rule: SURE: Login: No Activity

Screen Shot 2015-12-14 at 12.36.55 PM

This rule only applies if the user has no roles (other than the default authenticated user), to which Rules gives the user the rule ‘SURE: No Application’. Now we know every user who creates an account and logs in who has no account will receive this.

Rule: SURE: Account Created

Screen Shot 2015-12-14 at 12.38.39 PM

We also want to point users in the right direction to complete an application. Since we know they do not have an application created (because they are ‘SURE: No Application’, we can forward them over to the application page on login.

We’ll go over where this page redirect takes you shortly.

Rule: SURE: Application Complete

Screen Shot 2015-12-14 at 12.39.46 PM

And now we can build out the workflow for creating an application, which just simply removes the ‘SURE: No Application’ role and add in ‘SURE: Application Created’.

Step 3 – Build Submission & Recommendation Logic

tumblr_nh72raFE0h1s2t3cto1_500The next step is akin to jumping off a cliff, because at this point we’re juggling a few different pieces of logic in one step. Because all of the pieces of logic are interconnected and intertwined, it’s very important that we build and consider them all as one unit.

Let’s go through what needs to happen in this step:

  1. A recommender-specific form needs to be created for the submission of a recommendation.
  2. The recommender email needs to go out to the application field.
  3. The application needs to be submitted (via a flag).

I’ll preface this section with a quick disclaimer – my goal with this section is two fold:

  1. I want it to be as easy as possible for applications to be submitted. I want it to be incredibly evident, transparent, and intuitive that the student is submitting an application and can no longer adjust their application.
  2. I want it to be as easy as possible for recommenders to complete an application. I want the recommender to be able to do it anonymously (without logging in) and yet have a unique token to be able to reference the application. Their goal should be to click a URL, enter the token, complete their application, save/submit the recommendation, and do nothing else. Anything in addition to this just bogs down the process. And for developers and site maintainers, it risks having to hand-hold users through the process.

This should bring up the same perplexing question I had when I thought this logic step through:

How the heck am I going to create a unique token for referencing the application?

Recommendation Token – Enter UUID

While looking through Drupal’s project pages, there aren’t a lot of modules that allow you to generate a serial number as a field value. That was and is my primary goal for this step – to have a genuinely unique identifier that also strips all personal information out of the application.

I don’t want a serial number to be guessed (any value on the node), pre-determined (student provided), or even easy to determine (node IDs). Nor do I want to trust in the random function in token to create a key (hint: it doesn’t work).

This needs to be done first, since so much relies on having this logic in place. So let’s look more intimately at how recommendations are entered into the SURE application.

Screen Shot 2015-12-14 at 12.59.23 PM

The recommendation is a field as a paragraph, which is just a more efficient and cleaner field_collection.

Screen Shot 2015-12-14 at 12.59.52 PM

If we take a look at the paragraph bundle (SURE: Recommendation), it’s pretty straight-forward. We need an email, for example, to contact the recommender for how to submit the recommendation. We need their name to be able to create personalized emails, which is also why job title and employer is included.

(also – notice the field names on this paragraph entity – naming your fields’ machine names correctly is immensely important to your sanity!)

One field that was needed is a hidden field for the token – that’s the SURE_REC_UID. That’s the serial number, named obtusely because when this project began, I had no idea on how to generate that unique ID. There’s no module to create a serial number. We’re about out of ideas.

But wait… do you know the format of a UUID?


UUID generates a string that is 8-4-4-4-12 characters long. Long enough to be incredibly secure, standardized enough to be able to be copied and pasted into a node. UUIDs are similar enough to serial numbers and codes that the prior knowledge required to understand it should be evident to anyone who has ever installed an application.

In addition, it’s long enough to discourage typing the code in, and yet short enough to where the dashed UUID can be referred to easily for verification purposes.

So, how we add this UUID into a field on a paragraph?

Rule: SURE: Application Complete

One note about rules – rules are awful at pinging through an entityreference to examine, manipulate, or even query a field. You can try the ‘Entity: Has Field’ condition and ‘Fetch entity by ID’, but you will not be easily able to grab entity-specific fields.

So let’s treat each entity as its own rule. This rule does nothing but controls the starting point: the submission.

Screen Shot 2015-12-14 at 1.06.59 PM

Since we can’t jump through entities very well, let’s consider that the first rule can only touch the first two elements of the workflow we need to get to the paragraph entity:Screen Shot 2015-12-14 at 1.10.29 PM

With this rule, we can get to the entityform (because that’s where the rule begins) and get to the field, which is just an entityreference to a paragraph entity.

But, we’re unable to get to the actual entity, which houses the recommender’s email address, name, job title, and employer. However, we can trigger a second rule to act that’ll handle that information just by using the flag module.

All these two actions are doing (flag and unflag) are basically flipping a light-switch.


When we flip that switch (and get power), we’re giving an action that will prompt another rule (that we’re about to create) to act. We unflip that switch just because we’re nice people, and to return it to its original state.

Note: For Multiple Recommendations

Note that if you want multiple recommendations, that’s easy too. As I learned from the drupal community, do the following:

  1. Download, install, and enable VBO – Views Bulk Operations.
  2. Create a VBO View that shows all NIDs of paragraph recommendations on your entityform submission.
  3. Revise the rule above to use the VBO View to generate a list of NIDs to loop through, then use the ‘Add loop’ functionality to loop through each node ID and run the same actions above.

Problem solved.

Rule: SURE: Application Complete: Set UUID

Screen Shot 2015-12-14 at 1.07.09 PM

As mentioned above, this rule is the light-switch rule. When it’s flipped, the paragraph item checks that it’s the correct type of Paragraph and that it has a field (and in this case, you could further your sanity by checking that it == ”, or empty).

All we’re doing is saving the entity (which generates the UUID) and then taking that generated UUID and applying it to the data value of the SURE_REC_UID hidden field we set up earlier.

Why didn’t we just use the UUID?

One question that should probably come up at this point is wondering why we didn’t just use the UUID entity value embedded in Drupal? Well, this answer’s got two reasons:

  1. We want to save the UUID as it exists right now. Remember that a UUID is changed with each save, as the saved entity is going to be different and genuinely unique compared to the old value.
  2. UUID is a tough value to do comparisons on in some contributed modules. From my testing, locating UUIDs through Views and Rules was not as consistent as a old-fashioned field. To ensure I could always get to the UUID as long as I could reference the entity, making it a basic field makes the most sense.

Now, we need to look at how the end-user (recommender) will submit their recommendation.

Entityform: Recommendation Submission

Screen Shot 2015-12-14 at 1.28.56 PM

With the recommendation, the primary goal is to make it as easy as possible to submit. The recommendation and recommendation file both are very straight-forward fields.

(again – notice the field names on this entityform entity – naming your fields’ machine names correctly is immensely important to your sanity!)

However, the entity reference is unique in that it’s using a View as a widget instead of the default widgets. Why?

  1. The default widgets for entity reference are not scalable. Select lists and radio buttons go right out the window when we consider 100s of applications, each having a recommender. Funnily enough, even the Drupal Association ran into this same issue, albeit from a performance angle.
  2. The autocomplete fields will attempt to autocomplete a UUID after only a few characters. This is not acceptable from a security standpoint, as the autocomplete should only happen on when the complete UUID is entered, not a partial (IS, not CONTAINS). Drupal does not have any configuration for autocomplete to configure this, so we can throw that out. Like what was discussed before, any logic that gives the power to the end-user to guess or select from multiple applications opens up the application process to confusion, questions, and security issues.

Luckily, there’s an option that allows entityreferences to be custom developed for any need. Enter entity_reference_view_widget.

This module (ERVW) allows you to create a custom view to select an entity based on all of the views-related criteria and logic built in.

The important bit from this is that we can create a view that touches specifically the fields we want using the criteria we want to determine which UUIDs are active.

View: Entity Reference: Recommendation Submission

Screen Shot 2015-12-14 at 1.37.44 PM

The ERVW is a pretty easy beast to wrestle with, as long as you have had experience with the basic References display in Views core.

Looking at the relationships in the view, it’s simply tunneling from the recommendation information (paragraphs item) back to the application, and then ensuring that the application has actually been submitted.

The fields are the important piece to this view. The first field, the ERVW checkbox, is the selector field that determines the entity ID that the view will pass on to the parent referencing entity.

The magic in this view comes not from the views’ fields (which just show the application’s name, the recommender’s name, and the UUID code), but in the filter. As mentioned before, one major drawback of using the vanilla entity reference fields is that the autocomplete field begins to provide suggestions after typing the first character of the UUID code. The view, on the other hand, can be made to create an exposed filter that is both equal to (not contains or partial) on the UUID field and required. This means that no UUID will be shown until a user adds the UUID into the exposed filter, and that any partial UUID or character will not bring up any results.

With a little bit of CSS, this view can be made to look as foolproof as possible.

Screen Shot 2015-12-14 at 9.04.08 PM

Screen Shot 2015-12-14 at 9.04.22 PM

That covers the process for submitting a recommendation. The email, for those familiar with Rules, is much less chaotic. And that ties in gracefully with the submission logic.

Where We Are Now

At this point, we’ve got an application (entityform) and recommendation (entityform), connected to each other via the entityreference in the recommendation with the code.

All that’s left is to handle the finalized submission (a flag), which allows a user to flag a submission as done.

Screen Shot 2015-12-17 at 2.03.09 PM

This flag, showing up as a field on a view, just checks the entityform type and performs a simple sanity check (that the user submitting == the user who created the form), and then does the following as a flag on the initial SURE application:

  • Removes the SURE: Application Complete role (and any other SURE roles that may exist, as a sanity check).
  • Adds the SURE: Application Submitted role.
  • Flags the recommendation on the application (paragraph item).
  • Redirects the user to a panel containing multiple views showing the status of application step(s).

Note: For Multiple Recommendations

Note that if you want multiple recommendations, that’s easy too. As I learned from the drupal community, do the following:

  1. Download, install, and enable VBO – Views Bulk Operations.
  2. Create a VBO View that shows all NIDs of paragraph recommendations on your entityform submission.
  3. Revise the rule above to use the VBO View to generate a list of NIDs to loop through, then use the ‘Add loop’ functionality to loop through each node ID and run the same actions above.

Problem solved.

Wrap Up

The above method provides a neatly packaged and delivered content to deliver a foolproof solution to recommendations.

Please post any comments, questions, or suggestions below and I’d be happy to talk about this project further!

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *