WordPress website running Avada very slow when saving posts

If you are running ThemeFuse’s extremely popular template Avada and the website is very slow when saving/publishing posts this may help. This only applies to websites with lots of posts ( or WooCommerce products ).

Make sure this is the issue

The easiest way to check if this is what’s causing the issue is to install the plugin “Delete Expired Transients“. After you installed it go to “Tools > Delete transients” and check the number of expired transients on your website. If you have tens of thousands of transients this is very likely the issue.

The fix for Avada

Before clearing that data make sure you disable it so please go to “Appearance > Theme Options > Advanced > Dynamic CSS” and turn off Database Caching for Dynamic CSS.

Afterwards go back to the Delete transients page and delete all the expired transients ( the first option ). At this point you should experience a great improvement of the loading times when publishing/updating posts.

They mention in the documentation that the Dynamic CSS caching functionality might cause slowness with large websites. In case you missed it this is what happens:

A transient is added to the website’s database each time you create a new post in order to load custom css faster.

What are transients?

In WordPress, Transients are database entries made to store data more conveniently to access. In this case, the Avada theme stores custom CSS rules in order to prevent more complicated queries in a transient for each post, that’s why this is an issue only with a large number of posts.

More on transients and how to use them you can read at Css-tricks or in the codex page.

AWS S3 preview private and encrypted files with PHP

Recently I had to develop a WordPress plugin that is used to manage files stored on Amazon AWS S3. Due to the nature of the files which contain sensible information they are stored as private and using the AES256 ServerSideEncryption.

In order to serve the files to authorized users an AJAX call is used to generate a temporary link from the AWS PHP SDK. My problem is that for these links the “content-disposition” header is set to “attachment” which forces a download. As the client wanted to offer the option to also view directly in the browser these ( PDF ) files, I was faced with a problem.

Previewing the files without forcing a client download

The Amazon AWS documentation specifies an optional content-disposition parameter. So, for example, in a “getObjectUrl” call you would use:

$args = array(
    'ResponseContentDisposition' => 'attachment; filename=testing.pdf'
);
$request = $s3Client->getObjectUrl( $bucket, 'example/test.pdf', '+2 minutes', $args );

Those arguments get passed to the “getObject” function as explained here. The problem is that for some reason this parameter is ignored – probably because of the encryption.

I found a quick solution to this problem by downloading the file and serving directly from PHP on my server instead of trying to change the content-disposition on Amazon’s requests.

So in the WordPress plugin I added a simple function which takes a file and serves it with content-disposition inline:

add_action( 'wp_ajax_m_view_link', array( $this, 'view_pdf' ) );
function view_pdf() {
   if ( isset($_GET['url']) && is_user_logged_in() ) {
      header('Content-disposition: inline; filename='.basename($_GET['url']));
      header("Content-type:application/pdf");
      echo file_get_contents($_GET['url']);
   }
   die();
}

So now when calling /wp-admin/admin-ajax.php?action=m_view_link&url=url_of_amazon_file the users can preview the files directly in their browsers.

Notes:

To prevent potential security threats you should add some checks to the “view_pdf” function above.

Obviously you can use this for other purposes than Amazon S3 files it’s a quick way to change the content-disposition of certain external files.

Common WordPress caveats

This is an article which I will edit in the future with other common WordPress caveats so feel free to send suggestions.

Child_of vs parent in get_terms

Although this makes sense immediately after you read the documentation I ran into this issue. When getting custom taxonomy terms with the “get_terms” function you can use as argument either “child_of” or “parent”. The main difference between the two being that if the taxonomy is hierarchical ( like “Categories” ) using “parent” you will only get first descendants of that term. Using “child_of” you will get all the children and this can cause duplicate results. This is most likely to happen in a “related terms” situation.

tax_query array of arrays

It might be obvious to everybody by now but if you ever go crazy why tax_query or meta_query in a WP_Query isn’t working it could be that the format is wrong. So be careful to use an array of arrays, like this:

$args = array(
    'meta_query' => array(
        array(
            'key' => 'color',
            'value' => 'red'
        )
    )
);

Multisite max_upload_file_size

Creating a WordPress multi-site network you might encounter an issue with file uploads after the setup. Before going crazy changing all the php.ini settings and wp-config.php with no result make sure you check the value of “max_upload_file_size” in the network settings page.

That’s /wp-admin/network/settings.php in case you don’t know where to find it. Also note that it’s a value in kb – so don’t set it to something like 64.

Allow WordPress multi-site in sub-directories for existing websites

When trying to enable the WordPress multi-site you might get the following message in the setup:

“Because your install is not new, the sites in your WordPress network must use sub-domains. The main site in a sub-directory install will need to use a modified permalink structure, potentially breaking existing links.”

What this means is that there might be a conflict between existing pages/posts and new sites. So if you previously had a page with the slug “mobile” and after the setup you create a sub-site called “mobile” well…you see the problem.

The easiest way to avoid that restriction ( keeping in mind to avoid the slug conflict above ) is by adding the code below to an active plugin or theme.

add_filter( 'allow_subdirectory_install',
   create_function( '', 'return true;' )
);

After that, you can run the setup as for a new website, when you’re done don’t forget to remove the code.

WordPress admin check if content changed using Heartbeat

When creating a WordPress admin panel it is common to want to refresh the page based on real-time content changes. The most straightforward way of doing that is by hooking into the WP Heartbeat API.

Using the Heartbeat API you can have almost real-time updates to the admin content.

What is the WordPress Heartbeat API?

It was introduced in WordPress version 3.6 and basically it does a regular Ajax call. It does so in order to check if the user is still logged in or if someone else is editing a post. This way it can show you the whole “someone else is editing this post” screen which you can “Take over”.

The Ajax call is, by default, being triggered based on user activity and at a certain interval but we can also change that to a more “stable” call at a given interval.

But first, the data update!

When you are ready to update the data in your admin panel ( or another plugin’s admin panel ) there are 2 steps to follow:

  1. You add a piece of JavaScript that adds your custom variable
  2. You filter the value of “heartbeat_received” and populate the data which will also be used in the script you added above.

Here’s an example of doing that:

add_filter( 'heartbeat_received', 'm_add_heartbeat_data'), 10, 2 );
function m_add_heartbeat_data( $response, $data ) {
   if ( isset($data['daed_version']) && $data['daed_version']=='dashboard_editorial' ) {
      $response['daed_version'] = $this->get_version();
   }
   return $response;
}
add_action( 'admin_print_footer_scripts', 'm_heartbeat_footer_js');
function m_heartbeat_footer_js() {
   global $pagenow;
   // check the page you are on, maybe you don't want to run this on all the admin pages
   if( 'admin.php' != $pagenow )
      return;
   ?>
   <script>
      (function($){
         $(document).on('heartbeat-send', function(e, data) {
            // add your custom variable which will be populated in the ajax request
            data['m_sales'] = 'yes_send';
         });
         $(document).on( 'heartbeat-tick', function(e, data) {
            // don't run the script if the custom variable is not present
            if ( ! data['m_sales'] )
               return;
            if ( data['m_sales'] ) {
               $('#m_sales_count').text(data['m_sales']);
            }
         });
         // set the heartbeat interval - explained below
         wp.heartbeat.interval( 'slow' );
      }(jQuery));
   </script>
   <?php
}

If you’re asking yourself “why not add this in a separate js file?” – the answer is, because it will get cached and maybe you want to pass some variables to it.

Changing the interval

You can change the Heartbeat interval either by hooking into the “heartbeat_settings” filter using PHP and changing the “interval” value:

function m_heartbeat_settings( $settings ) {
   $settings['interval'] = 60; //only values between 15 and 60 seconds allowed
   return $settings;
}
add_filter( 'heartbeat_settings', 'm_heartbeat_settings' );

Or by using the JavaScript functions as I have done above using one of the 3 available values: fast, slow or standard.

// 5 seconds but no more than 2 and a half minutes
wp.heartbeat.interval( 'fast' );
// 60 seconds
wp.heartbeat.interval( 'slow' );
// 15 seconds
wp.heartbeat.interval( 'standard' );

 

Adding a custom table to the WordPress database

Many times in a WordPress plugin you want to store data in an easy accessible way without bloating the default WP tables. That’s when a custom database table comes in handy.

This isn’t something new just a way to do it clean and some warnings from troubles encountered in live environments.

The code

This is just a simple example of a database class which I include in custom plugins:

class M_Db {
    private static $db_version = '1.0.0';
    const DB_VERSION_NAME = 'm_db_version';
    const POSTS_TABLE = 'm_info';
    public static function install() {
        global $wpdb;
        $installed_ver = get_option(self::DB_VERSION_NAME);
        if ( $installed_ver != self::$db_version ) {
            $table_name = $wpdb->prefix . self::POSTS_TABLE;
            $charset_collate = $wpdb->get_charset_collate();
            $sql = "CREATE TABLE $table_name (
            id bigint(20) NOT NULL AUTO_INCREMENT,
            user_id bigint(20) DEFAULT 0 NOT NULL,
            region VARCHAR(50) NOT NULL,
               UNIQUE KEY id (id)
           ) $charset_collate;";
            require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
            dbDelta( $sql );
            update_option( self::DB_VERSION_NAME, self::$db_version );
        }
    }
}

This code will allow you to create a custom table and also easily update its structure using the version check. After a change is made ( like adding a new column ) you just need to change the static version and the table will be updated.

Of course, you don’t want to run this code each time the website is loaded so you’ll have to call the “install” function from the proper hook.

In a plugin ( and hopefully you won’t be creating custom tables with a theme! ) you should run this on plugin activation by adding this code to the main plugin file:

register_activation_hook( __FILE__, array( 'M_Db', 'install' ) )

Warnings

  • if you have a column for user_id – make sure you match its format to the WordPress wp_user ID format. You wouldn’t want user ids to get saved as the maximum value of an integer for all the users with a larger id number – this applies for any relation column.
  • make sure you consider adding a case for plugin deactivation if that’s necessary using register_deactivation_hook.
  • on the same note above – if the plugin will be present in a repository, make sure you handle updates properly.

WooCommerce Ajax add to cart custom redirect

If you want to redirect to a custom url after adding a product to cart you can achieve that pretty easily if you don’t have the Ajax functionality enabled. An example of that can be seen here.

What if you use Ajax

Using Ajax that’s a little bit harder to achieve but you can do that using by “fake” throwing an error as that is being handled directly by WooCommerce.

In the WooCommerce add-to-cart.js file you will see this piece of code:

if ( response.error && response.product_url ) {
   window.location = response.product_url;
   return;
}

You can use this to manipulate the behavior by hooking into “woocommerce_ajax_added_to_cart”.

add_action('woocommerce_ajax_added_to_cart', 'm_custom_redirect');
function m_custom_redirect( $product_id ) {
    // add your check if certain product should trigger the redirect
    if ( $product_id == 34 ) {
        // custom redirect url
        $custom_redirect_url = get_permalink(55);
        $data = array(
            'error'       => true,
            'product_url' => $custom_redirect_url
        );
        wp_send_json( $data );
        exit;
    }
}

This can be used in a variety of situations in which you want to suggest another product after a product has been added to the cart.

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.