It seemed like a simple request from a customer with an existing WordPress site: set up a registration form for a small conference they were hosting, allow them to get all registrations on a spreadsheet, let registrants pay online using PayPal, and set up a tracking ID so registrations can be reconciled with PayPal statements and checks that arrive in the mail. Getting all the pieces together into a cohesive system took a little digging, but the customer is happy with the solution. Here’s a writeup for posterity — something I wish I had when I started. If there’s an easier way (other than buying professional conference software) please don’t hesitate to tell me in the comments.
Initial Conditions:
- Customer has self-hosted WordPress site (version 4.0)
Added WordPress Plugins:
- Contact Form 7 (version 3.9.3)
- Really Simple CAPTCHA (1.8)
- Contact Form DB (2.8.11)
- Contact Form 7 – Dynamic Text Extension (1.2)
- CSS & Javascript Toolbox (7.2)
- jquery.cookie.js (version 1.4.1) — This isn’t a WordPress plugin, its available on GitHub.
Requirement #1: Registration form up and running.
Contact Form 7 is my go-to submission form package. It has been downloaded over 20 Million times for a reason and comes with plenty of documentation. I also installed an used Really Simple CAPTCHA which is implemented by the same author.
Requirement #2: Saving the results in a database and posting the data to users.
I used a plugin for this called Contact Form DB. This plugin has its own video tutorial, so I won’t go into details how to use it here. What I did find very useful for the conference chair is to give them their own password-protected page loaded with a bunch of Contact Form DB shortcodes to keep track of registrations. Here are some examples. Note that the shortcodes are specific to names of items in the form, so use the shortcode builder that comes with Contact Form DB to generate the right codes for you.
- [cfdb-count form="Conference Registration"] is a shortcode that prints out the number of registrants on a WordPress page or post.
- [cfdb-count form="Conference Registration" filter="registration-category=Student"] is a shortcode that prints out how many of those registrants applied for student rate.
- [cfdb-table form="Conference Registration" show="first-name,last-name, email,registration-category"] Shows a table of registrants with their name, email, and category.
- [cfdb-export-link form="Conference Registration"] is a link to the whole table.
Note that the free version of Contact Form DB does not allow one to edit/revise entries in the database, only delete them. There is a paid version available that does enable editing of fields in the database. I’ll bet half the purchase price that the customer will pay for it before the conference is over.
Requirement #3: Time/Date Stamp
Since prices change after certain deadlines, it was important to know exactly when a person registered. Contact Form 7 has [_date] and [_time] shortcodes for emailing this, and Contact Form DB records the times as well. Always nice when plugins just do the right thing.
Requirement #4: Get a PayPal payment button to appear when the form is successfully submitted.
Contact Form 7 strongly prefers a simple text message when a form is submitted correctly. There is a hook for successful submissions to fire some Javascript, which seems to be used mostly for redirecting the page (against the plugin author’s recommendation), or firing Google Analytics events. This hook is available at the very bottom of the form editor in a text block labeled “Additional Settings”. The syntax is dead simple
on_sent_ok: "(javascript goes here)"
Using the PayPal merchant tools to create a payment button with three registration fees (member, nonmember, & student), I had a sizeable blob of HTML that I wanted to insert. I could have used this hook to redirect to a different page, but I decided to do something more gentle and less alarming for the user. I set the “Additional Settings” thusly.
on_sent_ok: "$('.wpcf7-mail-sent-ok').load( '/buttons.html #paypal-conference2015-early');"
This code is written in jQuery and showcases the power of its “.load()” function. I use it here to substitute a section of the existing HTML file with a section of loaded HTML file. In particular, it replaces the success message from Contact Form 7 with other HTML from the resources file. The other HTML has my PayPal button nested inside. I would have preferred putting the HTML fragment in a unlisted post, but WordPress does not have such a concept in their vocabulary, which is regrettable. I just dropped a HTML file called buttons.html directly into the home directory of the webserver. Here’s what the contents look like
1 2 3 4 5 6 7 8 9 10 11 12 |
<html><head></head> <body> <div id=”paypal-conference2015-early”> <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top"> <!-- all kinds of stuff goes here --> <input type="hidden" name="on1" value="Transaction ID">Transaction ID</td></tr> <tr><td><input type="text" name="os1" id="paypal-transaction-id" maxlength="200"> <!-- more stuff follows...--> </form> </div> </body> </html> |
Note, I added a unique ID for the text input on line 7. That will come in handy later.
Requirement #5: The tracking code.
Sometimes things that look simple get really complicated. The idea here was just to create a little code that would show up in both PayPal statement and the registration forms so that the accounting would be easier. Since it’s a small conference and accounting is done by hand, this code doesn’t need strong guarantees of uniqueness so a random string would suffice.
5.a Generate a random string
I went into the functions.php file of the child theme and added the following code.
1 2 3 4 5 6 7 8 9 10 |
/* Generate An 8 Character String for Tracking */ function randomString() { $length = 8; $characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; for ($p = 0; $p < $length; $p++) { $string .= $characters[mt_rand(0, strlen($characters)-1)]; } return $string; } add_shortcode('trackingcode', 'randomString'); |
This gives me a shortcode [trackingcode] that I can use in WordPress pages.
5.b Populate form with the tracking code
Here is where I used Contact Form 7 Dynamic Text Extension. It works with Contact Form 7 and adds two additional form types dynamic text field, and dynamic hidden field. This basically is a shortcode that allows another shortcode to be embedded inside. Here’s the shortcode I used in the Contact Form 7 editor [dynamictext tracking-code id:tracking-code uneditable "trackingcode"] . The first word is the type of the short code. The second becomes the name shortcode for email purposes. Third is becomes the “id” attribute of the HTML element. This is useful for later, and it needs to be unique in the HTML document. The forth word, uneditable, makes it so the user can see the tracking code assigned to them, but cannot change it. Last is “trackingcode” which is the shortcode I created in step 4.a except it doesn’t have the square brackets ([]).
The form looks like this.Users can highlight the text inside the field and copy/paste it elsewhere for reference, but they cannot edit the content of the field.
5.c Propagate the tracking code to the PayPal Button
Now that we have the string and I labelled it with a unique ID, it is not hard to extend the reveal of the PayPal button to also populate the text field and make it uneditable as well. Going back to the Contact Form 7 editor, down in the Additional Settings, I expanded the code as follows (NOTE: I reproduce it here across multiple lines of ease of reading.)
1 2 3 4 5 6 7 8 |
on_sent_ok: "$('.wpcf7-mail-sent-ok').load( '/resources.html #paypal-conference2015-early', function () { var trackingCode = $('#tracking-code'); $('#paypal-registration-id').val( trackingCode ); $('#paypal-registration-id').prop('readonly',true); } );" |
Basically, I added a completion handler to the .load() function. After the PayPal button is loaded, it pulls the tracking-code (using the HTML attribute “id” from section 4.b) and inserts it in the value of the HTML element with id=paypal-tracking-id (from Requirement #4). I also set the property to make it readonly.
There’s only one problem with this solution. It doesn’t work. The reason is subtle, on_sent_ok is an event after the data is submitted and the form resets. Technically, the page does an HTML POST, then reloads as a separate GET operation. Thus a new random string is generated and appears in the PayPal button, not the one that just went out in email.
The solution is to store a little bit of data between form submission and reload. The solution is to use cookies.
5.d Cache the tracking code so they are the same in the email and the PayPal button.
A cookie is just a way of storing a key-value pair on a browser for later use. To manipulate cookies easily from JavaScript, I grabbed the jquery.cookie.js file from GitHub and dropped it in the wp-includes/js/jquery/ directory of the WordPress installation. Then I’ll make a one-line change in the Additional Settings of my Contact Form 7 editor to pull the value of the string from the cookie instead of the HTML element.
1 2 3 4 5 6 7 8 |
on_sent_ok: "$('.wpcf7-mail-sent-ok').load( '/resources.html #paypal-conference2015-early', function () { var trackingCode = $.cookie('tracking-code'); $('#paypal-registration-id').val( trackingCode ); $('#paypal-registration-id').prop('readonly',true); } );" |
Now all I need to do is push the value of the right field into a cookie at the right time. There are many JavaScript files on the server that I could have modified to do this, but the use is so specific to this one page I wanted a solution that would isolate the changes. This is where I turned to another plugin, CSS & Javascript Toolbox. This allows me to create snippets of CSS or JavaScript code in the header or footer of a document and control exactly which pages get it. I imagined this plugin would use custom content types to accomplish its task, but was a little surprised to see how many tables it created in the WordPress database.
The jQuery I really care about is amazingly concise:
1 2 3 4 5 6 7 8 |
if ( $.cookie('tracking-code')) { // if we already have a tracking code set it to our textbox $('#tracking-code').val($.cookie('tracking-code')) } else { // otherwise use the random number from the textbox and // and save it as a cookie for next time $.cookie('tracking-code',$('#tracking-code').val() ) }; |
Of course we need to do some housekeeping: namely load the jquery.cookie.js file into the page, wrap this code in <script> HTML tags, and wrap the interesting part in the cumpulsory “no conflict” incantations of JavaScript. The entire entry in CSS & Javascript Toolbox looks like this. I just assign it to the page containing the Contact Form.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<script type='text/javascript' src='http://trivalleywriters.org/wp-includes/js/jquery/jquery-cookie.js'></script> <script type="text/javascript"> // jquery no-conflict wrapper (function($){ $(document).ready(function(){ if ( $.cookie('tracking-code')) { // if we already have a tracking code set it to our textbox $('#tracking-code').val($.cookie('tracking-code')) } else { // otherwise use the random number from the textbox and save it as a cookie for next time $.cookie('tracking-code',$('#tracking-code').val() ) }; }); })(jQuery); </script> |
Conclusion
With just a few key pieces of PHP and jQuery, I was able to lash several plugins together into a good end-to-end solution for this customer. Now that they have the capability, they are beginning to think about using the same mechanism for monthly meetings and annual subscriptions instead of just this one-off conference.
There’s still a some work getting the registration page, the internal stats page, and the emails right. I will submit an update after the conference was over with experience from setting this up.