Blank PDF Certificates in Moodle

I had a strange issue today with a client agency building a new LMS using Totara’s implementation of Moodle that might help someone.

The LMS generally worked fine, when the user completed a course and went to download their PDF certificate, it appeared to be generated correctly and previewed correctly on a Mac, but when viewed in Acrobat it was blank. It was a high profile project and is to be shown to the end client tomorrow, so the pressure was on.

Oddly, the client had a similar site on the same server that worked fine. I scratched my head for a while and went down a few blind alleys before noticing that the PDF generator was TCPDF and the problematic site had a slightly newer version. I took a chance and swapped the /lib/tcpdf folder from the working instance to the new and everything started working correctly.

Not sure what the cause of the problem was, but the client is happy.

 

Bulk Replacing Content in WordPress – A New Trick

We’re currently working on a project to update the site of a client whose name and branding has changed. So there are lots of instances of their name to change in the posts and postmeta tables, as well as some inline styles left by the previous developer.

For the posts table, replacing strings in the MySQL table is usually fine, if a bit fiddly, but the postmeta table is not straightfoward, particularly when you have serialised data.

Our new trick is to use the excellent BackupBuddy plugin. If you look on the Database tab of BackupBuddy > Server Tools, down the bottom there’s a section called “Database Mass Text Replacement”. Enter your find and replace text and away you go. Obviously you need to be very, very careful, backup first, check as you go along etc, but for something like a domain or an email, it’s perfect.

 

More quirks in the latest version of WordPress

One of the great things about WordPress is the ease with which it can be updated. One button press does the job, which is so much easier than some of the other CMS’ that we work with. However, there are occasionally changes that can break older versions of themes and plugins, so it’s always worth taking a backup, and ideally testing on astaging version of the site.

We’re currently helping one client upgrade their estate to the latest version and have found a few issues that we want to share. They have been documented elsewhere, but no harm in repeating in case its helpful. The first is an upgrade to the version of jQuery and an incompatibility with older versions of Visual Composer.

We found a couple of themes where front end components in the theme weren’t working after the upgrade. Looking in Firebug we traced the issue to the following code,

a[href*=#]

searching and replacing it with

a[href*='#']

seemed to do the trick nicely and we were back in business.

Visual composer is a bit more tricky, generally what we found was that the backend editor wouldn’t load. If you bought the plugin separately then an update should be available, if you got it bundled with a theme, then your vendor should be able to support you and supply you with a new version of the theme or plugin.

If you have a recent version available, then a quick fix seems to be to replace the backend.min.js file from /wp-content/plugins/js_composer_theme/assets/js/dist.

If you are running the standard version of the plugin then the best plan is probably to overwrite the files using FTP with a the latest version (4.11.2.1) taken from a theme update or if you have a license for the standalone version of the plugin (it’s about US$35 IIRC). Should be a straight swap, but be careful if you have a very old version of the plugin running.

cURL and HTTPS

I had an odd problem the other day whilst building a new WordPress-based ticketing site with payment integration with PayPal Pro. The standard PayPal integration worked fine, but the Pro version kept giving me errors saying that it couldn’t connect to PayPal.

The vendor was quick to reply, but wasn’t much help. Their first suggestion was to upgrade the plugin, which didn’t change anything, and the second was to disable all other plugins, which didn’t help either.

So I downloaded the site to my Mac, ran it using MAMP and used the excellent HTTP Scoop to check whether it was really connecting to PayPal. The answer was no, so I delved into the code and started to look at what it was doing.

The answer was that it was using cURL to connect to PayPal. So I ran a quick test to explore why it wasn’t working and found that:

  1. It worked fine with an HTTP URL
  2. Although there were various suggestions around using directives, specifying alternate protocols etc, none of these seemed to work with the PayPal server

So I wondered whether it might work in curl at the command line (the -v is for verbose output).

curl -v https://api-3t.sandbox.paypal.com/nvp

That gave a message which suggested that there was a problem with the SSL handshake.Researching a little further, it seemed that there might be a problem with openssl or the version of curl itself.

At this point I didn’t want to start messing with the version of curl on my Mac, but went instead and tried on a development server that I had access to and upgraded the version of openssl to 1.0.1. That seemed to resolve the problem there, so I then checked on the client’s server and whilst they were running openssl 1.0.1, their version of curl was relatively old (7.19.0) so, after checking the backups I upgraded curl and it worked at command line. A quick run of my test script suggested that it was working, and when I went back to the site, the integration was working properly.  I should add that I didn’t have to upgrade PHP itself.

So there you go, if you can’t get cURL connecting via SSL, check your openssl and curl versions and check that both are up to date.

DNS Oddities in OSX

I recently encountered some weirdness with DNS in OSX that I thought I’d share, in case it came in helpful for anyone.

The scenario was that a client’s website displayed fine on my laptop with the plain variant of the email (e.g. http://domain.com), but the www variant was pointing to the server’s default page. Pinging the server from Terminal in my Mac yielded the same IP address for both, so all looked good.

Initially I thought that it was a server configuration issue, so checked all the settings, but there was nothing obvious.

I then checked the DNS records for the domain thinking that perhaps we didn’t change both variants when we went live (the site was done in a hurry and went live late at night), but everything looked fine.

Then, clutching at straws, I added a test file to the server’s default site to make sure I was really looking in the right place, and realised that I was looking at my Apache server, not the Windows one I expected.

At this point I realised that something odd must be going on with DNS, so went to the server in Remote Desktop and checked in the browser there. The www variant worked. Then I checked on my phone and it also worked, so something odd was definitely going on with my Mac.

At this point it occurred to me to open the hosts file, and to my surprise it seemed I’d added an entry for the www variant of the domain for the previous version of the client’s website (which was WordPress, hence the Apache server) literally years ago (in fact, OSX must have migrated the hosts file from my old laptop).

So the morals of the story are:

  1. Never forget that OSX is too clever by half
  2. Don’t trust the IP address yielded by ping in Terminal on a Mac, try a third-party service to be sure.

Suppressing Previews in Outlook.com

If you use Outlook.com (Hotmail as-was), you’ll notice that it has an annoying habit of adding previews when you add in a URL. These previews are composed of the site’s metadata plus a picture, usually the logo (though sometimes it can be a bit random what gets thrown up), and as far as we can tell, you can’t turn them off.

If you’re a regular user you’ll just click the link to remove the preview and be done with it. Unfortunately, following deployment of a new website for a client, their email signature started including a preview below the link to their website when it was replied to using Outlook.com, the client wasn’t happy and, even though we were pretty sure it wasn’t our fault, we were required to find a resolution. Removing the link to their website from the email footer wasn’t an option we were told.

Once we could repeat the problem, we looked at how to prevent it from happening. We demonstrated that it wasn’t just their website, the same problem occurred even if we changed the link to Amazon.com or Google.com. Unfortunately we couldn’t find much in the way of answers, so we were on our own.

Going back to first principles, we looked at adding rel=”nofollow”, and then realised that we could prevent the problem occurring if we took the link out of the table tags that (quite reasonably) had been used to govern layout. We tried reformatting the signature with div tags, but the problem recurred. We then wondered what would happen if we rearranged the order of links, at which point a eureka moment occurred, Outlook.com was picking the content of the first link it came across.

The solution was then straightforward. We added an empty hyperlink <a href=”#”></a> before the link to the client’s website, and the problem went away. Presumably Outlook.com finds the link, sees it links to no-where and stops. Result!

Adding WPML Taxonomy Translations Via Code

This is really obscure, but hopefully helpful to someone.

When you are using WordPress, this enables you to add a translation for an existing taxonomy term and associate it with the original. For example, when you are using WPML and creating posts via the WordPress API from an XML feed or similar.

So basically you pass in the details of your new term, the taxonomy name, the ISO language code you are targeting and the ID of the existing taxonomy. I lifted this from an existing sample, but  amended it so the slug was unique (I was lazy and just add the language code to the original), and also fixed an issue where the correct id wasn’t being returned.

Enjoy!

/* MT 2015-10-29 Enables Categories to be Programatically Added
    (str) $term is the text of the term you want to add.
    (str) $taxonomy is the name of your custom taxonomy.
    (str) $language_code is the two letter code of the language you want to save the term in.
    (int) $content_id is the ID of the term in the default language to associate with the new term.
    @ returns (bool) false on error or if term exists. (int) 0 for no affected rows and term object on success.
*/
function wpml_insert_term($term, $taxonomy, $language_code, $content_id) {
    
    $slug = "";
    $old_term = get_term( $content_id, $taxonomy );
    $slug = $old_term->slug . '-' . $language_code;
    
    $new_term = false;
    //if(!term_exists($term, $taxonomy) && taxonomy_exists($taxonomy)){
    if(taxonomy_exists($taxonomy)){
        $new_term = wp_insert_term($term, $taxonomy,array('slug' => $slug));
        
        if(isset($new_term['term_id'])){
            // Include WPML API
            include_once( WP_PLUGIN_DIR.'/sitepress-multilingual-cms/inc/wpml-api.php' );

            global $wpdb;
            $update = $wpdb->update( 
                $wpdb->prefix.'icl_translations', 
                array( // data
                    'trid' => wpml_get_content_trid( 'tax_'.$taxonomy, $content_id), 
                    'element_type' => 'tax_'.$taxonomy, 
                    'language_code' => $language_code, 
                    'source_language_code' => wpml_get_default_language()
                ), 
                array( // where
                    'element_id' => $new_term['term_id'],
                    'element_type' => 'tax_'.$taxonomy
                )
            );
            
            $new_term = ($update === false || $update === 0) ? $update : $new_term;
            
        }
    }
    return $new_term;
}

Sorting HTML Option lists with jQuery

I’ve recently been working on a WordPress plugin for a client that includes the ability to filter the results via multiple select boxes, with the contents of the select boxes themselves filtered so only the relevant options are shown.

It’s a nice little solution that I’d love to show off, but alas, I’m NDA’s and can’t talk about work for that client.

What I can share though is a neat little solution for sorting the contents of select lists alphabetically. It’s borrowed from elsewhere on the web, so not my own work, but useful nonetheless.

var options = $('select.whatever option');
    var arr = options.map(function(_, o) {
        return {
            t: $(o).text(),
            v: o.value,
            s: $(o).prop('selected')
        };
    }).get();
    arr.sort(function(o1, o2) {
        return o1.t > o2.t ? 1 : o1.t < o2.t ? -1 : 0;
    });
    options.each(function(i, o) {
        o.value = arr[i].v;
        $(o).text(arr[i].t);
        $(o).prop('selected', arr[i].s);
    });

Backup Strategies for WordPress

For even the smallest WordPress site, not having an effective backup strategy in place can mean a small issue with your website resulting in a prolonged period of downtime, losing your place in search engine rankings and many hours of valuable time being taken up trying to piece a site back together. If your website is a shop and/or includes significant amounts of user data, then the loss can be catastrophic and something that can sink your business. Fortunately, there are plenty of tools out there that can help, and with a small investment of time, you can be prepared for the worst.

It’s sometimes said in IT that you haven’t backed up properly unless your data is in at least three places, and used at least two separate backup methods. This is something we definitely agree with and encourage. If one set of backups are somehow inaccessible, then having a second set taken using an alternative mechanism increases the probability of recovery significantly.

The first step is to document your website. Make sure you have at least the following in a document, printed out and filed where it can be found in case of a disaster. Usually we don’t advocate writing down passwords, but this is an exception to the rule, you should password-protect your electronic copy though.

  • DNS registrar details and control panel login
  • IP address of your site and host control panel login details
  • Emergency contact details for your host
  • Your WordPress database connection details
  • Your WordPress dashboard login
  • Details of your theme and plugins
  • Details of any third party services you use and logins, such as MailChimp, PayPal etc.
  • Details of passwords for backups.

The next step is to make sure that your website is being backed up. We usually go with a two-pronged approach. The first is to have the host back-up the server and database. The usual pattern is to take a full backup each week (often on a Saturday or Sunday night), then an incremental backup each night that picks up changes. Keeping backups for the previous 4 weeks is usually a good trade-off between not taking up too much space and being able to go back a reasonable distance in time in case you find that a problem has existed for a while.

It goes without saying that you should check on a regular basis that your backups are running correctly (setting up email alerts is ideal) and that the files can be accessed. Unfortunately it can sometimes be difficult to do a test restore as it will result in your website being overwritten, but it’s generally a good idea to try out a backup and restore just before you send your website live. One thing to be wary of is that databases get backed up properly. Just backing up files is often not enough and in order to be able to restore your database you’ll need to have run a database-specific backup routine in place to dump the data. If you do this, make sure you keep a note of where it gets saved, and the steps you’ll need to perform to restore it.

The second prong of our approach is to use BackupBuddy. This is a brilliant plugin for WordPress which is a true “Swiss Army Knife” of backup. Our preferred strategy is to take a weekly full backup of the website, then nightly backups of the database, with a 4 week retention period. It’s usually enough unless you are continually loading files onto the site, in which case you’ll want to do a full backup more often.

One of the nice features of BackupBuddy is that it will send your backups to a number of different locations, including FTP, Dropbox, AWS and their own proprietary “Stash”. My preferred location is to leave the files on the web server and to send them to Dropbox as I have something like a TB of storage that I’m not otherwise using and it’s easy for me to get at the files from just about anywhere should I need to. One tip is to set the backup folders not to synchronise with your local machine to save bandwidth and your disk space. As with server backups, it can be handy to set BackupBuddy to remind you by email when it has run, and you should check from time to time that files are being saved to the right place.

The other nice thing about BackupBuddy is the ease of which you can restore files not just to your web server, but an alternative location, for example, if you want to check the contents of a backup on a test domain, or if you want to move your site to a new host. All you need is the zip of your backup (either the full or database-only version) and a  file called importbuddy.php. Upload both to the root of your website using FTP, then run importbuddy.php in a web browser and follow the simple on-screen instructions.

The other nice thing about the importbuddy process is that it will update your wp-config.php file and database should you change the domain or database server details. As such, it’s also a really useful tool during development, for example, when you want to move a site from a development to production server (or visa versa), but that’s something for another day.

 

 

A Few Contact Form 7 Tricks

One of my favourite WordPress tools is the free Contact Form 7 plugin. Simple and easy to use, it offers a really simple way to add feedback forms or surveys to your site with no programming required. It usually looks pretty good “out of the box”, but with a little styling it can look amazing.

Here are a few tricks that I’ve learned along the way:

How to save submissions to a database

It can often be really useful to be able to save form submission details in the WordPress database and be able to export them in CSV format, for example, so you can export the data to a CRM, email broadcast system or analyse the data in Excel. Even if you don’t need to do so immediately, it’s often well worth installing the brilliant Contact Form DB plugin, which seamlessly stores your data and provides you with a section in the dashboard to retrieve your data*.

*Don’t forget that your database will now contain live customer details, so you need to be extra-careful with backups etc.

How to import and export forms

It can occasionally be handy to import and export the forms themselves, so you can use them on another website, or create a variation.

Because Contact Form 7 stores its contact form data as a custom post (post type: wpcf7_contact_form), that’s really easy to do an you can export and import form data via Tools > Export and Tools > Import in the WordPress admin screen.

In the Export menu, choose “Contact Forms” if you want to export contact form data only. You can also choose “All content” (this includes contact form data).

How to log a submission as an event in Google Analytics

If you’ve set up Google Analytics in a conventional way, then it’s really easy to log submissions as events, you don’t even need to do any programming, there’s just a little code that you can add in the WordPress dashboard as you edit your form.

In the Additional Settings field at the bottom of the contact form management page; simply insert the following line:

on_sent_ok: "_gaq.push(['_trackEvent', 'Contact Form', 'Submit']);"

Note that you need to quote the codes properly and the code must be in one line.

on_sent_ok in Additional Settings

Now, if you have set everything up correctly, when someone makes a submission via your contact form, Google Analytics will track it as an event with Contact Form as the Category, and Submit as the Action.

To verify that this is working correctly, you can check the Content > Events > Overview report page on Google Analytics 24-48 hours after a submission. At that point, you should be able to find the Contact Form category there.

How to redirect following submission

It’s often handy to be able to redirect a user to a new page after they have completed the form, both to provide them with additional information such as a thank you, or because you want to log the submission in Google Analytics (although events are a neater way to do that).

The simplest way is using on_sent_ok JavaScript action hook. By using this hook, you can specify a JavaScript code that you wish to run after the form is successfully submitted. You will find the Additional Settings field at the bottom of the contact form management page. Simply insert the following line into it:

on_sent_ok: "location = 'http://example.com/';"

Obviously, you need to replace the http://example.com/ to the URL you want to redirect to.

on_sent_ok example

That’s it. So simple, isn’t it? Try it and check if it works.

It’s done using JavaScript, so you may occasionally find that not everyone gets redirected. The user will also possibly see the default Contact Form 7 thank-you message, so there might need to be a little bit of CSS added to tidy things up.

How to add an autoresponder

An autoresponder sends a user an email confirming their enquiry. Although it can seem like overkill, it can be a nice way to confirm that the request was sent and to provide users with contact details and other information that it might be handy for them to have.

It’s really easy to do in Contact Form 7, simply check the “Use mail (2)” box in the form’s configuration panel. Mail (2) is almost the same as Mail, except that Mail (2) is sent only when Mail has been sent successfully.

How to add id and class attributes to a form element

When you are creating a custom form, it can be really handy to be able to be able to specify an id and class for a form. To do this, just add the html_id and html_class attributes into a [contact-form-7 404 "Not Found"] shortcode.

Example:

[contact-form-7 404 "Not Found"]