Hooking into WooCommerce’s checkout JS events

Trying to prevent WooCommerce to submit the checkout form in order to manipulate the data submitted, I found that the plugin uses the jQuery “triggerHandler” function. That’s cool because you can use it to check for your custom data for example.

The actual use

Say you want to validate the form using WooCommerce’s Ajax calls, but prevent the actual order submission so you can add extra steps ( like presenting some up-sell offers ). In this case you can hook with js in a manner similar to the one below:

var checkout_form = $( 'form.checkout' );
checkout_form.on( 'checkout_place_order', function() {
    // do your custom stuff
    checkout_form.append('<input type="hidden" name="m_prevent_submit" value="1">');
// return true to continue the submission or false to prevent it return true; 
});

What you can do next is: allow the validation to go through just before the order is added and trigger a custom notice.

You’ll need to hook into “woocommerce_after_checkout_validation” and check if the hidden input we added previously is present. If it is and no error was previously recorded you can trigger the error.

add_action('woocommerce_after_checkout_validation', 'm_prevent_submission');
function m_prevent_submission($posted) {
    if ( isset($_POST['m_prevent_submit']) && wc_notice_count( 'error' ) == 0 ) {
           wc_add_notice( __( "custom_notice", 'm_example' ), 'error');
// change the data in $posted here
       } 
   }

Afterwards, in Javascript you can check for your custom error using a function like:

$( document.body ).on( 'checkout_error', function() {
    var error_text = $('.woocommerce-error').find('li').first().text();
    if ( error_text=='custom_notice' ) {
        // do your custom js stuff
    }
});

The same logic can be applied for example to changing the cart redirect for specific products, more on that on a future post.

Adding Youtube video to Instant Articles for WP plugin

The official plugin for adding Instant Articles support to a WordPress website is Instant Articles for WP created by Automattic & Facebook.

The problem

This plugin generates the XML needed by Facebook to enable the Instant Articles functionality for your website. Although it will include your external resources ( like a Youtube video ) by default if it’s placed in the main post content, adding it from a custom field isn’t that easy and you need to define custom functionality.

The easiest way of adding custom content is by hooking into the “instant_articles_transformed_element” filter and modifying the content directly.

Because the instant article is composed of multiple XML components you can’t just add the iframe to the content as that will get ignored in the final output or worse: throw errors.

Adding a video to the Instant Articles

The recommended way is to use the Facebook Instant Articles PHP SDK functions available from the plugin:

add_filter( 'instant_articles_transformed_element','m_add_video' );
function m_add_video ($instant_article) {
    
    // ideally you should check that the class exists,
    // but as the filter will only be called in the plugin it shouldn't be a problem
    $post_id = $GLOBALS['_POST']['post_ID'];
    $video_url = get_post_meta($post_id, 'video_url', true);
    
    if ( !empty($video_url) ) {
        // make a full reference to the class in the plugin
        $video = \Facebook\InstantArticles\Elements\Interactive::create();
        $video->withSource($video_url);
        $video->withWidth(560);
        $video->withHeight(315);
        $video->withMargin(Facebook\InstantArticles\Elements\Interactive::NO_MARGIN);
        $instant_article->addChild( $video );
        
    }
    
    return $instant_article;
    
}

This will add a new iframe reference at the end of the instant article, if you are looking to add it in the beginning ( before the post content ) you can use “unshiftChild” instead of “addChild“.

Important:

  • you must set both width and height if you use “withSource” or it will get ignored by the SDK as not valid
  • if you want to avoid setting a height for custom iframes you can send something in the “withHTML” method
  • setting the iframe source directly to player.vimeo.com doesn’t work for some reason. You’ll have to add the Vimeo videos using the full iframe code in the withHTML method.

Updating WordPress password without logging out

I recently created a plugin which among others forces the users to update their passwords during the setup phase.

After going live with the functionality, the client reported the users were confused as they could no longer access the website. I then realized that using wp_set_password() will update the password but also destroy the current user session forcing him to re-authenticate, which is good, but in my case the function was called after the initial headers were set so the user saw the loaded page but couldn’t use any of the features there.

The fix?

Use instead

$userdata = (array)wp_get_current_user();
$userdata['user_pass'] = $new_password;
wp_update_user($userdata);

This will update the password but also re-create the user session, as it shows at the end of the wp_update_user() function:

// Update the cookies if the password changed.
$current_user = wp_get_current_user();
if ( $current_user->ID == $ID ) {
   if ( isset($plaintext_pass) ) {
      wp_clear_auth_cookie();
      // Here we calculate the expiration length of the current auth cookie and compare it to the default expiration.
      // If it's greater than this, then we know the user checked 'Remember Me' when they logged in.
      $logged_in_cookie    = wp_parse_auth_cookie( '', 'logged_in' );
      /** This filter is documented in wp-includes/pluggable.php */
      $default_cookie_life = apply_filters( 'auth_cookie_expiration', ( 2 * DAY_IN_SECONDS ), $ID, false );
      $remember            = ( ( $logged_in_cookie['expiration'] - time() ) > $default_cookie_life );
      wp_set_auth_cookie( $ID, $remember );
   }
}

Note: this has to be called before the headers were sent as it will attempt to re-write them and you will get the “headers already sent” error. So I recommend running it in the wp action which runs exactly before anything is printed.