I recently had a client call with a weird request – was it possible to have a vCard attached to their Gravity Forms emails, so they can add contacts directly to their phones from the email? I did a little digging and discovered that, oddly enough, there are no extensions for Gravity Forms that do this.
So I decided to tackle it myself. I don’t know if my solution is the most efficient, or the most elegant, but it works really well and was pretty simple to set up.
Step One: Create a form
I’m not going to walk you through this one step-by-step. I created a form in Gravity forms, and made sure to add fields for all of the data I wanted to add to the vCard file. In my case, this meant collecting the user’s full name, company, email address, phone number, and physical address. This is my form:
Step Two: Attaching a file to the Gravity Forms notification email
Gravity Forms has some really good documentation, and the hook needed to modify the notification emails is gform_notification
. Per the gform_notification
documentation examples, I wrote some quick code to attach a file I had uploaded, sample.pdf to outgoing emails.
This is the code that I added to my theme’s functions.php to attach the file:
function add_vcard( $notification, $form, $entry ) { // target admin notification if( $notification['name'] == 'Admin Notification' ) { // get upload root for WordPress $upload = wp_upload_dir(); $upload_path = $upload['basedir']; $attachment = upload_path . '/sample.pdf'; // path to file if ( file_exists( $attachment ) ) { // check if file exists $notification['attachments'] = rgar( $notification, 'attachments', array() ); $notification['attachments'][] = $attachment; // attach file } } // return altered notification object return $notification; } add_filter( 'gform_notification_1', 'add_vcard', 10, 3 ); // only filter submissions for form ID 1
If you’re having trouble following along, essentially this code defines a function called add_vcard
(), to which I passed the $notification
, $form
, and $entry
objects. In the code, I target the notification titled “Admin Notification“. I then get the file I uploaded from the wp-uploads directory and attach it to the $notification
object. Finally, when I apply the filter I make sure to only apply it to the form with an ID of 1 using gform_notification_1
.
Note: Changing the number at the end of the gform_notification
hook targets the form of the corresponding ID. So if you wanted to target form ID 4, you’d need to use gform_notification_4
.
Step Three: Generate the vCard
This was the most challenging part. I honestly had no idea how to tackle it, so I had to do some digging. I eventually stumbled across this vCard PHP Library, which looked like exactly what I needed. So I decided to move forward with it and integrate it into my solution.
Now this is where I’m embarrassed to admit that I don’t know what Composer is, nor do I know how to use it. So I downloaded these files and included them manually. Someone who is better at coding than I am could probably do this much easier.
I downloaded the files and added them to my theme in the lib/vcard/ directory. I then included them in my functions.php, before my add_vcard()
function, like so:
// include files include_once( get_stylesheet_directory() . '/lib/vcard/VCard.php' ); include_once( get_stylesheet_directory() . '/lib/vcard/VCardException.php' ); include_once( get_stylesheet_directory() . '/lib/vcard/VCardParser.php' ); use JeroenDesloovere\VCard\VCard; function add_vcard( $notification, $form, $entry ) { ...
This is where I hit a little stumbling block – this vCard library depends on the Behat Transliterator which, because I downloaded the file manually, didn’t come pre-packaged with the file. So I had to download that manually, add the files to my lib/vcard/ directory and modify the lib/vcard/VCard.php line 12 to include them:
include_once( get_stylesheet_directory() . '/lib/vcard/Transliterator.php' ); use Behat\Transliterator\Transliterator;
Now that I had everything in place, I could move on to generating the vCard. That was pretty easy, since the library is pretty well documented. Based on the example.php file from the library, I modified the add_vcard()
function to generate a vCard from the data submitted in the form.
The first step was to create a new vCard object, like so: $vcard = new VCard();
Next, I needed to assign the entry data to the corresponding vCard fields. Fortunately, I already had the $entry
object. And Gravity Forms has a neat little helper function called rgar()
that both checks if a value exists and, if it does, returns it. So to access data from any given form field, I just needed to use rgar($entry, 'FIELD ID')
.
For example, adding an email to the vCard would look like: $vcard->addEmail(rgar($entry, 'FIELD ID'))
;
Some of the data in my form is in a complex form field, like the Name and Address fields. Accessing that data is a little different, as I needed to call rgar($entry, 'FIELD ID.SUB-FIELD ID')
.
Note: Field IDs can vary, but sub-field IDs remain the same for each instance of the field. So for example, the first name field will always be FIELD ID.3
. I recommend referring to the Entry object documentation, as well as the documentation for the various Advanced Fields, to learn more.
My code for adding the submitted data to the vCard, based on my form, looks like this:
// create new vcard $vcard = new VCard(); // add name $firstname = rgar($entry, '1.3'); $lastname = rgar($entry, '1.6'); $vcard->addName($lastname, $firstname, null, null, null); // add additional data $vcard->addCompany(rgar($entry, '3')); $vcard->addPhoneNumber(rgar($entry, '4')); $vcard->addEmail(rgar($entry, '5')); // add address $street = rgar($entry, '6.1') . ' ' . rgar($entry, '6.2'); // combine address 1 and 2 fields $town = rgar($entry, '6.3'); $state = rgar($entry, '6.4'); $zip = rgar($entry, '6.5'); $vcard->addAddress(null, null, $street, $town, $state, $zip);
Step Four: Save the vCard to the Uploads Directory
In order to attach the file, the file has to live somewhere. So I wanted to save my generated vCard to the default WordPress uploads directory. Fortunately, the vCard library has functions for defining file paths and file names.
Since I already had the uploads directory saved in my add_vcard()
as $upload_path
, I just needed to create a file name and save the file. So between declaring the $upload_path
variable and the $attachment
variable, I added the following code:
// define filename $firstname = preg_replace("/[^a-zA-Z0-9]/", "", $firstname); // strip special characters $lastname = preg_replace("/[^a-zA-Z0-9]/", "", $lastname); // strip special characters $filename = $lastname . '-' . $firstname; // create file name $filename = strtolower($filename); // convert to lowercase $vcard->setFilename($filename, true, ''); // file will be named lastname-firstname.vcf // save vcard to file in uploads directory $vcard->setSavePath($upload_path); $vcard->save();
This is pretty self-explanatory. I decide to call the file lastname-firstname.vcf, and then I save it to the uploads directory.
Step Five: Attach the vCard to the Notification Email
Since I’m already attaching a generic file to the email, all I needed to do was change what file I attached. To do that, I updated the $attachment
variable like so:
// get attachment path $attachment = $upload_path . '/' . $filename . '.vcf';
Once that’s updated, the add_vcard()
function was all done! Here’s the full function:
function add_vcard( $notification, $form, $entry ) { // target admin notification if( $notification['name'] == 'Admin Notification' ) { // create new vcard $vcard = new VCard(); // add name $firstname = rgar($entry, '1.3'); $lastname = rgar($entry, '1.6'); $vcard->addName($lastname, $firstname, null, null, null); // add additional data $vcard->addCompany(rgar($entry, '3')); $vcard->addPhoneNumber(rgar($entry, '4')); $vcard->addEmail(rgar($entry, '5')); // add address $street = rgar($entry, '6.1') . ' ' . rgar($entry, '6.2'); // combine address 1 and 2 fields $town = rgar($entry, '6.3'); $state = rgar($entry, '6.4'); $zip = rgar($entry, '6.5'); $vcard->addAddress(null, null, $street, $town, $state, $zip); // get upload root for WordPress $upload = wp_upload_dir(); $upload_path = $upload['basedir']; // define filename $firstname = preg_replace("/[^a-zA-Z0-9]/", "", $firstname); // strip special characters $lastname = preg_replace("/[^a-zA-Z0-9]/", "", $lastname); // strip special characters $filename = $lastname . '-' . $firstname; // create file name $filename = strtolower($filename); // convert to lowercase $vcard->setFilename($filename, true, ''); // file will be named lastname-firstname.vcf // save vcard to file in uploads directory $vcard->setSavePath($upload_path); $vcard->save(); // get attachment path $attachment = $upload_path . '/' . $filename . '.vcf'; if ( file_exists( $attachment ) ) { // check if vcard exists $notification['attachments'] = rgar( $notification, 'attachments', array() ); $notification['attachments'][] = $attachment; // attach file } } // return altered notification object return $notification; } add_filter( 'gform_notification_1', 'add_vcard', 10, 3 ); // only filter submissions for form ID 1
Step Six: Cleaning Up
I didn’t want to fill the client’s webspace with stray .vcf files. It also just kind of seems like a privacy issue to have these files with contact data stored on the server for a long time. So I wanted to make sure that after the email was sent, I deleted the generated vCard file. To do that, I needed to use the gform_after_submission
hook. This hook is similar to the gform_notification
one in that you can specify which form to target by appending a form ID.
To get started, I defined a function called delete_vcard()
and hooked it into gform_after_submission_1
, so it will run after a submission has been stored for form 1.
function delete_vcard($entry, $form) { // code goes here } add_action('gform_after_submission_1', 'delete_vcard', 10, 2);
From there, I needed to do two things:
- Get the name of the generated vCard
- Delete the generated file
The first part is pretty simple. Since I had access to the $entry
object, I essentially just have to re-get the data.
// get upload root for WordPress $upload = wp_upload_dir(); $upload_path = $upload['basedir']; // get filename $firstname = preg_replace("/[^a-zA-Z0-9]/", "", rgar($entry, '1.3')); // get first name without special characters $lastname = preg_replace("/[^a-zA-Z0-9]/", "", rgar($entry, '1.6')); // get last name without special characters $filename = $lastname . '-' . $firstname . '.vcf'; $filename = strtolower($filename); // convert to lowercase // assemble full vcard path $vcard = $upload_path . '/' . $filename ;
Now that I have the full path to the vCard file, I just need to delete it. I recommend making sure the file actually exists before trying to delete it, otherwise you run the risk of throwing an error. To do that, you just need the following snippet:
if (file_exists($vcard)) { // check if file exists unlink($vcard); // delete file }
So the full delete_vcard()
function looks like this:
function delete_vcard($entry, $form) { // get upload root for WordPress $upload = wp_upload_dir(); $upload_path = $upload['basedir']; // get filename $firstname = preg_replace("/[^a-zA-Z0-9]/", "", rgar($entry, '1.3')); // get first name without special characters $lastname = preg_replace("/[^a-zA-Z0-9]/", "", rgar($entry, '1.6')); // get last name without special characters $filename = $lastname . '-' . $firstname . '.vcf'; $filename = strtolower($filename); // convert to lowercase // assemble full vcard path $vcard = $upload_path . '/' . $filename ; if (file_exists($vcard)) { // check if file exists unlink($vcard); // delete file } } add_action('gform_after_submission_1', 'delete_vcard', 10, 2);
The vCard Generator in Action
Wondering how it looks when a user submits the form and the vCard is generated? Here’s a demo of the process:
Final Thoughts
If I’m being honest, I thought this would be a lot harder than it was. Is it a perfect solution? Probably not. I definitely think this could be extended further and built into a plugin. Unfortunately, I am *not* the person to develop that plugin, but I’m more than happy to share my code in the hopes that it inspires someone else to go out there and give it a try. 😂
Was this helpful?
I hope it was! If you’re feeling generous, feel free to leave me a tip on my Ko-fi!
Daniel Bishop says
Nice work! That’s a really cool solution.
Kyrie Tompkins-Overlock says
Thank you!
Aaron says
Great write-up. Thanks for sharing the process and the solution.
I like working with Gravity Forms since it offers so many options to extend it and build upon and has great documentation.