MailChimp form using API v3.0 and jQuery ajax

As per notification at the top of this page, all prior versions of the API will not be supported after 2016.

I created a simple HTML page for a dynamic MailChimp sign-up form using jQuery ajax. This means that your users can signup for your MailChimp list without leaving your page. Better than that, they will signup without a page refresh, as the jQuery and `ajax()` will dynamically update the page with the response from the MailChimp API server. This means that you can use jQuery animations to fade out the form, display an animated spinner while the user waits, and then fade in the message. With jQuery, and a little imagination, the possibilities are endless.

The PHP files are “hidden” in the background where the user never sees them yet the jQuery ajax can still access them invisibly. Even when your website is static HTML, without any PHP, this solution will work so that nobody will ever know PHP is being used in the background.

1) Download a PHP wrapper that supports API v3.0. As of this writing, there is nothing official listed in the latest MailChimp docs that supports v3.0, but several are listed on GitHub, so I selected this one.

2) Create the following PHP file, `store-address.php`, using your own API key and list ID, and then place it in the same directory as the wrapper from step one. Remember to follow the documentation for your wrapper, but they all seem fairly similar to this.

<?php // for MailChimp API v3.0

include('MailChimp.php');  // path to API wrapper downloaded from GitHub

use \DrewM\MailChimp\MailChimp;

function storeAddress() {

    $key        = "xxxxxxxxxxxxxxx-us1";
    $list_id    = "xxxxxx";

    $merge_vars = array(
        'FNAME'     => $_POST['fname'],
        'LNAME'     => $_POST['lname']
    );

    $mc = new MailChimp($key);

    // add the email to your list
    $result = $mc->post('/lists/'.$list_id.'/members', array(
            'email_address' => $_POST['email'],
            'merge_fields'  => $merge_vars,
            'status'        => 'pending'     // double opt-in
            // 'status'     => 'subscribed'  // single opt-in
        )
    );

    return json_encode($result);

}

// If being called via ajax, run the function, else fail

if ($_POST['ajax']) { 
    echo storeAddress(); // send the response back through Ajax
} else {
    echo 'Method not allowed - please ensure JavaScript is enabled in this browser';
}

3) Create your HTML/CSS/JavaScript(jQuery) form (It is not required to be on a PHP page, and the visitor will never see that PHP is being used in the background.)

The response is in JSON so you’ll have to handle it correctly.

Here is what my `index.html` file looks like:

<form id="signup" action="index.html" method="get">
    First Name: <input type="text" name="fname" id="fname" />
    Last Name: <input type="text" name="lname" id="lname" />
    email Address (required): <input type="email" name="email" id="email" />
    <input type="submit" id="SendButton" name="submit" value="Submit" />
</form>
<div id="message"></div>

<script src="jquery.min.js"></script>
<script>
$(document).ready(function() {
    $('#signup').submit(function() {
        $("#message").html("Adding your email address...");
        $.ajax({
            url: 'inc/store-address.php', // proper url to your "store-address.php" file
            type: 'POST', // <- IMPORTANT
            data: $('#signup').serialize() + '&ajax=true',
            success: function(msg) {
                var message = $.parseJSON(msg),
                    result = '';
                if (message.status === 'pending') { // success
                    result = 'Success!  Please click the confirmation link that will be emailed to you shortly.';
                } else { // error
                    result = 'Error: ' + message.detail;
                }
                $('#message').html(result); // display the message
            }
        });
        return false;
    });
});
</script>

CodeIgniter pagination configuration file seems pointless

In the CodeIgniter documentation:

Simply create a new file called `pagination.php`, add the `$config` array in that file. Then save the file in `application/config/pagination.php` and it will be used automatically. You will NOT need to use `$this->pagination->initialize()` if you save your preferences in a config file.

However, one of the pagination configurations is the `base_url`, which presumably is going to be unique in every instance of pagination. For instance, if you use pagination for a controller function named `yesterday` your `base_url` might be something like `/news/yesterday/pages/`. Then for a controller function named `today`, your `base_url` might be `/news/today/pages/`. Given this scenario, you’ll need settings common for both instances stored in your `pagination.php` file and unique settings declared at the time you create the pagination links.

Consider this code:

$config['base_url'] = '/news/yesterday/pages/';   // <- unique to this instance
$config['uri_segment'] = 4;   // <- depends on above setting

$this->load->library('pagination');
$this->pagination->initialize($config); 
$data['pagination'] = $this->pagination->create_links();

In older versions of CodeIgniter, this code worked in conjunction with a `pagination.php` config file. However, after upgrading my project to version 3.1.0, I discovered the styling of my pagination links were totally broken. The settings contained within `pagination.php` were suddenly being ignored. Apparently the `initialize()` function is resetting pagination to its defaults, disregarding the `pagination.php` file entirely, and only using the `$config` settings array passed into it.

I no longer see the use of having a `pagination.php` file for common settings. If one of the core principals of CodeIgniter is DRY (Do not Repeat Yourself), then `pagination.php` has been rendered useless in this regard. You’re going to have both common configuration settings for all pagination instances in your project along with settings that are unique to each instance. If having unique settings for multiple instances precludes being able to use `pagination.php`, then its own purpose is already defeated. In order to use `pagination.php`, every instance of pagination would need an identical URL structure, which makes it impossible to use on more than one Controller function.

Consider two scenarios that illustrate the issue:

1. Do not use `pagination.php` file – Declare all settings for each instance of pagination by constructing the `$config` array in every Controller function. Settings common to all instances of pagination will need to be repeated for each instance of pagination throughout your Controller functions. Does not adhere to DRY principles. Not ideal.

2. Use `pagination.php` file – All pagination settings are contained in a central location adhering to DRY principles. However, since `base_url` is also a pagination setting, you’ll be forced to use the same Base URL for all instances of pagination. Impossible.

Workaround:

1. Copy your common pagination settings into your custom configuration file contained within `application/config/` directory. If you don’t already have a custom configuration file, create and auto-load it. Assign the array to a single configuration item named `pagination`:

// pagination
$config['pagination']	= [
    'attributes'            => array('class' => 'page'),
    'full_tag_open'         => '<p class="pagination">',
    'full_tag_close'        => '</p>',
    'cur_tag_open'          => '&nbsp;<span class="page page-current">',
    'cur_tag_close'         => '</span>',
    'first_link'            => '&laquo;',
    'last_link'             => '&raquo;',
    'next_link'             => '&rsaquo;',
    'prev_link'             => '&lsaquo;',
    'num_links'             => 6,
    'use_page_numbers'      => TRUE,
    'per_page'              => 16,	// number of items per page
];

2. Access these common settings within your Controller by calling your new custom configuration item. Use a `foreach` to loop through the `pagination` settings array:

foreach ($this->config->item('pagination') as $key => $value)
{
    $config[$key] = $value;
}
$config['base_url'] = '/news/yesterday/pages/';   // <- unique to this instance
$config['uri_segment'] = 4;   // <- depends on above setting

$this->load->library('pagination');
$this->pagination->initialize($config); 
$data['pagination'] = $this->pagination->create_links();

One advantage of this method is that you can individually over-ride any specific setting by simply assigning it to the `$config` array anytime after your `foreach` loop.

foreach ($this->config->item('pagination') as $key => $value)
{
    $config[$key] = $value;
}
$config['first_link'] = '&larr;'; // <- overrides your custom setting
$config['base_url'] = '/news/yesterday/pages/';   // <- unique to this instance
$config['uri_segment'] = 4;   // <- depends on above setting

3. Delete the seemingly useless `pagination.php` file!

How to handle an expired CSRF token after a page is left open

I’m using CodeIgniter 2 along with the Ion Auth authorization system by Ben Edmunds.

After creating my project, I would sometimes get a CodeIgniter error upon certain login attempts but this error was intermittent.

The action you have requested is not allowed.

After some troubleshooting, it became apparent this error was caused by an invalid CSRF token. Why is the token invalid? Well, in CodeIgniter’s configuration file, it’s set to expire in 4 hours. So if you load your login page and allow it to sit there for 4 hours before attempting a login, the CSRF tokens will expire and this will generate the error message as above. Simply reloading the login page avoids any issues.

You can verify this error message for yourself by deleting the CSRF cookie after you load the login page.

A cleaner solution would be to redirect to a custom error page or to display a flash message. However, this solution is not as simple as it sounds because when you extend the CodeIgniter Security class, certain hook-points are not available and you cannot yet access CodeIgniter’s Super Object using `get_instance()`.

So when you extend the Security class, you’re limited to standard PHP. In this case, I’m using PHP `header()` to redirect the offending login page (or any form page) back to itself.

<?php
class MY_Security extends CI_Security {

    public function __construct()
    {
        parent::__construct();
    }

    public function csrf_show_error()
    {
        // show_error('The action you have requested is not allowed.');  // default code

        // force page "refresh" - redirect back to itself with sanitized URI for security
        // a page refresh restores the CSRF cookie to allow a subsequent login
        header('Location: ' . htmlspecialchars($_SERVER['REQUEST_URI']), TRUE, 200);
    }
}

This works fine except that the user gets a screen refresh without any indication why they have to enter their login credentials a seconds time.

I decided to make this a bit more user-friendly by adding another function into a Controller, in my case, the Ion Auth controller…

function csrf_redirect()
{
    $flash = 'Session cookie automatically reset due to expired browser session.&nbsp; Please try again.';
    $this->session->set_flashdata('message', $flash);
    redirect('/login', 'location');
}

As you can see, this function sets a flash message telling the user what happened and then redirects them to a fresh instance of the login page.

Session cookie automatically reset due to expired browser session. Please try again.

Instead of using PHP `header()` to redirect a page refresh, redirect to this new Ion Auth controller function at `/auth/csrf_redirect`.

<?php
class MY_Security extends CI_Security {

    public function __construct()
    {
        parent::__construct();
    }

    public function csrf_show_error()
    {
        // show_error('The action you have requested is not allowed.');  // default code

        // force redirect to the csrf_redirect function
        // this gives the user a useful message instructing them to login again
        // while the CSRF cookie is also refreshed to allow a new login
        header('Location: /auth/csrf_redirect', TRUE, 302);
    }
}

The minor downside to this method is that you are always redirected back to the login page rather than a refresh of whatever page/form you’re trying to submit. However, that should be a moot point, since the session cookie expires at nearly the same time as the CSRF cookie, you’d be redirected back to the same login page regardless. You may also not be requiring the user be logged in for your particular form, so please be aware and re-direct accordingly.

Extending the CodeIgniter Database Utility Class

You’d like to take advantage of CodeIgniter’s built-in database backup function as described here…

http://www.codeigniter.com/user_guide/database/utilities.html#backup

As you can see, only the `mysql` PHP database extension is supported. However, since the `mysql` PHP database extension has been deprecated, maybe you’re using another PHP database extension like `mysqli` instead. Now the problem is that you can no longer use CodeIgniter’s built-in database backup function without getting this error…

Unsupported feature of the database platform you are using.

The error simply means that if you use any PHP database extension besides `mysql`, there is no function included within any of CodeIgniter’s database drivers’ utility file for doing the backup.

No problem, we’ll just “extend” CodeIgniter’s database class.

Wrong! As per documentation,

Note: The Database classes can not be extended or replaced with your own classes.

Despite this limitation, there is a workaround below that will not involve editing CodeIgniter’s core system files.


Solution:
Instead, we’ll simply “extend” CodeIgniter’s Loader class. Within this custom Loader, we’ll only copy and slightly modify the `dbutil()` function. Study the function below and compare it to the original. I simply check for the existence of my custom utility driver file and load it in place of the default.

`application/core/MY_Loader.php` contains…

<?php class MY_Loader extends CI_Loader {

    public function dbutil()
    {
        // this function taken from CI v2.2.0
        // system/core/Loader.php
        // modified as indicated below

        if (! class_exists('CI_DB'))
        {
            $this->database();
        }

        $CI =& get_instance();

        // for backwards compatibility, load dbforge so we can extend dbutils off it
        // this use is deprecated and strongly discouraged
        $CI->load->dbforge();

        require_once(BASEPATH . 'database/DB_utility.php');

        // START custom >>

        // path of default db utility file
        $default_utility = BASEPATH . 'database/drivers/' . $CI->db->dbdriver . '/' . $CI->db->dbdriver . '_utility.php';

        // path of my custom db utility file
        $my_utility = APPPATH . 'libraries/MY_DB_' . $CI->db->dbdriver . '_utility.php';

        // set custom db utility file if it exists
        if (file_exists($my_utility))
        {
            $utility = $my_utility;
            $extend = 'MY_DB_';
        }
        else
        {
            $utility = $default_utility;
            $extend = 'CI_DB_';
        }

        // load db utility file
        require_once($utility);

        // set the class
        $class = $extend . $CI->db->dbdriver . '_utility';

        // << END custom

        $CI->dbutil = new $class();

    }

}

Now we need to find the database driver that we’re using. For `mysqli`, it should be located within `system/database/drivers/mysqli/mysqli_utility.php`. This file contains the `_backup()` function and you can see that it’s empty (“unsupported”).

Create an exact duplicate of this file; rename it `MY_DB_mysqli_utility.php` and place it here…

`application/libraries/MY_DB_mysqli_utility.php`

Everything within this file should remain the same as the original, except for the name of the `class` and the contents of the `_backup()` function…

<?php
    class MY_DB_mysqli_utility extends CI_DB_utility {
    
    // this code taken from CI v2.2.0
    // system/database/drivers/mysqli/mysqli_utility.php

    // everything in here is same as default mysqli_utility
    ....
    // EXCEPT the _backup() function contains my own code

    function _backup($params = array())
    {
        //  my custom working backup code
        ....

NOW you can put whatever code you see fit into this version of the `_backup()` function. I’m not going to tell you how to do this, but you could look inside of `system/database/drivers/mysql/mysql_utility.php` for some inspiration. You could also try the backup function suggested in this posting, which seems to be working.


The advantages to my technique are as follows…

– Updating the CodeIgniter system files leaves this solution intact.
– Changing the `dbdriver` setting in `config/database.php` will simply cause a fallback to the selected database utility driver. This solution is specific to one particular database driver (`mysqli`).
– Removing the `MY_Loader.php` file causes a fallback to the default database utility driver.
– Removing the `MY_DB_mysqli_utility.php` file causes a fallback to the default database utility driver.
– Update any other database utility driver by changing `mysqli` where-ever appropriate.

Note: This solution was successfully performed using CodeIgniter v2.2.0.

Briefly unavailable for scheduled maintenance. Check back in a minute.

Oh no! You get this message on every single page of your WordPress site including the Admin area.

Briefly unavailable for scheduled maintenance. Check back in a minute.

It’s likely you were doing an update of WordPress or a WordPress plugin/theme and something went wrong or maybe you interrupted the process by closing your browser window.

Simply login to your server and in the root of your WordPress directory you’ll see a file called `.maintenance`. Delete it… yes, just delete the file called `.maintenance`.

Now your site is back up and you’ll be able to access the WordPress Admin area again. Just remember that when you’re doing a WordPress software updates, wait for the confirmation screen before clicking on something else or leaving the page.

Facebook Like Box is blank/empty in some browsers

Recently, I discovered that my custom Facebook Like Box on my website was totally empty or blank, but only within all versions of Internet Explorer and still working fine in certain other browsers. (I run all my versions of Explorer in a virtualization environment.)

At first, I thought that I overlooked something in my code. After rearranging some code and inspecting the DOM, I still could not see any problems, but yet the Like Box remained invisible.

Then I thought that Facebook updated their Like Box code making mine obsolete, so I updated it to the latest version… still invisible.

Finally, I found a thread on StackOverflow that had the solution. Apparently, if you have any age or country restrictions on your Facebook Page, the Like Box will remain invisible unless you’re logged into Facebook in that particular browser. Sort of makes sense, since Facebook cannot validate your age or country unless you’re logged in.

The solution is simple, remove all age and country restrictions from your Facebook Page forcing the Like Box to always appear on your website.

It seems weird that they give you this tool, but yet there’s no graceful degradation for the very common situation where a visitor may not be logged into Facebook. Rather than breaking your website layout, it seems that they could have put a placeholder or some other generic content into the Like Box for any unauthenticated visitors.

When CodeIgniter’s CSRF Protection breaks your Ajax

CSRF stands for “Cross Site Request Forgery” and if you’re using forms on your site, you’ll probably want to protect yourself and users against this kind of attack.

You just finished your latest PHP project using the CodeIgniter framework and decide to enable the CSRF protection option in your `config.php` file.

$config['csrf_protection']  = TRUE;  // <- set to TRUE
$config['csrf_token_name']  = 'csrf_token';   // <- name this whatever
$config['csrf_cookie_name'] = 'csrf_cookie';  // <- name this whatever
$config['csrf_expire'] = 7200;  // <- default is two hours

Enabling it within `config.php` is not enough. You also need to use the form helper `form_open()` function to construct the form's HTML markup. This function constructs the form so that it contains a `<input type="hidden">` element containing the CSRF token value. If the submitted form data is missing this token, it will not submit.

Now CSRF is working but you discover that your jQuery ajax requests are all suddenly failing with a type 500 server error. This is a direct result of activating the CSRF Protection option in CodeIgniter. As just explained, the submitted form data must contain the CSRF token, but it's missing from your ajax requests.

The solution is simple. You need to make sure that your ajax requests simulate a regular form submission by including the CSRF token value within the submitted data.

There are two types of solutions:

Solution #1:

This only works if your ajax requests occur when a form is already constructed on the page, such as when doing remote validation to check to see if a password or username already exists.

You'll need to copy the value from the hidden field called `csrf_token` (the name is exactly as per your `$config['csrf_token_name']` option setting) and send this along with your ajax request.

var csrf = $('input[name="csrf_token"]').val();  // <- get token value from hidden form input

$.ajax({
    // your other ajax options,
    data: {  // submit token value with YOUR token name
        csrf_token: csrf
    }
});

Solution #2:

This works for all ajax requests, even when you do not have a form on the page, such as remotely loading some content.

In this case, you can't get the CSRF token from a hidden field, since there is no form. You must retrieve it from the CSRF cookie. I'm using a jQuery cookie plugin.

var csrf = $.cookie('csrf_cookie');  // <- get token value from cookie

$.ajax({
    // your other ajax options,
    data: {  // submit token value with YOUR token name
        csrf_token: csrf
    }
});

Notice how the ajax in both solutions is sending the token with the same name, that's your name as per your configuration, `csrf_token`. Only the source of the token value is different... Solution #1 gets the token value from the hidden field, where Solution #2 gets the same token value from the cookie.

You can only use Solution #1 when you have a form on the page constructed with the `form_open()` function. However, you can use Solution #2 with or without a form, in all cases.

NOTES:

I have CodeIgniter v2.1.4 and by default, the `$config['csrf_token_name']` option is set to `csrf_test_name`. This mismatch might get a little confusing, but you can use whatever naming convention you wish. In my solution above, I changed it to `csrf_token`.

  • To retrieve the current token from the hidden input, use the name assigned to the `$config['csrf_token_name']` option.
  • To retrieve the current token from the cookie, use the name assigned to the `$config['csrf_cookie_name']` option.

No matter how you retrieve the token value, the important thing to remember is to always send the token value along with whatever name you've assigned to the `$config['csrf_token_name']` option.

Point & Click Geolocating on your own Google Map

There are many ways to find the latitude/longitude coordinates on a Google Map:

  • You can create a map at Google and pull the coordinates of the center point from the query string of the permalink.
  • You can right-click on the map at Google, go to “What’s Here” which creates a marker, then when you hover that marker, you’ll see the geographical coordinates.
  • You can use Google Earth, which shows the coordinates in the corner of the screen as you move the cursor around.

No matter how easy these various techniques might seem, getting the Latitude/Longitude of a larger number of map points can become quite tedious.

Let’s say you’re using Google Maps JavaScript API to create an embedded map for your website and you need to draw a line or polygon through a long series of map coordinates. Now what?

Easy fix. During map development, this chunk of JavaScript integrated into your own Google Map JavaScript will give you an info window containing the coordinates wherever you click.

google.maps.event.addListener(map, 'click', function (event) {
    new google.maps.InfoWindow({
        position: event.latLng,
        content: event.latLng.toString()
    }).open(map);
});

As long as your map object is named `map`, you’re all set. Of course, if you have a polygon on the map already, you may have trouble clicking on it. In that case, change `map` to the name of your polygon object and continue.

The info window will stay open until you close it which is handy for mapping out a line of coordinates… just cut & paste each lat/long into the JavaScript that makes your lines and polygons.

Once you’re done creating your custom Google map, simply remove this piece of code. (unless you want your website visitors to have access to this great feature too.)

Working DEMO: http://jsfiddle.net/X7vHG/

WordPress turning “next” into “previous”

One of my favorite WordPress themes is Twenty Thirteen and I’ve been using it as the parent of my child themes lately. However, on my most recent project, Illinois Doberman Rescue Plus, I discovered a weird little thing with how this theme’s archive pagination function is operating. (Also true for this website‘s theme. However, I’m keeping it here as my post sorting is always chronological descending.)

No matter how you sort your postings, when you get to the bottom of the page, the link that is supposed to take you to the next page of posts is labeled “Older posts”, it’s in the lower-left corner and pointing towards the left (just like the browser’s “back” button). This only makes sense for one narrow case, when the newest post is at the top of page 1. (Even then I could argue that the link to next page of results should not be displayed like a “back” button.)

← Older posts

So for cases when the sorting is different depending on the type of archive page, let’s simply change the “Older posts” and “Newer posts” labels to something more generic… but to what? Hmm, how about “Next page” and “Previous page”? Yes, that makes sense. Err wait, the link pointing to the next page (page 2) is down in the lower-left corner and pointing to the left (just like our browser’s “back” button). This is getting goofy… a link called “Next page” but it’s pointing backwards.

← Next page

I suppose we could redesign all the templates and re-work the CSS but I think we need to take a closer peek at the workings of the `twentythirteen_paging_nav()` function which is located in Twenty Thirteen’s `functions.php` file. This function is called by the various archive template files to automatically create these pagination links as needed. Ignore the “older/newer” labels for now.

<div class="nav-links">

    <?php if ( get_next_posts_link() ) : ?>
        <div class="nav-previous">
            <?php next_posts_link( __( '<span class="meta-nav">&larr;</span> Older posts', 'twentythirteen' ) ); ?>
        </div>
    <?php endif; ?>

    <?php if ( get_previous_posts_link() ) : ?>
        <div class="nav-next">
            <?php previous_posts_link( __( 'Newer posts <span class="meta-nav">&rarr;</span>', 'twentythirteen' ) ); ?>
        </div>
    <?php endif; ?>

</div><!-- .nav-links -->

Did you catch that? They’ve place the `next_posts_link()` function inside of a `div` with the `nav-previous` class. And conversely, the `previous_posts_link()` function is contained within the `nav-next` class. This certainly explains a lot.

In defense of Twenty Thirteen, the context of the newest post being on top, the “next” page would be “older” posts. In this theme, the link to the “next” page is labeled “Older posts” and it’s pointing to the left signifying “back”. I call it “goofy” because it will break when posts are sorted in any other fashion. When oldest posts are on top or when they’re sorted alphabetically, etc., the “older” label is rendered totally meaningless. Since it, technically, always goes to the next page of the results query, it should be labeled as “next” and indeed the `next_posts_link()` function is how it’s created. However, simply re-labeling it as “next” is not good enough as it’s in the lower-left corner and pointing backwards towards the left.

My fix is simple. I copied this entire function into my child theme’s `functions.php` file. Since the original is wrapped inside `if ( ! function_exists( ‘twentythirteen_paging_nav’ ) )`, the version in the child theme will take precedence. Then I rearranged the function a bit…

<div class="nav-links">

    <?php if ( get_next_posts_link() ) : ?>
        <div class="nav-next">
            <?php next_posts_link( __( 'Next page <span class="meta-nav">→</span>', 'twentythirteen' ) ); ?>
        </div>
    <?php endif; ?>

    <?php if ( get_previous_posts_link() ) : ?>
        <div class="nav-previous">
            <?php previous_posts_link( __( '<span class="meta-nav">←</span> Previous page', 'twentythirteen' ) ); ?>
        </div>
    <?php endif; ?>

</div><!-- .nav-links -->

Now using this slightly modified version, I get a “Next page” link that always points to the right and it always goes to the next page in the results. The link in the lower-left corner is labeled as “Previous page” and always goes to the previous page in the results. This is the most logical way as that it breaks all dependance on the sorting order.

                                               Next page →

As you can see my proposed arrangement perfectly corresponds to more traditional numbered pagination with “previous” on the left and “next” on the right…

← Prev      1  2  3  4  5  6  7  8      Next →

You’ll still need to adjust your CSS a bit as Twenty Thirteen makes the “previous” link in the lower-left corner (formally called “Older posts”) about 60% larger than its mate. I think their idea was to make the button going to the next page larger. Since their positions and labels are flipped, you must adjust the CSS sizes.

Since you’re over-riding the parent theme, you’ll need to over-ride everything dealing with spacing and size. The following is the bare minimum required in the child theme to flip the sizes to correspond with our previous changes.

.paging-navigation .nav-previous {
	padding: 13px 0;
}
.paging-navigation .nav-next {
	padding: 0;
}
.paging-navigation .nav-previous .meta-nav {
	padding: 3px 0 8px;
	width: 50px;
}
.paging-navigation .nav-next .meta-nav {
	padding: 17px 0 23px;
	width: 80px;
}