Half-Elf on Tech

Thoughts From a Professional Lesbian

Author: Ipstenu (Mika Epstein)

  • REST API On This Day

    REST API On This Day

    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.

    A Word About Evolution of Code

    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.

    On My Site (aka The Service)

    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;
    	}
    

    Output

    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.

    On Your Site (aka The Plugin)

    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.

  • JSON Rest API (In Peace)

    JSON Rest API (In Peace)

    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.

    Sketching Out The Concept

    To do this, I broke my concept down into the logical steps of what was needed.

    • Output the data
    • Create a JSON URL
    • Format the data there

    And no, I have no idea how to do any of that, except outputting the data.

    The Rest API

    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:

    • Namespace
    • Version
    • Route
    • Arguments

    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!

    The Code

    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.

    Calling the Data From YOUR Site!

    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!

  • REST API – Democratizing Reading

    REST API – Democratizing Reading

    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.

    Democratize Discovery

    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.

    Democratize Consuming

    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.

    Democratize Reading

    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.

    Democratize Publishing

    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:

    • Allow users to download a CSV with all the products on your site and their costs
    • Create a plugin that calls custom data, allowing others to have a widget with the data
    • Power a mobile app without loading your theme

    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.

  • It Has Been X Days Widget

    It Has Been X Days Widget

    Yesterday I talked about ordering posts by post meta in a weirdly unique situation. Today I’m going to tell you about an even weirder one.

    As I mentioned, what I’m working with is a list of all the dead characters recorded on a site. One of the aspects of this is to allow for us to show a widget that tells visitors how many days it’s been since the last death, and who it was. There are a lot of ways to do this, and one of them is manually updating, but no one likes this. If an event can be automated, everyone wins.

    Since I’d already done the lion’s share of the work, forking it over into a widget and grabbing the last (i.e. most recent) death was easier.

    The Code

    I broke this up into two classes because it’s habit. I have no other reason.

    class Dead {
    	public function __construct() {
    		add_action( 'widgets_init', array( $this, 'register_widget' ) );
    	}
    
    	public function register_widget() {
    		$this->widget = new Dead_Widget();
    		register_widget( $this->widget );
    	}
    
    	public static function dead_queers() {
    		// Get all our dead queers
    		$dead_chars_loop = new WP_Query( array(
    			'post_type'       => 'post_type_characters',
    			'posts_per_page'  => -1,
    			'meta_query'      => array(
    				array(
    					'key'     => 'chars_death_year',
    					'value'   => $thisyear,
    					'compare' => 'REGEXP',
    				),
    			),
    		) );
    		$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);
    
    		$diff = abs( time() - $last_death['died'] );
    		$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));
    
    		$days_since = '';
    		if ( $years != 0 ) $days_since  .= $years .' '. _n( 'year, ', 'years, ', $years );
    		if ( $months != 0 ) $days_since .= $months .' '. _n( 'month', 'months', $months );
    		if ( $years != 0 ) $days_since  .= ',';
    		if ( $months != 0 ) $days_since .= ' and ';
    		if ( $days != 0 ) $days_since   .= $days .' '. _n( 'day', 'days', $days );
    
    		$return = 'It has been <strong>'. $days_since .'</strong> since the last death: <a href="'.$last_death['url'].'">'.$last_death['name'].'</a> - '.date('F j, Y', $last_death['died'] );
    
    		echo $return;
    	}
    }
    new Dead();
    
    class Dead_Widget extends WP_Widget {
    
    	protected $defaults;
    
    	function __construct() {
    		$this->defaults = array(
    			'title'		=> 'Bury Your Dead',
    		);
    
    		$widget_ops = array(
    			'classname'   => 'dead-queer deadwidget',
    			'description' => 'Displays days since the last death',
    		);
    
    		$control_ops = array(
    			'id_base' => 'dead-person',
    		);
    
    		parent::__construct( 'dead-person', 'Days Since Last Dead', $widget_ops, $control_ops );
    	}
    
    	function widget( $args, $instance ) {
    		extract( $args );
    		$instance = wp_parse_args( (array) $instance, $this->defaults );
    
    		echo $before_widget;
    
    		if ( ! empty( $instance['title'] ) ) {
    			echo $before_title . apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ) . $after_title;
    		}
    
    		echo Dead::dead_chars();
    
    		echo $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' ) ); ?>">Title:</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
    	}
    }
    

    Which looks like this:

    Bury Your Queers

    An Explanation

    Some things to note.

    While I only allow for customizing the title, I’ve left my arguments as an array in order to let me extend it later. What if, for example, I want to display the featured image as well?

    I’ve also left in some ‘default’ calls to Genesis’ StudioPress theme with regards to the widget layout. That’s because I wrote this for a StudioPress theme.

    The whole reason the death year is an array is that, as I mentioned the other day, people may die multiple times. Insert Buffy singing “Hey I died twice” here. I have to get all the dates, pick the largest number (which is the most recent date, remember Unix timestamps are forever increasing), and save that one. Once I find the ultimate largest number, I know that’s my character to save. Problem is I have to go through them all to figure out who died last.

    I’m inordinately happy about getting to use array_slice() to grab the final item in an array, and ditto array_shift() to move the multidimensional array to a single dimension.

    Using array_shift() changed this:

    Array ( 
        [gina] => Array (
            [name] => Gina 
            [url] => https://example.dev/character/gina/ 
            [died] => 1481068800 
        )
    )
    

    to this:

    Array ( 
        [name] => Gina 
        [url] => https://example.dev/character/gina/ 
        [died] => 1481068800 
    ) 
    

    And getting to finally use _n() in a non-translation sense was fun.

    The reason the dead characters are in a function in the first class is that I plan to extend this code later on, maybe making a shortcode or a JSON API call. Who knows!

    Finally, I mentioned that there were two ways to list the dead characters. One was by a taxonomy of character clichés and the other was by the death date post meta. In this case, I opted to sort by the taxonomy because if someone has a death date but is not marked as dead, then they were dead and came back to life.

  • Display Posts, Ordered By Post Meta

    Display Posts, Ordered By Post Meta

    One of the things I’ve been working on over on my tv project was getting better lists of characters who died. Initially all that we recorded was the year. However we started getting the idea of a timeline and a calendar and a ‘this year in queer TV’ section, and I realized I needed more precise data.

    I’m going to skip the headache part here, but I changed the year dropdown to a calendar date field that stored the date as MM/DD/YYYY. While some calculations would have been easier with the time saved as a UNIX timestamp, having it be text let me search as text to collect all the death for a year. There were pros and cons to both sides of the coin of how to save the data. The other trick was I needed to save the data as an array. Just in case someone died twice.

    Suffice to say, here’s your scenario. I have meta data in the post containing the date of character death, and I want to display it in a specific way, not supported out of the box by the default queries.

    Display all the dead in a list ordered by date

    The simple part in WordPress is to get a loop of all posts in a type that have the meta for death. Using WP_Query I grabbed the array and instead of saving all the post’s data, I extracted the data I needed:

    • character name and URL
    • date of death
    • list of shows and their respective URLs

    This I turned into a very simple array that I can use in multiple situations:

    $dead_chars_loop = new WP_Query( array(
    	'post_type'       => 'post_type_characters',
    	'posts_per_page'  => -1,
    	'meta_query'      => array(
    		array(
    			'key'     => 'chars_death_year',
    			'value'   => $thisyear,
    			'compare' => 'REGEXP',
    		),
    	),
    ) );
    $dead_chars_query = wp_list_pluck( $dead_chars_loop->posts, 'ID' );
    
    foreach( $dead_chars_query as $dead_char ) {
    	$show_info = get_post_meta( $dead_char, 'chars_show', true );
    	$died = get_post_meta( $dead_char, 'chars_dead', true );
    
    	$output_array[$post_slug] = array(
    		'name'  => get_the_title( $dead_char ),
    		'url'   => get_the_permalink( $dead_char ),
    		'shows' => $show_info,
    		'died'  => $died,
    	);
    }
    

    This array is made by looping through those WP_Query results and, for each character, grabbing the post meta data for shows and death. The simple array $output_array contains only the data I need for display. So no post content, no author, nada. Just the name, the URL, the shows, and the death. That said, I’m omitting my insane extra loop for generating $show_info with its links because it deserves its own post. Suffice to say, it’s weird. Oh and $thisyear is a page variable based on the URL you visit. If you go to /this-year/2015/ it will populate as ‘2015’ and so on.

    Side note. Characters actually have both a taxonomy for being dead and a lost meta for the date. That was a happy accident that allowed me to search for everyone who had died, and perhaps wasn’t currently dead. The time traveling Sara Lance is jokingly called Schroedinger’s Bisexual, since she is both alive and dead at any one given point in time.

    Fix The Date

    Okay! With my simple array of data, I can output the list however I want, depending on the way I ordered the query. The problem is that the query has a limited number of possible sorts, and they all have to do with the post subject, the title, not random post meta. Plus my post meta was saved in a way that wasn’t easily sortable.

    To solve this, I converted the date into a Unix timestamp:

    // Date(s) character died
    $died_date = get_post_meta( $dead_char, 'chars_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 ) {
    	$died_year = substr($date, -4);
    	if ( $died_year == $thisyear ) {
    		$date_parse = date_parse_from_format( 'm/d/Y' , $date);
    		$died = mktime( $date_parse['hour'], $date_parse['minute'], $date_parse['second'], $date_parse['month'], $date_parse['day'], $date_parse['year'] );
    	}
    }
    

    I put this in the foreach( $dead_chars_query as $dead_char ) {...} section and it transformed my date from 01/01/2017 to it’s commiserate Unix timestamp.

    Fix The Order

    The next step was to re-order my array based on the value of the death time. I wanted to go from smallest value to largest, as Unix timestamps increase over time.

    For this I used uasort to order the entire array by the date:

    // Reorder all the dead to sort by DoD
    uasort($death_list_array, function($a, $b) {
    	return $a['died'] <=> $b['died'];
    });
    

    This produced my desired results. Excellent. But now I have the date in a non-human readable format!

    Fix It On output

    Thankfully making the date human readable wasn’t all that messy. Once I have my finished array, I output it and clean it up:

    <ul>
    	<?php
    	foreach ( $death_list_array as $dead ) {
    		echo '<li><a href="'.$dead['url'].'">'.$dead['name'].'</a> / '.$dead['shows'].' / '.date( 'd F', $dead['died']).' </li>';
    	}
    	?>
    </ul>
    

    Voila!

    This Year example for 2014

    An ordered list of dead characters.

  • A Case for REST API

    A Case for REST API

    WordPress 4.7.1 and 4.7 were vulnerable via the REST API. Any unauthenticated user could modify the content of any post or page on a site. Since the release of the information, a surprisingly large number of users failed to update to 4.7.2 and, thus, were hacked.

    I say surprisingly because WordPress enabled automatic updates quite a while ago (WordPress 3.7), which will automatically secure your WordPress install. There have been 18 automated releases since then (which is why we have 3.7.18) and the vast majority have addressed security in one way, shape, or form.

    But this post isn’t about the reasons why someone might need to disable the automatic updates. No, this is about the argument I saw stem from the vulnerability, whereby people said it was proof the REST API should be disabled by default.

    And to them I say “No.”

    The REST API Probably Has More Vulnerabilities

    Look, I’m not going to lie to you. The odds are high that the REST API, which is a very new feature, probably has some serious issues still. But, as my friend Helen pointed out to those arguing for it to be disabled by default.

    Why should this be treated differently from XML-RPC? Have you gone through the history of the XML-RPC setting?

    While the REST API may introduce new vulnerabilities to WordPress, and even ones that are horrifyingly accessible to non-logged in users, it is not the first nor the last feature WordPress has added that puts a site at risk. The API, being brand new and not used or tested as much as other aspects of WordPress, is going to be dangerous to exist, but realistically not more or less than any other part of WordPress.

    Go through the list of all WordPress security releases. A lot of issues have been fixed. This is just the first one for the REST API.

    In 2008, XML-RPX in pre-2.3.3 systems allowed remote attackers to edit posts of other blog users, if registration was enabled. That’s a long time ago, and since then the majority of the exploits have revolved around DDoS and pingbacks. But yes, back in the day, this new feature was also problematic.

    Do You Trust WordPress?

    Everyone makes mistakes. Every code has bugs. Everything is vulnerable. The question you have to ask is not “Why did WordPress let this happen?” nor “Why did they put me in danger?” but “Did they handle this well?”

    The heart of the matter is not if you trust WordPress not to screw up, but that you trust them to react responsibly, quickly, and with the appropriate concern. Some security issues are massive. Others are not. With over 26% of the internet using WordPress, fixing the security issues quickly and properly is a huge concern.

    So you have to ask “Do I trust WordPress to fix security issues?” And “Do I trust WordPress has my best interests, with regards to security, at heart?”

    I do, but it’s a question of risk.

    Risk = {si, λi, xi}

    My father, a risk analyst, asks three important questions:

    • What can go wrong?
    • How likely is it?
    • What are the consequences?

    Obviously a lot can go wrong. So much, in fact, that it can be impossible to wrap your head around the vastness of the possibilities and subsequent probabilities. Even just listing the things you think might go wrong is incomplete. You know you’re not thinking of everything, so you have to start with the big picture.

    Most of us aren’t capable of calculating the risk and reliability of WordPress. Those Six Sigma experts aren’t either. There’s no way I could actually explain well enough how to determine the risk of using WordPress. If I could, I’d start with asking people “What can go wrong if I disable automatic updates?” and “What can go wrong if I don’t disable automatic updates?”

    The likelihood of the REST API being vulnerable is a little higher than other aspects of WordPress core, but much lower than plugins and themes. The consequences are generally higher than that of plugins and themes, but there’s an extra factor you must consider.

    • How quickly can it be fixed?

    If you leave WordPress auto-updates on, then the answer is “As quickly as humanly possible.” And that, I feel, lowers the overall risk.

    Planned Disclosure

    To those arguing WordPress should have told everyone sooner, this is not the first time WordPress waited a while before announcing a security issue. Take, for example, The Case of the Trojan Emoji. That’s not a Sherlock Holmes mystery, that’s the true case of where WordPress hid a major security fix inside the additions of emoji support.

    There’s a big difference between the Trojan Emoji in 4.2 and the RESTless Backdoor we faced in 4.7, and that is the exploitability. The vulnerability fixed with emojis was hard to do. The one in the API was super easy. In both cases, however, the moment the vulnerability is disclosed, a ticking clock begins to see when people get hacked.

    Sometimes folks tell me I’m hyperbolic or exaggerating when I say ‘attacks will begin within minutes.’ I think that my claim was proven valid, considering how many sites were hacked, and how quickly it happened. Within days of the announcement,

    Remember, security is nuanced. It’s never as simple as “Hey, I found a vulnerability! Patch it!” It requires testing, validation, more testing, and more testing. A good patch doesn’t introduce worse problems (yes, that’s happened before).

    Rarely Used Code Is More Vulnerable

    I bring this guy up every time people argue why WordPress turns on features by default. The WordPress argument is that if they don’t turn it on, people won’t use it. The reason that matters can be found in the works of Herbert Hecht.

    Hecht wrote papers about rare conditions and their effect on software failures. The tl;dr summary for you is this: Rarely used code fails more often.

    Now, Hecht specially was referring to the early days of software use, which the REST API certainly falls under, but the basic philosophy is valid. The less the code is use, the more at risk it is, specifically because it’s used so rarely. People are less familiar with it, they don’t know how to fix it or even test it for all the possibilities, because they don’t yet know what they are.

    Are those reasons to not use the REST API? Not always. You have to consider the risk of using it for yourself. Will your site go down? How bad is that if it happens? Are you doing everything you could to protect yourself?

    Should You Disable The REST API?

    No. Unless you’re certain you’ll never use it, ever, leave it on. It will be fixed, and as long as you apply security patches promptly, you’ll be fine.