For the first time in over three years, there will not be a post about tech or tech adjacent items today.
Today, March 8, 2017, is International Women’s Day. And today I do no work.
Read more about A Day Without a Woman.
Thoughts From a Professional Lesbian

For the first time in over three years, there will not be a post about tech or tech adjacent items today.
Today, March 8, 2017, is International Women’s Day. And today I do no work.
Read more about A Day Without a Woman.

I am writing to inform you that I will not be working on March 8th.
The Women’s March organized this day in the same spirit of love and liberation that inspired the march on January 21, 2017. Today, A Day Without A Woman, is a recognition of the value that women of all backgrounds add to our socio-economic system. It is to raise awareness and remind us all of the pervasive and systemic gender-based inequalities that still exist within our society, from the wage gap, to vulnerability to discrimination, sexual harassment, and job insecurity.
I am one woman. On any given weekday, I read a thousand lines of code. I review between 30 and 60 plugins. I write at least 2,000 words on WordPress.
But not March 8th.
On that day, I will review no plugins.
On that day, I will answer no questions in the forums.
On that day, I will not work on WordPress.
I’m one person, and I do shy away from the political on this blog, but now, this year, this time is different.
I understand if you can’t, we can’t all afford to do this, we can’t all risk this. But I can. And I will. Because I am one person, but my family taught me to care about everyone, not just myself. I am one person, but together we are a nation. I am one person, but I am not alone.
So on March 8th, there will be no WordPress for me.
I hope you will stand in support of me, and any of my friends and colleagues. I hope that everyone will give women workers today off without retribution. I hope you will all stand with me and others.
I know this is a big ask, and I know a lot of you can’t. I understand that, I respect that, and I support that. We need to eat.
I am dedicated to my work. I am dedicated to WordPress and DreamHost. I respect them, the work we do, and their values. And I am grateful my company does support me when I chose to do these things. I am lucky and thankful and grateful I won’t be fired for taking this day off.
At an increasingly insecure time for the rights of women and other minority groups, it is important to me that I also stand for the value of equality.
I hope you, the people who read this, will support me in my decision. I hope you will join me if you can.

After making a simple REST API output that showed the date (and time since) the last death listed on a site, I had an idea. What if I could list everyone who died on a specific day? I had that data after all, so why not.
Over time, the code behind my “Bury Your Characters” plugin evolved a great deal. I went from unique function names to singletons to one aspect and now to two. The design changed a lot in the middle as well. I unpacked the parsing of data from being on the data driven site and moved that to the site running the plugin.
The evolution of all this was for a simple reason. I wanted the JSON output to be generated as fast as possible, grabbed, and parsed locally. If the data takes less time to generate on my end, then the speed becomes more of a factor of how fast can the end-user’s site run. That would allow more people to use my plugin.
A secondary bonus reason was that I hate repeating myself in code. If I only have one place to update my code that generates the list of dead characters, for example, then there are fewer odds I’ll screw up and only edit one of the six places it gets used.
This means that the code I posted last week doesn’t look at all like that anymore.
In a file called bury-your-queers.php (yes, this is for that site) I have a class called LWTV_BYQ_JSON.
In that class I have a constructor that calls the rest_api_init and that’s where I define my URLs. I already had the last-death set, so now I’ve added in two more for on-this-day:
public function rest_api_init() {
register_rest_route( 'lwtv/v1', '/last-death', array(
'methods' => 'GET',
'callback' => array( $this, 'last_death_rest_api_callback' ),
) );
register_rest_route( 'lwtv/v1', '/on-this-day/', array(
'methods' => 'GET',
'callback' => array( $this, 'on_this_day_rest_api_callback' ),
) );
register_rest_route( 'lwtv/v1', '/on-this-day/(?P<date>[\d]{2}-[\d]{2})', array(
'methods' => 'GET',
'callback' => array( $this, 'on_this_day_rest_api_callback' ),
) );
}
The reason I have two for my new route is that I wanted both the main URL and the sub URL to work. That is /lwtv/v1/on-this-day/ and /lwtv/v1/on-this-day/03-03/ both work. They both call the same callback which checks what was passed to it. If the params for date are empty, it assumes today:
public function on_this_day_rest_api_callback( $data ) {
$params = $data->get_params();
$this_day = ( $params['date'] !== '' )? $params['date'] : 'today';
$response = $this->on_this_day( $this_day );
return $response;
}
The callback grabs the function on_this_day(), passing the date through to it. Now this is the first BIG change. I pulled the code to generate the list of all the dead out of the function for last_death because it’s called twice. Now it calls LWTV_Loops::post_meta_query which is just a class for all my common loops that get reused a lot. They’re no faster than making the query the regular way, they’re just neater and tidier.
public static function on_this_day( $this_day = 'today' ) {
if ( $this_day == 'today' ) {
$this_day = date('m-d');
}
// Get all our dead queers
$dead_chars_loop = LWTV_Loops::post_meta_query( 'post_type_characters', 'lezchars_death_year', '', 'EXISTS' );
$dead_chars_query = wp_list_pluck( $dead_chars_loop->posts, 'ID' );
$death_list_array = self::list_of_dead_characters( $dead_chars_query, $dead_chars_loop );
$died_today_array = array();
foreach ( $death_list_array as $the_dead ) {
if ( $this_day == date('m-d', $the_dead['died'] ) ) {
$died_today_array[ $the_dead['slug'] ] = array(
'slug' => $the_dead['slug'],
'name' => $the_dead['name'],
'url' => $the_dead['url'],
'died' => date( 'Y', $the_dead['died'] ),
);
}
}
if ( empty( $died_today_array ) ) {
$died_today_array[ 'none' ] = array(
'slug' => 'none',
'name' => 'No One',
'url' => site_url( '/cliche/dead/' ),
'died' => date('m-d'),
);
}
$return = $died_today_array;
return $return;
}
And all that works to output this (if you pick 03-03):
{"alisa-davies":{"slug":"alisa-davies","name":"Alisa Davies","url":"https:\/\/lezwatchtv.com\/character\/alisa-davies\/","died":"2010"},"lexa":{"slug":"lexa","name":"Lexa","url":"https:\/\/lezwatchtv.com\/character\/lexa\/","died":"2016"}}
or this (if you pick a date where no one died):
{"none":{"slug":"none","name":"No One","url":"https:\/\/lezwatchtv.com\/cliche\/dead\/","died":"02-19"}}
Unlike what I did for ‘last death’, here I’m only reporting the year. The reason is that’s all I care about just now. I’m passing the date through, so I don’t need that information anymore.
The plugin for this has three classes. The first is the master class to build out the widgets and shortcodes and also to parse the data. The other two are for the widgets.
Parsing the data is what we’re going to talk about today.
The function is straightforward. You pass the date to the function, it grabs the data for the date (defaulting to ‘today’ if none is provided) and spit out the resulting data:
public static function on_this_day( $this_day = 'today' ) {
$echo_day = ( $this_day == 'today' )? time() : strtotime( date('Y').'-'.$this_day );
$json_day = ( $this_day == 'today' )? '' : $this_day.'/' ;
$request = wp_remote_get( 'https://lezwatchtv.com/wp-json/lwtv/v1/on-this-day/'.$json_day );
$response = wp_remote_retrieve_body( $request );
$response = json_decode($response, true);
$count = ( key($response) == 'none' )? 0 : count($response) ;
$how_many = __('no characters died!', 'bury-your-queers');
$the_dead = '';
if ( $count > 0 ) {
$how_many = sprintf( _n( '%s character died:', '%s queer female characters died:', $count, 'bury-your-queers' ), $count );
$the_dead = '<ul class="byq-otd">';
foreach ( $response as $dead_character ) {
$the_dead .= '<li><a href="'.$dead_character['url'].'">'.$dead_character['name'].'</a> - '.$dead_character['died'] .'</li>';
}
$the_dead .= '</ul>';
}
$onthisday = '<p>'. sprintf( __( 'On %s, %s', 'bury-your-queers'), date('F jS', $echo_day ), $how_many ).'</p>';
$return = $onthisday.$the_dead;
return $return;
}
I picked paragraphs and un-ordered lists, since I feel those would be easily formatted by most people.

This week I’ve been talking about my list of dead characters, which I needed to order by year and wanted to display the most recent death in a widget.
Part of my end goal with all this was a dream I had to let people have a widget on their own site where they could display the most recently dead character. There were other ideas I had, like a ‘this day in YEAR, Character X died.’ But for now, I wanted to start my delving into the JSON API with something more simple.
Because this is my first serious go at it.
To do this, I broke my concept down into the logical steps of what was needed.
And no, I have no idea how to do any of that, except outputting the data.
To initialize the Rest API, you have to call a function on init() and that’s where it defines the URL that will be called. There are three (or four) parts to a URL:
The version isn’t technically required, but in the interests of future proofing, it’s probably a good idea. And in this specific case, I don’t have any arguments I want to pass through. You’re going to get just the output of the last dead. In deciding that, I was able to determine the most sensible structure.
My namespace should be for my site, not just this ‘show the dead’ feature. This is not always going to be the case, but since I’m adding in what I presume will be the first of some APIs, it’s wise to name in a way that is forward thinking. Similarly, I need my route to be logically named, and in this case I’m showing the last death, so I called it last-death.
This makes my desired URL /wp-json/MYSITE/v1/last-death/ and the code is this:
public function rest_api_init() {
register_rest_route( 'MYSITE/v1', '/last-death', array(
'methods' => 'GET',
'callback' => array( $this, 'rest_api_callback' ),
) );
}
The callback code is what gets my data, and based on my original widget resulted in the page displaying the following:
"It has been <strong>2 months and 6 days<\/strong> since the last death: <a href=\"https:\/\/example.dev\/character\/gina\/\">Gina<\/a> - December 7, 2016"
But I didn’t want it to be formatted like that. Instead, I wanted the code to spit out an array. To do that with my existing setup, I rewrote the dead_char() function, taking out all the parts that generated the days since the last death and instead put that in a separate plugin. Now this one gives an API output:
{"name":"Gina","url":"https:\/\/example.dev\/character\/gina\/","died":1481068800,"since":"5792233"}
That has the added bonus of letting anyone who wants to call it on their own for whatever they want isn’t stuck with my design. Yay, open source!
My JSON code went into a class like this:
class MYSITE_Dead_Character_JSON {
/**
* Constructor
*/
public function __construct() {
add_action( 'init', array( $this, 'init') );
}
/**
* Init
*/
public function init() {
add_action( 'rest_api_init', array( $this, 'rest_api_init') );
}
/**
* Rest API init
*
* Creates the callback - /MYSITE/v1/last-death/
*/
public function rest_api_init() {
register_rest_route( 'lwtv/v1', '/last-death', array(
'methods' => 'GET',
'callback' => array( $this, 'last_death_rest_api_callback' ),
) );
}
/**
* Rest API Callback
*/
public function last_death_rest_api_callback( $data ) {
$response = $this->last_death();
return $response;
}
/**
* Generate List of Dead
*
* @return array with last dead character data
*/
public static function last_death() {
// Get all our dead queers
$dead_chars_loop = MYSITE_tax_query( 'post_type_characters' , 'cliches', 'slug', 'dead');
$dead_chars_query = wp_list_pluck( $dead_chars_loop->posts, 'ID' );
// List all queers and the year they died
if ( $dead_chars_loop->have_posts() ) {
$death_list_array = array();
// Loop through characters to build our list
foreach( $dead_chars_query as $dead_char ) {
// Date(s) character died
$died_date = get_post_meta( $dead_char, 'lezchars_death_year', true);
$died_date_array = array();
// For each death date, create an item in an array with the unix timestamp
foreach ( $died_date as $date ) {
$date_parse = date_parse_from_format( 'm/d/Y' , $date);
$died_date_array[] = mktime( $date_parse['hour'], $date_parse['minute'], $date_parse['second'], $date_parse['month'], $date_parse['day'], $date_parse['year'] );
}
// Grab the highest date (aka most recent)
$died = max( $died_date_array );
// Get the post slug
$post_slug = get_post_field( 'post_name', get_post( $dead_char ) );
// Add this character to the array
$death_list_array[$post_slug] = array(
'name' => get_the_title( $dead_char ),
'url' => get_the_permalink( $dead_char ),
'died' => $died,
);
}
// Reorder all the dead to sort by DoD
uasort($death_list_array, function($a, $b) {
return $a['died'] <=> $b['died'];
});
}
// Extract the last death
$last_death = array_slice($death_list_array, -1, 1, true);
$last_death = array_shift($last_death);
// Calculate the difference between then and now
$diff = abs( time() - $last_death['died'] );
$last_death['since'] = $diff;
$return = $last_death;
return $return;
}
}
new MYSITE_Dead_JSON();
You may notice that I don’t seem to have a loop, and I call this instead:
$dead_chars_loop = MYSITE_tax_query( 'post_type_characters' , 'cliches', 'slug', 'dead');
For various reasons, I reuse a lot of loop calls. To make my own theme and plugin more human readable, that function does the query. It looks like this:
function MYSITE_tax_query( $post_type, $taxonomy, $field, $term, $operator = 'IN' ) {
$query = new WP_Query ( array(
'post_type' => $post_type,
'posts_per_page' => -1,
'no_found_rows' => true,
'post_status' => array( 'publish', 'draft' ),
'tax_query' => array( array(
'taxonomy' => $taxonomy,
'field' => $field,
'terms' => $term,
'operator' => $operator,
),),
) );
wp_reset_query();
return $query;
}
Since I have to do a lot of odd calls for the statistics on that site, it became smarter to do it that way.
I made an endpoint! This is all well and good, but the new question is “How does someone else include this on their site?”
The answer there is they need a widget. And it’s a widget I’ve mostly already made! All I had to do was create a plugin that made the widget and instead of calling the loops locally, just call the API.
class Bury_Your_Dead {
public function __construct() {
add_action( 'widgets_init', array( $this, 'last_death_register_widget' ) );
}
public function last_death_register_widget() {
$this->widget = new BYD_Last_Death_Widget();
register_widget( $this->widget );
}
public static function last_death() {
$request = wp_remote_get( 'https://example.dev/wp-json/MYSITE/v1/last-death/' );
$response = wp_remote_retrieve_body( $request );
$response = json_decode($response, true);
$diff = $response['since'];
$years = floor($diff / (365*60*60*24));
$months = floor(($diff - $years * 365*60*60*24) / (30*60*60*24));
$days = floor(($diff - $years * 365*60*60*24 - $months*30*60*60*24)/ (60*60*24));
$since = '';
if ( $years != 0 ) $since .= sprintf( _n( '%s year, ', '%s years, ', $years, 'MYSITE' ), $years );
if ( $months != 0 ) $since .= sprintf( _n( '%s month', '%s months', $months, 'MYSITE' ), $months );
$since .= ( $years != 0 )? ', ' : ' ';
$since .= ( $months != 0 )? __('and ', 'MYSITE') : '';
if ( $days != 0 ) $since .= sprintf( _n( '%s day', '%s days', $days, 'MYSITE' ), $days );
$response['since'] = $since;
return $response;
}
}
new Bury_Your_Dead();
class BYD_Last_Death_Widget extends WP_Widget {
protected $defaults;
function __construct() {
$this->defaults = array(
'title' => __( 'The Most Recent Death', 'MYSITE' ),
);
$widget_ops = array(
'classname' => 'dead-character deadwidget',
'description' => __( 'Displays time since the last WLW death', 'MYSITE' ),
);
$control_ops = array(
'id_base' => 'lezwatch-dead-char',
);
parent::__construct( 'lezwatch-dead-char', __( 'The Latest Dead', 'MYSITE' ), $widget_ops, $control_ops );
}
function widget( $args, $instance ) {
extract( $args );
$instance = wp_parse_args( (array) $instance, $this->defaults );
echo $args['before_widget'];
if ( ! empty( $instance['title'] ) ) {
echo $args['before_title'] . apply_filters( 'widget_title', $instance['title'] ) . $args['after_title'];
}
$dead_character = Bury_Your_Dead::last_death();
echo sprintf( __('It has been %s since the last death', 'MYSITE'), '<strong>'.$dead_character['since'].'</strong>' );
echo ': <a href="'.$dead_character['url'].'">'.$dead_character['name'].'</a> - '.date('F j, Y', $dead_character['died'] );
echo $args['after_widget'];
}
function update( $new_instance, $old_instance ) {
$new_instance['title'] = strip_tags( $new_instance['title'] );
return $new_instance;
}
function form( $instance ) {
$instance = wp_parse_args( (array) $instance, $this->defaults );
?>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php _e( 'Title', 'MYSITE' ); ?>: </label>
<input type="text" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" value="<?php echo esc_attr( $instance['title'] ); ?>" class="widefat" />
</p>
<?php
}
}
Whew. That’s huge, I know! And you may have noticed I snuck some translation in there. Always look forward!

Democratize Publishing.
We say that a lot in WordPress. We boldly state that the goal of WordPress is to democratize publishing through Open Source, GPL software.
I’d like to turn that on it’s ear for a moment.
The purpose of the REST API is to democratize reading.
One of the myths of websites is that if you build it, people will come. If that was true, we’d never need plugins like Yoast SEO and we’d never need to self-promote. The reality is that we have to work hard and make our content good. WordPress helps us make it look good, and makes it easier to get it out there, and usually the rest is up to us.
There’s another aspect to discovery, though, and that is the ease of findability. We already address this in WordPress to a degree with RSS, but as much as I’d love to say that was that, it’s not. It’s 2017. People don’t want RSS, they want email and they want the information on social media immediately.
And? They want apps on their phones that alert them to new data.
Reading data with WordPress is easy. We go to a webpage, we see the data, we consume, we move on. With emails and social media, people usually have to click to come to your site and read. While this is good for your traffic, anytime a person has to click you’re running a risk they might not click. That’s why Tweetstorms are more consumable than blog posts. While they are far more transient, they’re accessible in an immediate way.
Making the consuming of data more direct, a more one-to-one relationship, improves the chances that someone will actually read the information. Much of this can be achieved with a good lede, but readers are well to aware of click-bait today. “She tried this blogging software. You won’t believe what happened next!”
Right. You get the point. In order to get people to read, you have to make it easier for them to read. Converting WordPress posts to something an app can read needs to be easier for the blogger, or they won’t be able to have success.
The REST API does just that.
It makes the discovery and consumption of your website easier by simplifying your output.
When you look at a website you see the design and the layout and the wonderful beauty. When an app reads your data, however, it doesn’t want or need any of that. An app needs the raw data. And the REST API does that. It outputs the data in a super basic and simple way. Furthermore, it lets you add on to this and output specific data in special ways.
You can publish your data in multiple ways. Today, we all know about the blogging aspect of WordPress. But here are some other ways you could share your data:
Could you do all that without the REST API? Of course.
Does the REST API make it easier and thus help you democratize faster? Yes. It does.
The REST API can change your life, if you’d only let it.