Category Archives: WordPress

WordPress Logo

Convert Taxonomy Options to Term Meta

WordPress 4.4 introduced Taxonomy Term Meta. Until this version, meta had to be stored as an Option. Most tutorials would tell you to give it a prefix followed by the ‘term_id’ so you can grab the data easily later. I already had a site with lots of populated meta. What I needed was a conversion script to move meta from an Option into real Term Meta.

/**
 * Convert Taxonomy Options to Term Meta
 */
$taxonomy = 'my_taxonomy';
$option_prefix = 'taxonomy_';

$taxonomy_terms = get_terms( $taxonomy, array( 'hide_empty' => false ) );
$counter = 0;

foreach ( $taxonomy_terms as $term )
{
    $term_id = $term->term_id;
    $option = get_option( $option_prefix . $term_id );

    if ( !empty( $option ) )
    {
        $counter++;

        foreach ( $option as $meta_key => $meta_value )
        {
            delete_term_meta( $term_id, $meta_key );

            if ( !empty( $meta_value ) )
                update_term_meta( $term_id, $meta_key, $meta_value, true );
        }
    }
}

echo "Processed " . $counter . " Taxonomy Term Options";

$counter = 0;

foreach ( $taxonomy_terms as $term )
{
    $term_meta = get_term_meta( $term->term_id );

    if ( !empty( $term_meta ) )
        $counter++;
}

echo " >> [ " . $counter . " ] Taxonomy Terms now have Term Meta.";

I found a couple of interesting quirks with Term Meta while converting my site.

The first is that if you run get_term_meta( $term->term_id ) it will present you with an array of arrays. The value is stored as the first key of these sub-arrays. Where you might have had $term_meta[‘my_key’] for your option, you now need $term_meta[‘my_key’][0]. If the value returned is an array, it will need to be unserialized. This only applies if you want all Term Meta for a taxonomy. If you use get_term_meta( $term->term_id, ‘my_key’, true ) it will automatically perform both these tasks to return a single meta key.

Another difference shows itself when saving values. An Option will overwrite the value (array) in most cases. However the update function for Term Meta will only update individual meta keys. You need to iterate through and store them individually. However this is no bueno when using checkboxes. If the checkbox is unchecked, it will not be present in the postdata. Below is my generic save function for Term Meta, it will delete a value if it was stored previously but is no longer available.

/**
 * Save Term Meta
 */
add_action( 'create_my_taxonomy', 'tsg_save_term_meta', 10, 2 );
add_action( 'edited_my_taxonomy', 'tsg_save_term_meta', 10, 2 );

function tsg_save_term_meta( $term_id )
{
    $term_meta = $_POST['term_meta'];

    if ( !empty( $term_meta ) )
    {
        $term_meta_old = get_term_meta( $term_id );
        foreach ( $term_meta_old as $meta_key => $meta_value )
            if ( !array_key_exists( $meta_key, $term_meta ) )
                delete_term_meta( $term_id, $meta_key );

        foreach ( $term_meta as $meta_key => $meta_value )
            update_term_meta( $term_id, $meta_key, $meta_value );
    }
}

When you’re all finished converting and everything is sweet. You’ll probably want to clean up your options table. Use this function at your own risk.

/**
 * Delete All Taxonomy Term Options - USE AT YOUR OWN RISK
 */
global $wpdb;

$wpdb->query( $wpdb->prepare("
        DELETE FROM " . $wpdb->prefix . "options
        WHERE option_name LIKE %s
    ",
    array( 'taxonomy\_%' )
));
Cloudflare Logo

Use CloudFlare to Secure WordPress by Country Codes

Firstly, check that you have the IP Geolocation option enabled on CloudFlare.

The most efficient way to do this with PHP would be to place the code below in the top of your wp-login.php, but WordPress will overwrite this file when it updates. The next best position is at the top of wp-config.php. If you follow the way WordPress loads, wp-login.php will require wp-load.php first, then after 4 minor lines of code, it will then get wp-config.php.

/**
 * CloudFlare - Connecting IP - for wp-config.php.
 */
if ( !empty( $_SERVER['REMOTE_ADDR'] ) && !empty( $_SERVER['HTTP_CF_CONNECTING_IP'] ) )
    $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP'];

/**
 * CloudFlare - Limit WordPress Login to Australia with Whitelist - for wp-config.php.
 * It is best to bypass for IPv6 unfortunately, unreliable country code from CloudFlare at the moment.
 */
$ip_whitelist = array( '::1', '127.0.0.1' );

if ( in_array( $_SERVER['PHP_SELF'], array( '/wp-login.php' ) ) && !in_array( $_SERVER['HTTP_CF_IPCOUNTRY'], array( 'AU' ) ) )
{
    if ( !in_array( $_SERVER['REMOTE_ADDR'], $ip_whitelist ) && !preg_match( '/^([0-9a-f\.\/:]+)$/', $_SERVER['REMOTE_ADDR'] ) )
    {
        header( 'Location: /' );
        exit;
    }
}

This is just a different interpretation of my friends script. My version does not allow WordPress to waste CPU before booting the IP from the login page. It also allows for an IP whitelist to bypass this for trusted IP addresses.

It is best to pair this with a plugin like Simple Login Lockdown. There are also some useful .htaccess rules you can use, but I won’t go into that here.

WordPress Logo

Stay logged in with WordPress on Subdomains

A clients site performs some magic to show different content per subdomain. By default each subdomain asks you to login, which is a bit annoying. It would be convenient if admins of a single WordPress installation could stay logged in when they jump between subdomains.

I found that all we needed to do was set the cookie domain and path in wp-config.php. At first it seemed only COOKIE_DOMAIN and COOKIEPATH would be needed, but it did not behave until COOKIEHASH was also set. You could probably set it to anything you like, I just had a defined variable already.

/**
 * Set cookie properties to allow persistent login across subdomains.
 */
define('MY_DOMAIN', 'thatstevensguy.com');
define('COOKIE_DOMAIN', MY_DOMAIN);
define('COOKIEPATH', '/');
define('COOKIEHASH', md5(MY_DOMAIN) );

This may also work between WordPress installations across subdomains. I haven’t tested this theory. Both installations would require the same login details and this config. It is working with WordPress versions 3.8+.