Half-Elf on Tech

Thoughts From a Professional Lesbian

Author: Ipstenu (Mika Epstein)

  • WP-CLI Tables

    WP-CLI Tables

    I was working on an update to the Varnish plugin I’ve adopted, and one of the requested features was for more debugging tool. I’d added in a status page, but this plugin is also used by web hosts, and sometimes asking a customer “Can you go to this bad and send me the results?” is a time sink.

    So why not add in a command line tool?

    WP-CLI?

    I love WP-CLI. It’s a command line interface (CLI) for WordPress (WP) that lets you do most anything via the command line. You can install and activate plugins, update themes, even write posts and add users. But if you’re tech savvy, it’s also a great tool to automate and manage the minutia of WordPress maintenance drudgery.

    I’d already built out a basic varnish flush command (wp varnish purge) so adding on to it isn’t terribly difficult. But what was difficult was making the output what I wanted.

    Start With an Array

    No matter what you need an array of the correct format for this to work. I was already storing everything in an array I save in a variable called $results that looks like this:

    Array ( 
        [varnish] => Array ( 
            [icon] => awesome 
            [message] => Varnish is running properly and caching is happening. 
        ) 
        [remote_ip] => Array ( 
            [icon] => awesome 
            [message] => Your server IP setup looks good. 
        ) 
        [age] => Array ( 
            [icon] => good 
            [message] => Your site is returning proper "Age" headers. 
        )
    )
    

    I was initially doing this so I could loop and output all results with an icon and message on the status page, but translating this to wp-cli was a matter of taking the array and repurposing it.

    WP-CLI Tables

    In order to add in a table output to WP-CLI, you use the format_items function:

    WP_CLI\Utils\format_items( $format, $items, $headers );
    

    The $format value is taken from $assoc_args['format'] (I set mine to default to table if it’s not defined). The $items are your array, and the $headers are another array of what your headers are.

    This is the tricky part. You have to make sure your array of headers matches your array of items, and fulfills the output you desire. In order to do this, start with figuring out what you need to output.

    In my case, I wanted a name, a status (aka the icon), and the message. This means my array looks like this: $headers = array( 'name', 'status', 'message' )

    Rebuild The Array

    Once I sorted out what the format was like, based on the headers, I built the items array as follows:

    // Generate array
    foreach ( $results as $type => $content ) { 
    	$items[] = array(
    		'name'    => $type,
    		'status'  => ucwords( $content['icon'] ),
    		'message' => $content['message'],
    	);
    }
    

    Remember, my $results were already an array. I’m just making it look right here.

    Final Results

    How does it look? Like this:

    +-----------+---------+-------------------------------------------------------+
    | name      | status  | message                                               |
    +-----------+---------+-------------------------------------------------------+
    | varnish   | Awesome | Varnish is running properly and caching is happening. |
    | remote_ip | Awesome | Your server IP setup looks good.                      |
    | age       | Good    | Your site is returning proper "Age" headers.          |
    +-----------+---------+-------------------------------------------------------+
    

    And that is a nice tool for people to debug.

  • Shortcode Example: Reviews

    Shortcode Example: Reviews

    Sometimes people want to have reviews mean ‘people leave reviews on my work.’ But the other kind of reviews are the ones where I review other peoples’ works. And for that, I found it helps to have some kind of standard.

    Let’s say I’m reviewing a TV show for overall quality but also overall gayness. That is, I want to be able to write up a post and then, at the bottom, put up a shortcode to say “This show is really good but has no gay characters.” To do that, I made a list of the important factors to distill:

    • Name: The name of the TV show
    • Summary: A short, 140 character summary of the overall show.
    • Queer: A 1-5 rating of how queer the show is
    • Rating: A 1-3 scale (yes, meh, no) for how good the show is overall
    • Warning: Is there a trigger warning (or CW) to be aware of

    The intent is to make it easy for someone to scroll down and find what they want to watch. Right?

    The Code

    A bit of warning. There are two specific to my site bits of design in here. First are the icons. While I’ve generalized them for you as Emoji, keep in mind you probably want to have your own style here. Second, I’m using Bootstrap, so I’ve leveraged some of their default code. You’ll want to tweak the CSS.

    class TV_Shortcodes {
    
    	/**
    	 * Constructor
    	 */
    	public function __construct() {
    		add_action( 'init', array( $this, 'init' ) );
    		add_filter( 'widget_text', 'do_shortcode' );
    	}
    
    	/**
    	 * Init
    	 */
    	public function init() {
    		add_shortcode( 'review', array( $this, 'review' ) );
    	}
    
    	/**
    	 * Reviews.
    	 */
    	public function review( $atts ) {
    
    		$attributes = shortcode_atts( array(
    			'title'   => 'Coming Soon',
    			'summary' => 'Coming soon ...',
    			'queer'   => '3',
    			'rating'  => 'meh',
    			'warning' => 'none',
    		), $atts );
    
    		$queer = (float) $attributes['queer'];
    		$queer = ( $queer < 0 )? 0 : $queer;
    		$queer = ( $queer > 5 )? 5 : $queer;
    
    		$worth = ( in_array( $attributes['worth'], array( 'yes', 'no', 'meh' ) ) )? $attributes['worth'] : 'meh';
    		switch ( $worth ) {
    			case 'yes':
    				$worth_icon = '??';
    				$worth_color = 'success';
    				break;
    			case 'no':
    				$worth_icon  = '??';
    				$worth_color = 'danger';
    				break;
    			case 'meh':
    				$worth_icon  = '?';
    				$worth_color = 'warning';
    				break;
    		}
    
    		// Get proper triger warning data
    		$warning = '';
    		$trigger = ( in_array( $attributes['trigger'], array( 'high', 'medium', 'low' ) ) )? $attributes['trigger'] : 'none';
    
    		if ( $trigger != 'none' ) {
    			$warn_image    = '⚠️';
    			switch ( $trigger ) {
    				case 'high':
    					$warn_color = 'danger';
    					break;
    				case 'medium':
    					$warn_color = 'warning';
    					break;
    				case 'low':
    					$warn_color = 'info';
    					break;
    			}
    
    			$warning = '<span data-toggle="tooltip" aria-label="Warning - This show contains triggers" title="Warning - This show contains triggers"><button type="button" class="btn btn-' . $warn_color . '"><span class="screener screener-warn ' . $warn_color . '" role="img">' . $warn_image . '</span></button></span>';
    		}
    
    		$output = '<div class="bd-callout"><h5 id="' . esc_attr( $attributes['title'] ) . '">Screener Review on <em>' . esc_html( $attributes['title'] ) . '</em></h5>
    		<p>' . esc_html( $attributes['summary'] ) . '</p>
    		<p><span data-toggle="tooltip" aria-label="How good is this show for queers?" title="How good is this show for queers?"><button type="button" class="btn btn-dark">Queer Score: ' . $queer . '</button></span> <span data-toggle="tooltip" aria-label="Is this show worth watching? ' . ucfirst( $worth ) . '" title="Is this show worth watching? ' . ucfirst( $worth ) . '"><button type="button" class="btn btn-' . $worth_color . '">Worth It? <span role="img" class="screener screener-worthit ' . lcfirst( $worth ) . '">' . $worth_icon . '</span></button></span> ' . $warning . '</p>
    		</div>';
    
    		return $output;
    
    	}
    }
    new TV_Shortcodes();
    

    The Future

    I’m thinking about changing the scores from numbers to stars, and adding in a link if the show has been added to the site. But it being a shortcode, it’s reasonably extensible.

    Enjoy, and export to your own review sites.

  • Genesis Theme: Anonymize Posts

    Genesis Theme: Anonymize Posts

    Actually, I anonymize my pages.

    See, I have multiple authors on a site, and one of the things I like is to celebrate them when they post. Awesome. But I don’t want to highlight who wrote the pages on the site. Or who posted the videos. Those are informational and don’t need any ego attached.

    When using StudioPress’ Genesis theme, you can edit the post authors by filtering genesis_post_info

    The Code

    add_filter( 'genesis_post_info', 'EXAMPLE_genesis_post_info' );
    function EXAMPLE_genesis_post_info( $post_info = '' ) {
    	if ( is_singular( array ( 'videos', 'page' ) ) )
    		$post_info = 'By the Barbarians [post_edit]';
    	return $post_info;
    }
    

    Now all my posts are by Barbarians!

  • Light Fingered Fish

    Light Fingered Fish

    To explain the joke before we get too far, Jetpack’s contact form was originally called Grunion. The book “Memory” by Lois McMaster Bujold uses the phrase “light fingered fish” to talk about fish who elude hooks.

    I was building a site for my father and he wanted the contact form to redirect to another page. Thankfully you can do this with a filter on grunion_contact_form_redirect_url (see? Grunion? Fish?)

    The Code

    If you use the official code, then you’re going to need to know two things:

    1) What is the page ID you’re redirecting from
    2) What is the page slug you’re redirecting to

    Yes, it’s weird that you have to know those, but … well. That’s what we’ve got. I tried to come up with a reason why, and I think it’s just that searching for posts by slug is hard.

    function EXAMPLE_grunion_custom_form_redirect_url( $redirect, $id, $post_id ){
    
        $redirected_urls = array(
            '123' => home_url( 'contact' ),
            '456' => home_url( 'about' ),
            '789' => 'https://wordpress.org/surprise/',
        );
     
        foreach ( $redirected_urls as $source => $destination ) {
            if ( $id == $source ) {
                return $destination;
            }
        }
     
        // If there's no custom redirect, return the default
        return $redirect;
    }
    add_filter( 'grunion_contact_form_redirect_url', 'EXAMPLE_grunion_custom_form_redirect_url', 10, 3 );
    

    Buuuuut what if you wanted to do it by slug?

        $redirected_urls = array(
            'contact'  => home_url( 'contact-success' ),
            'about'    => home_url( 'about-success' ),
            'surprise' => 'https://wordpress.org/surprise/',
        );
     
        $slug = get_post_field( 'post_name', $id );
    
        foreach ( $redirected_urls as $source => $destination ) {
            if ( $slug == $source ) {
                return $destination;
            }
        }
    

    The benefit to this is you can change the post ID and, as long as it has the same slug, you’re good to go. Also let’s say you have a bunch of separate contact pages (contact-me, contact-mom and so on). You could use the logic to redirect all pages that have the word ‘contact’ or ‘about’ or ‘surprise’ …

        foreach ( $redirected_urls as $source => $destination ) {
            if ( strpos( $source, $slug ) !== false
                return $destination;
            }
        }
    
  • Types of Related Posts

    Types of Related Posts

    At it’s heart, related posts are the drive to help people find more content on your site. They serve no other purpose than keeping people on your site by piquing their interest in your words.

    But what if I told you there were multiple types of related posts for WordPress?

    Categories and Tags

    The first type of related posts are really just organization. I have three main categories on this site: How It Is, How It Works, and How To. I also have a million tags, like everyone else. If you wanted to read my thoughts on how things are, or rather why they are, you’d scroll through the category of “How It Works.” If you wanted to see everything I wrote about SVGs, you would check out my tag of ‘svg’ or possibly ‘images.’

    The point here is that categorization is a type of related posts. It’s entirely manual, but it’s the best way to say ‘These posts are like each other.’ And they have a fatal flaw. You see, if I wanted to read all the “How To” posts about SVGs, WordPress doesn’t easily cross relate. That is, I can’t list all the items in a category and a tag.

    Which is why we have …

    Related Posts Plugins

    There are two main types of plugins for this. There are the services, like Jetpack’s related posts, that scrape all your posts, toss them into a database, and use some complex algorithms to sort out what is and isn’t related. The other sort scan your posts locally and figure the same thing out.

    So which is better? Well. Jetpack requires you to trust Jetpack, or whatever service you pick, with your data. For some people, this can be a deal breaker. On the other hand, if you run it locally, you’re at the behest of how fast your site runs. For example, if it scans your posts live and you have, say, 300k posts, then that could be really slow. Or if it makes it’s own database table, how often is it going to update and cross relate?

    By the way, the 300k posts is not an exaggeration. That’s a site I looked at recently.

    Alertnative Relations

    There’s a secret third option, actually.

    I called it Semi Related Posts, and while I did it across post types, you could use the general logic. The concept is that instead of letting your site try and divine relations, you could manually connect them. It does require more upfront work, but cross relating posts by hand gives you the ultimate control.

    Of course, you’ll note that I did automate this as much as I could. I’m not crazy you know. If you can find a way to do that, maybe code a way to list 4 other posts in the same category and tags as this post, then you’ve automagically automated the simple.

    Until you hit that 300k post limit. Then you’ll have to rethink things again.

  • A Name is Not A Description

    A Name is Not A Description

    One day, you found a app or plugin or add-on for something. It was a feature you always wanted, did exactly what you needed, was well written and supported. It was that panacea of perfection. You loved it. Then you had a computer crash, or a house fire, or moved, and you forgot what the name was. All you could remember was the name was something about what it did. So you decided to Google for it, and quickly found a billion things that fit the bill.

    SEO vs Generic

    When you’re naming your product or company, you work very hard to think of a name that encapsulates what you are, what you do, and what makes you unique. For example, you don’t name yourself “Shoe Company” and expect people to be able to find you. With very few exceptions (and really only No Name comes to mind), if you want to stand out, you pick a good name where you are prominent.

    This directly relates to SEO, and people’s ability to find you. Ever used Apple Pages or Sheets and tried to Google something? Like “How do I make Pages Templates” perhaps. You often feel damn lucky when you get the right result immediately:

    A google search that has useful results!

    But you’re not Apple, are you? So if you named your product “Foods,” you’d probably have a devil of a time getting ranked so people could find you in search!

    Unique vs Memorable

    Take a look at WordPress. Pretend you’re looking for a slider plugin. Hush, just come with me here. Now. You remember a really cool slider plugin, but all you remember is it was named something like “Best Slider Plugin.” Yeah. You ain’t gonna find it. Probably ever. But what if you were looking for a lightbox plugin, and you remembered the name as “Foobox Lightbox” … Hang on a second. That’s one you’re going to be able to find. It has a unique name, but better than that, it has a memorable name!

    The only reason Apple Pages actually works is that Apple is huge and also the fact that most of us Google “Apple Pages whatever” and not just “Pages.” It’s the same with the Apple Watch. It’s nice they call it “Watch.” We call it the “iWatch” because we have to be able to find it, and they picked stupid generic names. Being Apple, they can get away with it.

    To their credit, the name is memorable. It’s not unique, but you will remember it. Even if you remember it as “That stupid Pages app Apple made.” You remember Microsoft Word, but you also will remember WordPerfect, and possibly WordStar. But if you listed four Twitter apps, could you remember what differentiates each one without looking? Definitely unique names, like Tweetbot and Twitterific, and certainly memorable, but in the wrong way.

    Names vs Descriptions

    Many people make a common mistake. They remember the tools they use on their computers, like “TextEdit” and “Notepad” and they think that in order to be found, the name must be short and descriptive. That’s why we get Notepad++ and iTerm. To an extent, this works. LastPass and OnePassword are going to be memorable and unique and descriptive names. But the longer a product, or suite exists, the more likely they are to corner a market and make it harder for the little people.

    Let’s go back to WordPress. You’ve made a great popup plugin and you want everyone to know it. There are roughly 500 plugins that use ‘popup’ or ‘popups’ as a tag. There are 2500 or so plugins that show up for a search on ‘popup’ in the directory. Besides the fact that you really should use the ‘popup’ tag in your plugin, there’s no way in the world you’re going to get your new popup plugin to the top of the list in a day.

    But … users don’t look for ‘popup’ or even ‘best popup plugin.’ They look for something else. “WordPress popup plugin with call to action on page exit.” They may simply that to “wordpress popup plugin call to action page exit” but they’re going to look for what they need. And they’re going to remember the plugin named “Wait Don’t Go! Popups” that has a nice plugin description of “Grab your visitors’ attention one more time before they leave your page forever.”

    Humans vs Robots

    Putting a million buzzwords in your product’s name, the description, and the URL aren’t ever going to make you popular. The only thing that does is bring people in the yard. If they see your website is fill with upsell and hyperbole, they’re going to walk right out again. If they see features and explanations and proof that you are, indeed, the bees knees, they’ll stay. If you have a catchy or unique name, they’ll remember and recommend you to their friends.

    And then, then you will be a success.