How to Allow REST API Requests While Restricted Site Access is Active

If you’re developing a WordPress site and have Restricted Site Access enabled, but want to allow requests to the REST API without requiring authentication, here’s how to do that. Filter restricted_site_access_is_restricted, and check for the presence of the rest_route key in the query_vars property of the global WordPress object. Here’s the code packaged up as a plugin; just drop it in your plugins directory and activate.

<?php
/**
 * Plugin Name: Unrestrict REST API
 * Plugin URI: https://philipnewcomer.net/2016/05/allow-rest-api-restricted-site-access/
 * Description: Allows REST API requests while Restricted Site Access is enabled.
 * Version: 0.1.0
 * Author: Philip Newcomer
 * Author URI: https://philipnewcomer.net
 */

/**
 * Filter Restricted Site Access to allow REST API requests.
 *
 * @param bool   $is_restricted Whether access is restricted.
 * @param object $wp            The WordPress object.
 *
 * @return bool Whether access should be restricted.
 */
function pn_unrestrict_rest_api( $is_restricted, $wp ) {

    if ( ! empty( $wp->query_vars['rest_route'] ) ) {
        return false;
    }

    return $is_restricted;
}
add_filter( 'restricted_site_access_is_restricted', 'pn_unrestrict_rest_api', 10, 2 );

This is also available as a gist at https://gist.github.com/philipnewcomer/16a0aacff2e073eadc4d.

Note that at the time of this writing, 10up hasn’t updated the Restricted Site Access plugin on the wordpress.org plugin repository with the latest version which is compatible with the REST API; you’ll need to use the develop branch from the plugin’s GitHub repo until the repository is updated.

WordPress MU Domain Mapping in VVV

TLDR: After setting up WordPress MU Domain Mapping on local in the standard way, add the subsite’s desired top-level domains to your hosts file and modify the Nginx config for your main site to listen for those URLs. Then just edit the site and point the site URL to the desired top-level domain.

It’s not uncommon when a client has a network of sites running off of one Multisite install to use the WordPress MU Domain Mapping plugin in production to give each site its own top-level domain. Normally we can develop just fine on local using the subdirectory or subdomain multisite structure. However, there are some (rare) cases where we need to have domain mapping setup in our local development environments in order to debug some weird issue that can only be reproduced with domain mapping enabled, such as something related to site URLs. This document describes the steps to take to enable domain mapping in VVV, where each site in a multisite network will have its own top-level domain.

What we are aiming for is something like this:
http://multisite.dev/subsite1/ to be accessible at http://subsite1.dev
http://multisite.dev/subsite2/ to be accessible at http://subsite2.dev
http://multisite.dev/subsite3/ to be accessible at http://subsite3.dev
etc.

In most cases you’ll be working with a copy of the production site’s database, in which case you’ll just adjust the commands and examples given here to what is appropriate for your setup.

1. Installing WordPress MU Domain Mapping on the network

The following describes the standard procedure of setting up WordPress MU Domain Mapping, and is not specific to VVV. If you’ve already done this, skip ahead to section 2.

SSH into VVV and go to the site root directory
vagrant ssh
cd /srv/www/multisite/

Install the WordPress MU Domain Mapping plugin
wp plugin install wordpress-mu-domain-mapping --activate-network

Copy sunrise.php to the wp-content directory
cp htdocs/wp-content/plugins/wordpress-mu-domain-mapping/sunrise.php htdocs/wp-content/

Edit your wp-config.php file and add this line:
define( 'SUNRISE', 'on' );

Add the VVV IP address to the Domain Mapping settings
Go to Settings > Domain Mapping and add 192.168.50.4 as the “Server IP Address” setting. If for some reason your VVV IP address is different use that instead.

2. Modify your VVV Install to work with Domain Mapping

We need to do two things to make domain mapping work in VVV: modify the Nginx config file of the main site to listen for the subsite domains, and add the mapped domains to your hosts file.

1) Still inside VVV, etc the Nginx configuration file for the main site. For example:
sudo vim /etc/nginx/custom-sites/multisite.conf
In the server_name directive, add each of the subsite domains in addition to the main domain, for example:
server_name multisite.dev subsite1.dev subsite2.dev subsite3.dev;
Restart Nginx
sudo service nginx restart

2) Back on your local machine, edit your hosts file (/etc/hosts on Mac and Linux, C:\Windows\System32\Drivers\etc\hosts on Windows)
Add an entry pointing each subsite domain to VVV’s IP address (192.168.50.4), for example:
192.168.50.4 subsite1.dev subsite2.dev subsite3.dev

3. Change your subsite’s URLs to the desired mapped domains

Finally, you can change the URLs of your subsites to their own top-level domain.

In the Sites network admin screen, simply edit the settings of each site, and change its URL to the top-level domain you added to your hosts file and the Nginx config.
vvv-domain-mapping-edit-site

That’s it.

Better Human-friendly Time Ago PHP Function

Last month I posted about a Human-friendly Time Ago PHP Function. Well, I’ve come across another way of doing it. This method has less code duplication, but is harder to understand at first glance. I prefer this method though, as it seems a bit more elegant than the other method. I’ve refined the code from the Stack Overflow post, and here’s what I have now.

/**
 * Calculates a human-friendly string describing how long ago a timestamp occurred.
 *
 * @link http://stackoverflow.com/a/2916189/3923505
 *
 * @param int $timestamp The timestamp to check.
 * @param int $now       The current time reference point.
 *
 * @return string The time ago in a human-friendly format.
 *
 * @throws \Exception if the timestamp is in the future.
 */
function time_ago( $timestamp = 0, $now = 0 ) {

    // Set up an array of time intervals.
    $intervals = array(
        60 * 60 * 24 * 365 => 'year',
        60 * 60 * 24 * 30  => 'month',
        60 * 60 * 24 * 7   => 'week',
        60 * 60 * 24       => 'day',
        60 * 60            => 'hour',
        60                 => 'minute',
        1                  => 'second',
    );

    // Get the current time if a reference point has not been provided.
    if ( 0 === $now ) {
        $now = time();
    }

    // Make sure the timestamp to check predates the current time reference point.
    if ( $timestamp > $now ) {
        throw new \Exception( 'Timestamp postdates the current time reference point' );
    }

    // Calculate the time difference between the current time reference point and the timestamp we're comparing.
    $time_difference = (int) abs( $now - $timestamp );

    // Check the time difference against each item in our $intervals array. When we find an applicable interval,
    // calculate the amount of intervals represented by the the time difference and return it in a human-friendly
    // format.
    foreach ( $intervals as $interval => $label ) {

        // If the current interval is larger than our time difference, move on to the next smaller interval.
        if ( $time_difference < $interval ) {
            continue;
        }

        // Our time difference is smaller than the interval. Find the number of times our time difference will fit into
        // the interval.
        $time_difference_in_units = round( $time_difference / $interval );

        if ( $time_difference_in_units <= 1 ) {
            $time_ago = sprintf( 'one %s ago',
                $label
            );
        } else {
            $time_ago = sprintf( '%s %ss ago',
                $time_difference_in_units,
                $label
            );
        }

        return $time_ago;
    }
}

How to Delete all WordPress-generated Thumbnail Sizes

After changing the registered thumbnail sizes and regenerating your thumbnails a few times during theme development, you may have quite a few thumbnails left over from old images sizes that don’t match any of the currently registered sizes, but are just sitting there in your uploads directory taking up space. Here is a command which will delete all generated thumbnails, after which you can regenerate your thumbnails one final time and be left with only the sizes currently registered in the theme.

Navigate to your uploads directory, and run this command:
find . -name *-*x*.jpg | xargs rm -f

Note that this will delete ALL generated thumbnails, so only run this if that’s what you want. The originals will be unaffected, unless they are named like a thumbnail.

How to Make the Tab Key Indent Text in a Texarea

I recently had a situation where the user needed to be able to insert tabs into a texarea. Normally, when the cursor is inside of a textarea, pressing tab will move the cursor to the next field or button in the form. However, in this case, I needed to change that behavior and make the tab key insert a tab character into the textarea content instead. Here’s how I did it in raw Javascript, no jQuery needed.

Note: Doing this will break the accessibility of your page in a very bad way. Make sure you have a Very Good Reason to do this before implementing it, as it will make it impossible for users to tab out of the textarea.

/**
 * Hijack the tab key to insert the tab into the content rather than moving the cursor to the next field or button in
 * the form.
 */
document.getElementById( 'my-texarea' ).addEventListener( 'keydown', function( event ) {

    if ( event.keyCode === 9 ) {

        // Set up some variables. We need to know the current position of the cursor or selection.
        var selectionStartPos = this.selectionStart;
        var selectionEndPos   = this.selectionEnd;
        var oldContent        = this.value;

        // Set the new content.
        this.value = oldContent.substring( 0, selectionStartPos ) + "\t" + oldContent.substring( selectionEndPos );

        // Set the new cursor position (current position + 1 to account for the new tab character).
        this.selectionStart = this.selectionEnd = selectionStartPos + 1;

        // Prevent the default action (tabbing to the next field or control).
        event.preventDefault();
    }
});

Note: You will want to make sure this code runs after the DOM has been loaded, or it will not be able to access your textarea.

Human-friendly Time Ago PHP Function

Say you want to get the amount which has elapsed in a human-friendly format. Something like “27 minutes ago” or “3 days ago.” If you’re using WordPress, there is a handy function called human_time_diff() which does just that. But if you’re not using WordPress, here is a PHP function which will do the same.

Update: I’ve come across a slightly more elegant way of doing this.

/**
 * Generates a human-readable string describing how long ago a timestamp occurred.
 *
 * @param int $timestamp The timestamp to check.
 * @param int $now       The current time reference point.
 *
 * @return string The time ago in a human-friendly format.
 *
 * @throws Exception if the timestamp is in the future.
 */
function time_ago( $timestamp = 0, $now = 0 ) {

    // Set up our variables.
    $minute_in_seconds = 60;
    $hour_in_seconds   = $minute_in_seconds * 60;
    $day_in_seconds    = $hour_in_seconds * 24;
    $week_in_seconds   = $day_in_seconds * 7;
    $month_in_seconds  = $day_in_seconds * 30;
    $year_in_seconds   = $day_in_seconds * 365;

    // Get the current time if a reference point has not been provided.
    if ( 0 === $now ) {
        $now = time();
    }

    // Make sure the timestamp to check is in the past.
    if ( $timestamp > $now ) {
        throw new Exception( 'Timestamp is in the future' );
    }

    // Calculate the time difference between the current time reference point and the timestamp we're comparing.
    $time_difference = (int) abs( $now - $timestamp );

    // Calculate the time ago using the smallest applicable unit.
    if ( $time_difference < $hour_in_seconds ) {

        $difference_value = round( $time_difference / $minute_in_seconds );
        $difference_label = 'minute';

    } elseif ( $time_difference < $day_in_seconds ) {

        $difference_value = round( $time_difference / $hour_in_seconds );
        $difference_label = 'hour';

    } elseif ( $time_difference < $week_in_seconds ) {

        $difference_value = round( $time_difference / $day_in_seconds );
        $difference_label = 'day';

    } elseif ( $time_difference < $month_in_seconds ) {

        $difference_value = round( $time_difference / $week_in_seconds );
        $difference_label = 'minute';

    } elseif ( $time_difference < $year_in_seconds ) {

        $difference_value = round( $time_difference / $month_in_seconds );
        $difference_label = 'month';

    } else {

        $difference_value = round( $time_difference / $year_in_seconds );
        $difference_label = 'year';
    }

    if ( $difference_value <= 1 ) {
        $time_ago = sprintf( 'one %s ago',
            $difference_label
        );
    } else {
        $time_ago = sprintf( '%s %ss ago',
            $difference_value,
            $difference_label
        );
    }

    return $time_ago;
}

New Plugin Release: Customizer Framework

A lightweight and easy-to-use framework for the WordPress Customizer. Provides a simple and intuitive API for registering Customizer settings, including advanced control types. Automatically sanitizes settings based on the control type. Eliminates the tedious task of registering a setting, control, and sanitization function for each individual Customizer setting.

Homepage: https://philipnewcomer.net/wordpress-customizer-framework/
WordPress Plugin: http://wordpress.org/plugins/customizer-framework/
GitHub Project: http://github.com/philipnewcomer/Customizer-Framework