Category Archives: Programming

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\_%' )
));
HTML5 Logo

Disable Arrows on Number Inputs

HTML5 introduced some interesting form element changes. Inputs can now have an input type of “number”.

Web Browsers set character input restrictions and validation parameters for this input type. It will restrict to characters 0-9. This is great because it reduces the code overhead required to ensure a user enters the correct data. It will also show a numeric keypad on devices with dynamic keyboards. You should always have server side form validation. By using a number input you’re not relying entirely on JavaScript for client side validation.

Unfortunately there are now a couple of caveats to using the number input, depending on your use case. Firefox and Webkit browsers will show an Up and Down arrow for the user to increment the number. These can get in the way of styling or cause user error. The other issue is that this increment can be adjusted with the Mouse Wheel and the Up and Down keys.

The below code will allow you to remove the increment arrows and maintain the other benefits of a number input.

/* Hide HTML5 Up and Down arrows. */
input[type="number"]::-webkit-outer-spin-button, input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

input[type="number"] {
    -moz-appearance: textfield;
}
jQuery(document).ready( function($) {

    // Disable scroll when focused on a number input.
    $('form').on('focus', 'input[type=number]', function(e) {
        $(this).on('wheel', function(e) {
            e.preventDefault();
        });
    });

    // Restore scroll on number inputs.
    $('form').on('blur', 'input[type=number]', function(e) {
        $(this).off('wheel');
    });

    // Disable up and down keys.
    $('form').on('keydown', 'input[type=number]', function(e) {
        if ( e.which == 38 || e.which == 40 )
            e.preventDefault();
    });  
      
});
CSS3 Logo

Ticker Using CSS Transitions & jQuery

The other day I was looking for a lightweight ticker to show some special offers. I couldn’t do this entirely with CSS (CSS3 Transitions). Some JavaScript is required to toggle an active state. I also wanted the ticker to pause when you mouse over the element. I forgot that I could use the Carousel built into Bootstrap, which the site does use, so I wrote my own jQuery plugin. Rather than delete the plugin, I thought I would share it.

The HTML is simple. You’ll need a wrapping element with some children. Give the wrapping element a class for its role ‘ticker’. For each of the children you want it to toggle the active state, add class ‘item’. The first item you want to show should be given the class ‘active’.

<div class="offers ticker">
    <ul>
        <li class="item active">10% off your first purchase when you <a href="#">sign up</a>!</li>
        <li class="item">Free Shipping on orders over $100!</li>
    </ul>
</div>

Next we need some CSS. There is many transitions you can create. I want a crossfade and a vertical movement. I have provided just what is required to make the animation work.

.offers {
    position: relative;
}

.offers .item {
    position: absolute;
    top: 0;
    -webkit-transition: -webkit-transform .3s ease-in, opacity .3s linear;
    -moz-transition: -moz-transform .3s ease-in, opacity .3s linear;
    -ms-transition: -ms-transform .3s ease-in, opacity .3s linear;
    -o-transition: -o-transform .3s ease-in, opacity .3s linear;
    transition: transform .3s ease-in, opacity .3s linear;
}

.offers .item:not(.active) {
    -webkit-transform: translateY(-100%);
    -moz-transition: translateY(-100%);
    -ms-transition: translateY(-100%);
    -o-transition: translateY(-100%);
    transform: translateY(-100%);
    opacity: 0;
}

And then finally, the JavaScript. I wrote this using jQuery, which means you will need to add this before my plugin if its not already there.

/*!
 * TSG Ticker Plugin v1.0.0
 * http://www.thatstevensguy.com/
 *
 * Released under the MIT license.
 * Copyright 2015, That Stevens Guy
 */

(function( $ ) {

    $.fn.ticker = function( options ) {
        
        var settings = $.extend( {
            timeout     : 5000,
            targetClass : '.item',
            activeClass : '.active'
        }, options );
        
        this.each( function() {
                        
            var ticker = $( this );
            var interval;
            
            $( ticker ).on( 'mouseover', function() {
                pause( ticker );
            });
            
            $( ticker ).on( 'mouseout', function() {
                start( ticker );
            });
            
            function start()
            {
                interval = setInterval( next, settings.timeout );
            }
            
            function pause()
            {
                clearInterval( interval );
            }
            
            function next()
            {
                var target;
                target = $( ticker ).find( settings.targetClass + settings.activeClass ).next();
                target = target.length ? target : $( ticker ).find( settings.targetClass ).first();
                $( ticker ).find( settings.targetClass + settings.activeClass ).removeClass( settings.activeClass.replace('.', '') );
                $( target ).addClass( settings.activeClass.replace('.', '') );
            }
            
            start( this );
            
        });       
        
    };

}( jQuery ));

Then use this to execute the plugin.

jQuery(document).ready( function($) {
    $('.ticker').ticker();
});

If you are using Bootstrap. You can forget about the jQuery plugin I created. But you can use the CSS, just change class ‘ticker’ to class ‘carousel’ and add the other attributes below.

<div class="offers carousel" data-ride="carousel" data-interval="5000">
    <ul>
        <li class="item active">10% off your first purchase when you <a href="#">sign up</a>!</li>
        <li class="item">Free Shipping on orders over $100!</li>
    </ul>
</div>

All you need to do now is add your own finishing touches!