Half-Elf on Tech

Thoughts From a Professional Lesbian

Category: How To

  • 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.

  • When a Page is an Endpoint

    When a Page is an Endpoint

    In making a site with non-standard WordPress pages, I ran into a common problem. How do I make a page that calls the data? To put it in a different way, I needed a URL (say, domain.com/stats/) to show site statistics. A ‘virtual page’ if you will.

    The Easy Way

    The first way I did this was the ‘easy’ way, though I’m loathe to really call it that. I made a page in WP Admin called ‘stats’ and I set that page to use a custom page template that, instead of calling post content, called my stats code. Custom page templates are pretty powerful, and having the page be a page meant I could allow editors to write and edit the page all they wanted.

    But the downside is that making ‘sub’ pages gets messy, and what happens if someone changes the slug or deletes the page or changes the template? No. There had to be a better way that let me force generate the page.

    The ‘Better’ Way

    This brings me to custom endpoints.

    Now there is a huge problem with this, and it’s that if you want to make your custom virtual pages look like the rest of your WordPress site, it’s … messy. This is why BuddyPress generates pages automatically for you. It needs the pages. No matter what, I was going to have to make pages. But there is a distinct difference between 19 pages and 5.

    My (unique) situation was that I wanted to have a series of sub pages. That would let me make one page, for example, for ‘roles’ and then automagically generate pages for /role/regular/ and /role/guest/ and so on. And yet, this brings up the other major problem.

    You see, it would also mean the slugs /about/regular/ could exist. Obviously there are ways around it but the point to hammer home here is that endpoints are not meant for this. They’re meant for if you want a custom endpoint (hah) on every page of a type.

    The (Really) Better Way

    Thankfully, there’s a better way!

    Rewrite rules and custom query vars will let me do everything I wanted, with only one page for each item.

    The Setup

    I have five types of pages with ‘extra’ sub pages: newest, role, star, stats, and thumbs. Each one has a few accepted types, but I don’t need to really worry about that just yet, because how I handle it differs on each page (more on that in a second). First I made a list of all my pages, the ‘kind’ of extra they had, and their custom template. Because yes, each page has a custom template.

    • page slug: newest, extra: newtype, template: newtype.php
    • page slug: role, extra: roleype, template: roletype.php
    • page slug: star, extra: starcolor, template: starcolor.php

    You’ll notice I went with a theme there. It makes it easier to remember what’s what. Each template is heavily customized for the data within, since each page is wildly different from each other. The one check I make on every page though is that the ‘extra’ value matches what I think it should. For example, here’s the stats header:

    $statstype = ( isset($wp_query->query['statistics'] ) )? $wp_query->query['statistics'] : 'main' ;
    $validstat = array('death', 'characters', 'shows', 'lists', 'main');
    
    if ( !in_array( $statstype, $validstat ) ){
    	wp_redirect( get_site_url().'/stats/' , '301' );
    	exit;
    }
    

    That means if you go to /stats/humbug/ it redirects you back to the main stats page.

    Cool, right? So the question next is how did I get /stats/characters/ to work if there’s no commensurate page?

    The WordPress Way

    The answer is add_rewrite_rule and query_vars. I made an array with all my page slugs and their extra, which you’ll remember was the same name as the template file. This let me use a series of loops and checks so my code is simpler.

    My query arguments are this:

    $query_args = array(
    	'newest'	=> 'newtype',
    	'role'		=> 'roletype',
    	'star'		=> 'starcolor',
    	'stats'		=> 'statistics',
    	'thumbs'	=> 'thumbscore',
    		);
    

    Notice how that matches exactly what my design was above? That’s why I take the time to plan all this out.

    The next thing I did with all this was to set up my query variables. These loop through the query array and set up a variable for each one.

    add_action ('query_vars', 'helf_query_vars');
    function helf_query_vars($vars){
    	foreach ( $query_args as $argument ) {
    		$vars[] = $argument;
    	}
    	return $vars;
    }
    

    What that does is makes a URL like this work: http://example.com/?pagename=stats&statistics=death

    It also means this works: http://example.com/stats/?statistics=death

    Neither of those are particularly ‘pretty’ permalinks, though, are they? That means it’s time for add_rewrite_rule!

    foreach( $query_args as $slug => $query ) {
        add_rewrite_rule(
            '^'.$slug.'/([^/]+)/?$',
            'index.php?pagename='.$slug.'&'.$query.'=$matches[1]',
            'top'
        );
    }
    

    This code is actually in an init function, but what it does is make a custom rewrite rule so we can call the URL like this: http://example.com/stats/death/

    Which is what I want.

    The Whole Code

    The following is actually the code I use. Feel free to fork! I put it into a class and did some extra work to call the right templates on the right pages.

    class LWTVG_Query_Vars {
    
    	// Constant for the query arguments we allow
    	public $query_args = array();
    
    	/**
    	 * Construct
    	 * Runs the Code
    	 *
    	 * @since 1.0
    	 */
    	function __construct() {
    		add_action( 'init', array( $this, 'init' ) );
    
    		$this->query_args = array(
    			'newest'	=> 'newtype',
    			'role'		=> 'roletype',
    			'star'		=> 'starcolor',
    			'stats'		=> 'statistics',
    			'thumbs'	=> 'thumbscore',
    		);
    	}
    
    	/**
    	 * Main Plugin setup
    	 *
    	 * Adds actions, filters, etc. to WP
    	 *
    	 * @access public
    	 * @return void
    	 * @since 1.0
    	 */
    	function init() {
    		// Plugin requires permalink usage - Only setup handling if permalinks enabled
    		if ( get_option('permalink_structure') != '' ) {
    
    			// tell WP not to override
    			add_action ('query_vars', array($this, 'query_vars'));
    
    			foreach( $this->query_args as $slug => $query ) {
    			    add_rewrite_rule(
    			        '^'.$slug.'/([^/]+)/?$',
    			        'index.php?pagename='.$slug.'&'.$query.'=$matches[1]',
    			        'top'
    			    );
    			}
    
    			// add filter for page
    			add_filter( 'page_template', array( $this, 'page_template' ) );
    
    		} else {
    			add_action( 'admin_notices', array( $this, 'admin_notice_permalinks' ) );
    		}
    	}
    
    	/**
    	 * No Permalinks Notice
    	 *
    	 * @since 1.0
    	 */
    	public function admin_notice_permalinks() {
    		echo '<div class="error"><p><strong>Custom Query Vars</strong> require you to use custom permalinks.</p></div>';
    	}
    
    	/**
    	 * Add the query variables so WordPress won't override it
    	 *
    	 * @return $vars
    	 */
    	function query_vars($vars){
    		foreach ( $this->query_args as $argument ) {
    			$vars[] = $argument;
    		}
    		return $vars;
    	}
    
    	/**
    	 * Adds a custom template to the query queue.
    	 *
    	 * @return $templates
    	 */
    	function page_template($templates = ""){
    		global $wp_query, $post;
    
    		if ( array_key_exists( $post->post_name, $this->lez_query_args ) )
    			$the_template = $this->lez_query_args[$post->post_name].'.php';
    
    		foreach ( $this->lez_query_args as $argument ) {
    			if( isset( $wp_query->query[$argument] ) ) {
    				$templates = dirname( dirname( __FILE__ ) ) . '/page-templates/' . $the_template;
    			}
    		}
    
    		return $templates;
    	}
    
    }
    
    new LWTVG_Query_Vars();
    

    PS: Yes, I totally built it all out in endpoints before I got smarter.

  • Drop DES Ciphers

    Drop DES Ciphers

    My (former) coworker read my recent post about the forward secrecy and noted the following:

    @p0pr0ck5: you really ought to get rid of the DES ciphers too.

    As it happened I was looking into that!

    What Is DES?

    DES stands for Data Encryption Standard. It’s a symmetric-key algorithm using 56-bit keys, which means it applies its magic to a block of data at once, rather than one bit at a time.

    Back in the 1970s, The National Bureau of Standards (in the US) created DES because it occurred to them that secure data was a good idea. A bunch of brains were invited to meet their proposal for a standard, and of them IBM (yes, that IBM) came up with a winner.

    What’s the Problem?

    It’s 2017, not 1971, and data is bigger and more complex. First off, 56-bit keys are too small. AES, the current standard, uses 128-bit and 256-bit keys. But perhaps more importantly, we don’t do symmetric encryption anymore. Sending the keys over the same channel you’re going to encrypt is dangerous and easy to snipe.

    Back in the late 1990s, a big machine could break a DES key in 22 hours. Today, using a cloud network on Amazon, it could be done in seconds, and be worth it.

    Oh and if you’ve heard of 3DES and are wondering if that’s better, it’s not. Remember the massive Target hack in late 2013? A bunch of credit cards were stolen and it turned out Target stored their PINs in 3DES. So no. Not safe at all.

    Why Did You Have It?

    Because Windows XP is a sack of shit and up until last year, I had to support it.

    Seriously, that was it. Now that everyone I know who use some XP (and NT) are on modern OS’s (or blocked from the server… or dead) I don’t have to worry so much.

    How Do We Ditch It?

    Remember, these are the default chiphers:

    ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS
    

    In there you have the following: ECDHE-ECDSA-DES-CBC3-SHA, ECDHE-RSA-DES-CBC3-SHA, EDH-RSA-DES-CBC3-SHA, and DES-CBC3-SHA

    While I already have !DES in my cipher suite (as I posted a few days ago), I should remove it fully. But that also means in the Pre Main Include section, I need to change my value for SSLCipherSuite to match!

    When I tested, I noticed that I was still pulling a TLS suite with 3DES: TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA

    To fix that, I changed HIGH to +HIGH:+MEDIUM:-LOW and that gave me the following:

    ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:+HIGH:+MEDIUM:-LOW:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4:!DSS
    

    The -LOW is the magic sauce to say “Don’t use anonymous insecure ciphers.” when you’re on cPanel, you see.

  • Show Featured Images on Post List

    Show Featured Images on Post List

    Let’s say you want to be sure every single post, in ever single post type, has a featured image. And you know someone might forget but you don’t want to have to open up every damn post to see.

    There are a lot of answers to this problem, and I love that there’s a plugin that will add in a Featured Image Column that shows a teeny featured image. But it wasn’t what I wanted. It was over kill.

    All I needed was a simple check mark for if there was an image and an X if there wasn’t, and that would suit me fine. And I wanted the column to be small, without a lot of fuss or folderol. I wanted something simple:

    My Featured Image Column

    That gives me a fast overview of if everything is what I wanted and where I wanted.

    The Code

    The code itself is the most basic column code, with a little bit of magic to put it as the first column on the list. Since I want to show this on all post types, I used the generic functions.

    /*
     * Show mark if featured image is set
     *
     * @since 1.1
     */
    
    add_filter('manage_posts_columns', 'helf_fi_manage_posts_columns');
    function helf_fi_manage_posts_columns( $columns ) {
    	if ( !is_array( $columns ) ) $columns = array();
    	$new_columns = array();
    
    	foreach( $columns as $key => $title ) {
    		if ( $key == 'title' ) $new_columns['featured_image'] = '<span class="dashicons dashicons-camera"></span>';
    		$new_columns[$key] = $title;
    	}
    
    	return $new_columns;
    }
    
    add_action('manage_posts_custom_column', 'helf_fi_manage_posts_custom_column', 10, 2);
    function helf_fi_manage_posts_custom_column( $column_name, $post_ID ) {
        if ($column_name == 'featured_image') {
            $post_featured_image = helf_fi_manage_column_check( $post_ID );
            $output = '<span class="dashicons dashicons-no"></span>';
            if ( $post_featured_image && $post_featured_image == true ) $output = '<span class="dashicons dashicons-yes"></span>';
            echo $output;
        }
    }
    
    function helf_fi_manage_column_check( $post_ID ) {
        $post_thumbnail_id = get_post_thumbnail_id( $post_ID );
        $post_thumbnail_img = false;
        if ( $post_thumbnail_id ) $post_thumbnail_img = true;
    	return $post_thumbnail_img;
    }
    
    add_action( 'admin_print_scripts', 'helf_fi_admin_print_styles' );
    function helf_fi_admin_print_styles(){
    	echo '
    	<style>
    		th#featured_image,
    		td.featured_image.column-featured_image {
    			max-height: 25px;
    			max-width: 25px;
    			width: 25px;
    			color: #444;
    		}
    		td.featured_image span.dashicons-no {
    			color: #dc3232;
    		}
    		td.featured_image span.dashicons-yes {
    			color: #46b450;
    		}
    		div#screen-options-wrap.hidden span.dashicons-camera {
    			padding-top: 5px;
    		}
    	</style>';
    }
    

    Fork and enjoy!

  • Custom Colors with the Twenty Seventeen Theme

    Custom Colors with the Twenty Seventeen Theme

    In building out a site, I had a cause to use Twenty Seventeen, the new theme for WordPress. I’d tested it before, helping figure out the usability of features. This time I was using it ‘for real’ and as I often say, there’s nothing quite as impressive as ‘for real.’

    Overall, I still find Twenty Seventeen as easy to work with as any other theme I’ve used. Customizing a theme to look like how you want is incredibly weird as it’s always a unique experience. This is to be expected. Themes are difficult when we’re trying to guess what people want. They’re second only to search in complex usability. In my use, I found two places where I felt other themes did things better.

    Documentation

    While there is a technical document on how to theme with Twenty Seventeen there is no walk through. For example, when I use StudioPress’ Genesis themes, every single one comes with a walkthrough of “How to make the theme look like our demo!” Twenty Seventeen has the luxury of the new default content, but even then, it’s not the same as directions. I have to do trial and error to figure out things like how to change the ‘section’ images on the front page.

    Answer? Change the featured image. Of course. That was logical to me because I’m an experienced WordPress user. I can’t say it was logical to anyone else.

    A great deal of the theme makes sense contextually. By which I mean if you look at it, it all follows and you can suss out what’s next. But it’s not perfect. No theme is. I still think if a simple walkthrough doc existed, it would help a lot of first time WordPress users.

    Colors

    About a day into my project, I’d used Twenty Seventeen, ditched it for something else, wrote a lot of custom post type/taxonomy code, and then came back to Twenty Seventeen. By the time I did, I had a very clear-cut idea in my head about what I wanted the base color to be.

    I have to explain it like that because that’s pretty abnormal, I feel. Most people don’t go “I want to use #d1548e as my base color for links and stuff!” They go “I want pink!” The problem here is that if you look at the color tool for customizing colors, it’s a slider. And worse, it’s a slider without an override.

    Twenty Seventeen Custom Color Slider

    Now compare that to the picker you get for the Header text color:

    Twenty Seventeen Custom Color Picker for Header

    Right there I can go in and put my color in hex format and it uses it. Perfect.

    I can guess why they don’t have this for the custom color, though. Twenty Seventeen does something special with colors and instead of just saying “Links are pink and headers are magenta,” it uses saturation. This lets the theme create a dynamic color scheme based on your selection. Which is fucking awesome, right up until you’re me (or you try to use the exact same color schema twice).

    I want to stress that I do not feel this was a bad choice for the theme. Since the theme is going to use math to cleverly calculate out what the related colors should be for the theme, it’s genius to set the colors on a slider. This puts the concept in your head, when you move the slider, that the colors are relative to each other. It’s a perfect example of seamlessly introducing new users to a tool. It’s actually intuitive.

    How I ‘Fixed’ My Color Woe

    First I made a lot of jokes with my buddy James about how they would ‘hue the day’ for this one. Because thats how I roll. Then I dug into where the hell this was set at all. Like all WordPress settings, its saved in the database in the wp_options table, under theme_mods_twentyseventeen which has a value like this:

    a:5:{i:0;b:0;s:18:"nav_menu_locations";a:2:{s:3:"top";i:2;s:6:"social";i:3;}s:18:"custom_css_post_id";i:-1;s:11:"colorscheme";s:6:"custom";s:15:"colorscheme_hue";i:312;}'
    

    Yours may be longer. The important bit is here: s:15:"colorscheme_hue";i:312

    That number, 312, is the color hue! If you change it, it changes the colors. Once I knew that, I had to reverse engineer a hex code into a hue. To do that, I used workwithcolor.com. That site has a color picker, and if you put in the value you want (say d15483 it spits back a whole lot of information.

    The Hue Picker with a LOOOOT of information

    That part I circled, the 337, that’s the important part. I can now go into my database and change 312 to 337 and magically it works.

    But boy that sucks. Instead I used set_theme_mod() to fix it by putting this in my theme’s function:

    add_action( 'after_setup_theme', 'helf_after_setup_theme' ); 
    function helf_after_setup_theme() {
    	$hue = '337';	
    	if ( get_theme_mod('colorscheme_hue') !== $hue ) set_theme_mod( 'colorscheme_hue', '337' );
    }
    
    

    If I wanted to get fancy, I’d put in a real control for it, but this at least gets me started.

  • cPanel and Two-Factor Authentication

    cPanel and Two-Factor Authentication

    I’m talking a lot about security. There are reasons for that. If you’re not keeping your online behavior safe, you’re in for some headaches. Two-Factor Authentication (TFA or 2FA) is one of the better solutions as it protects you by requiring you to have a password and a physical object in order to log in.

    WebHost Manager (the admin part of a cPanel server) has this.

    Let’s Do This Thing!

    Go to WHM Home » Security Center » Configure Security Policies and check the box for Two-Factor Authentication.

    Set TFA for cPanel

    Next, go to Home » Security Center » Two-Factor Authentication and move the slider to enable:

    Enable TFA

    Click on the tab for “Manage My Account” and set up TFA for your root account.

    The Problem….

    Oh there’s a problem? Yeah, and it’s the age old problem of TFA. You can’t enforce it. I mean, you can’t turn it on for everyone and walk away singing the praises of your success. You have to turn it on as each user. That means they have to turn it on. You can’t even require it.

    None of that means you shouldn’t do it. Everyone should do use TFA for their server connections. I use it for my servers and for my Gmail accounts. Protect yourself.

    Extra Credit

    Did you know you could turn on TFA for APIs on your server as well? Yeah.