Half-Elf on Tech

Thoughts From a Professional Lesbian

Author: Ipstenu (Mika Epstein)

  • Why I Write About What I Code

    Why I Write About What I Code

    I was asked this the other day. Obviously sometimes I write about technology in general, or software I find and like, but a great deal of the posts here are about how I figure things out. And the reason I do that is, simply, it makes me a better writer and a better coder.

    Want to write better?

    There’s nothing that will make you a better write than writing. You will learn your voice, your tone, and your flavor of writing only if you write. It doesn’t matter if your writing is bad at first. By writing more and more and more you will only get better and better at the process, and more comfortable doing it.

    Getting into the habit of writing, where it’s an every day occurrence in your life, is imperative if you want to write better. It’s a talent, yes, but it’s also a skill. And if you don’t practice skills they get rusty. If they get too rusty, they break and you give up.

    Want to code better?

    The fastest way to get better at code is to read and review other people’s code and try to figure out how they did what they did. The reason I can continue to think as sharply as I do about plugin reviews is that I do it every day. Every. Single. Day. I look at 30 to 100 plugins, review the code as written by just as many developers, reverse engineer what they’ve done, and I start to understand better. I peer review people’s code, day in and day out.

    But nothing makes you a better code than coding. Obviously. And yet there’s one thing most people miss. You see, the critical review of your own code is absolutely necessary if you want to become a better coder. And in the absence of peer reviewed code, the best thing to do is rip it apart yourself.

    Can you explain your code?

    That’s it. That’s the magic. If you can explain your code, why you did what you did, why it does what it does, then you are at the step of critically reviewing your code. The number of times my code has improved because I’ve blogged about it is uncountable. As I write my post, I find myself typing “I used the function X because…” and I stop. Why did I use that function?

    It’s in the questioning of my own actions that I begin to understand my own internal logic. You know, the part of your brain your parents and teachers helped you form. Those early days of logic where you learned fire was hot and one plus one was two, you also developed your own style of thinking.

    Can you explain why?

    My father likes to tell me I used to do my math backwards, from left to right, before my school taught me otherwise. On occasion, I still do it that way because I want to look at my math from a different perspective. Talking about why I do that changes my understanding of the process. The solution was always the same, but the process of getting there is vastly different.

    When I talk about why I chose the path I did, I do more than just verbalize to myself what I’ve done, I teach someone else that there’s an answer and there’s a way to their answers as well. I’ve shown a path.

    I write to understand myself

    Above all else, I write to understand myself. Only by doing that can I improve at anything.

  • FacetWP and Genesis Pagination

    FacetWP and Genesis Pagination

    If you use StudioPress’ Genesis themes, you may be used to that pretty numerical pagination instead of ye olde previous and next buttons. And if you use FacetWP, you may have found out you need to use their pagination to make things work right.

    Thankfully this can be fixed thanks to two people’s previous work.

    Replace the Navigation

    First we look to Sridhar Katakam who came up with an elegant way to replace the normal Genesis post navigation. The code is nearly the same, I just single-lined the returns:

    // Replace Genesis' Pagination with FacetWP's.
    // Context: Posts page, all Archives and Search results page.
    // @author Sridhar Katakam - http://sridharkatakam.com/facetwp-genesis/
    add_action( 'loop_end', 'MYSITE_replace_genesis_pagination' );
    function MYSITE_replace_genesis_pagination() {
    	if ( ! ( is_home() || is_archive() || is_search() ) ) return;
    	remove_action( 'genesis_after_endwhile', 'genesis_posts_nav' );
    	add_action( 'genesis_after_endwhile', 'MYSITE_facet_posts_nav' );
    }
    function MYSITE_facet_posts_nav() {
    	echo facetwp_display( 'pager' );
    }
    

    The nice part about this is that if you’re not using FacetWP on that page, it reverts to the default pager. I’m not doing a check on if Facet exists because I have this in a giant block that does it for me. If you need to, though, the code would be this:

    if ( function_exists( 'facetwp_display' ) ) { 
        add_action( 'loop_end', 'MYSITE_replace_genesis_pagination' ); 
    }
    

    And yes, you should always check if the plugin is loaded before you break things.

    Style the Navigation

    The styling magic comes from a slight fork of Matt Gibbs’ code to remove pagination if there’s only one post:

    // Style pagination to look like Genesis
    // @author Matt Gibbs
    // https://gist.github.com/mgibbs189/69176ef41fa4e26d1419
    function MYSITE_facetwp_pager( $output, $params ) {
        $output = '<div class="archive-pagination pagination"><ul>';
        $page = (int) $params['page'];
        $total_pages = (int) $params['total_pages'];
    
        // Only show pagination when > 1 page
        if ( 1 < $total_pages ) {
    
            if ( 1 < $page ) {
                $output .= '<li><a class="facetwp-page" data-page="' . ( $page - 1 ) . '">&laquo; Previous Page</a></li>';
            }
            if ( 3 < $page ) {
                $output .= '<li><a class="facetwp-page first-page" data-page="1">1</a></li>';
                $output .= ' <span class="dots">…</span> ';
            }
            for ( $i = 2; $i > 0; $i-- ) {
                if ( 0 < ( $page - $i ) ) {
                    $output .= '<li><a class="facetwp-page" data-page="' . ($page - $i) . '">' . ($page - $i) . '</a></li>';
                }
            }
    
            // Current page
            $output .= '<li class="active" aria-label="Current page"><a class="facetwp-page active" data-page="' . $page . '">' . $page . '</a></li>';
    
            for ( $i = 1; $i <= 2; $i++ ) {
                if ( $total_pages >= ( $page + $i ) ) {
                    $output .= '<li><a class="facetwp-page" data-page="' . ($page + $i) . '">' . ($page + $i) . '</a></li>';
                }
            }
            if ( $total_pages > ( $page + 2 ) ) {
                $output .= ' <span class="dots">…</span> ';
                $output .= '<li><a class="facetwp-page last-page" data-page="' . $total_pages . '">' . $total_pages . '</a></li>';
            }
            if ( $page < $total_pages ) {
                $output .= '<li><a class="facetwp-page" data-page="' . ( $page + 1 ) . '">Next Page &raquo;</a></li>';
            }
        }
    
        $output .= '</ul></div>';
    
        return $output;
    }
    add_filter( 'facetwp_pager_html', 'MYSITE_facetwp_pager', 10, 2 );
    

    This is somewhat obvious. It adds in <li></li> around each page item, and also wraps the entire block in the default Genesis code of <div class="archive-pagination pagination"><ul></ul></div> to produce the default Genesis display.

  • Will You Help Me Sell My Plugin?

    Will You Help Me Sell My Plugin?

    I get asked this a lot. It comes with the territory, but people ask me to help them monetize their plugins all the time. And my answer is always the same.

    No

    As much as I am a strong advocate of people making money off of WordPress, and as much as I support plugin and theme devs in their work, I’m not out here to help you run your business. While I do spend time thinking of ways to get people to pay for services and software, I don’t prioritize it, and most of my ideas are just that. Ideas.

    Really what people are asking for is my ideas and my free work. And to that, I say no.

    Business Help Isn’t Free

    If you wanted to hire me to help, to look at your code and to assist you in coming up with business strategies, based on my experience in the WordPress world, that’s a different matter. That gets a ‘no’ because I don’t have the time to dedicate to that work. I have a full time job that I do like, and I have some volunteer work I enjoy, and I have a very addictive side project. Since I enjoy being married, I don’t take on extra work right now. I don’t need the money.

    But the point here, if you can’t tell, is that yes, I would expect you to pay me for my work.

    I’m No Good At Sales

    Of course, keep in mind the fact that I’m a terrible salesman. I don’t like exaggerating what a product can do, I don’t like even suggesting a lie. I downplay. And that’s because I don’t like it when people promise the moon and only deliver low Earth orbit. I want realistic goals and possibilities. Can you do anything with this plugin? Sure. But it comes at a cost and I feel people should know that cost.

    I’m Hard to be Bought

    Everyone may have a price, but my price is rarely money. I know this sounds weird, since I said I expect people to pay me for my work. You see, asking me to do you a favor for free doesn’t really happen. But also, asking me to do you a favor for pay won’t happen.

    And by this I mean reviews.

    I’ve been asked, many times, to review people’s themes and plugins and post about it here. And in general, I say no. I review the things I use and like because I use and like them. I’m driven by usability. If I like your ‘thing’ and I think people should hear about it, I’ll talk it up. If your thing is free or for sale, I don’t care. What I care is if your thing was what I needed and wanted, and I liked it.

    I Won’t Help You Sell

    That’s not my deal. It’s not my deal on this blog. I’ve never been bought off for a review, I’ve never been asked “Would you review this product of mine?” unless I’ve already been known to use it. And even then, I’ve told people “You don’t want me to review it. I like it, but you have some bad bugs.”

    I’m honest. I’m direct. I’m incurably truthful.

    You probably don’t want me to help you sell your stuff, but if I really like it, I may anyway.

  • CMB2, Select2, and Taxonomies

    CMB2, Select2, and Taxonomies

    I’m going to start with “This is not my best work.”

    In using CMB2, I have created situations where it’s smarter to have the ‘normal’ WordPress taxonomy fields changed. Oh sure, they work most of the time for most things, but most is not all. In my situation, I had some custom taxonomies that I did not want people adding to from the post-edit screen.

    To get around the issue, most of the taxonomies were drop-downs using the taxonomy_select field type. That let me control the display and have the drop-down be the terms they could add. Anyone with admin access could add more, of course, but they’d see special notes about that. It gave me control.

    The problem really arose when I had a multicheck list of terms to add. Yes, I had 1 to 20 terms that might be added. And while I could use taxonomy_multicheck to do that, it wasn’t perfect. It made the screen very large.

    Select2 is Better

    Select2 is a jQuery replacement for select boxes. Using it, you can make a simple dropdown where you can have a single (or even multiple) selections, but also it has a nice interface for multiple selections:

    Select2 example: a single and a multicheck

    That looks much nicer than a list or grid of 20 options. You click on the box and you get a dropdown:

    Select2 - Showing the dropdown

    Select2 and CMB2

    Thankfully there’s already a plugin/add-on for this with CMB2. Phil Wylie made cmb-field-select2 which I pulled into my site and it works quite well. Except… You can’t use it to save Taxonomy data properly!

    This is due to a lot of complicated things, and while my first instinct was to complain to myself that core CMB2 could do it, and thus so could everyone, I know it’s not that simple. All the effort CMB2 put into making that work is little short of phenomenal. It was hard and it’s complex and it’s outright weird. I looked at the code and backed away slowly.

    But that doesn’t mean it’s impossible. It’s just a little weird and it’s not my best work. But it does work.

    Show the Taxonomies

    The first step is that you have to make a function to convert the taxonomy to something that can be used in a selection box. Thankfully Phil already did this and his example code works:

    /**
     * Get a list of terms
     *
     * Generic function to return an array of taxonomy terms formatted for CMB2.
     * Simply pass in your get_terms arguments and get back a beautifully formatted
     * CMB2 options array.
     *
     * @param string|array $taxonomies Taxonomy name or list of Taxonomy names
     * @param  array|string $query_args Optional. Array or string of arguments to get terms
     * @return array CMB2 options array
     */
    function iweb_get_cmb_options_array_tax( $taxonomies, $query_args = '' ) {
    	$defaults = array(
    		'hide_empty' => false
    	);
    	$args = wp_parse_args( $query_args, $defaults );
    	$terms = get_terms( $taxonomies, $args );
    	$terms_array = array();
    	if ( ! empty( $terms ) ) {
    		foreach ( $terms as $term ) {
    			$terms_array[$term->term_id] = $term->name;
    		}
    	}
    	return $terms_array;
    }
    

    Next you call that in your CMB2 code:

    // Field: Genre
    $field_genre = $cmb_notes->add_field( array(
    	'name'              => 'Genre',
    	'desc'              => 'Subject matter.',
    	'id'                => 'theshows_genre',
    	'taxonomy'          => 'my_genres',
    	'type'              => 'pw_multiselect',
    	'select_all_button' => false,
    	'remove_default'    => 'true',
    	'options'           => iweb_get_cmb_options_array_tax( 'my_genres' ),
    	'attributes'        => array(
    		'placeholder' => 'What kind of show...'
    ),
    

    And now you can add taxonomy items via Select2. But … It doesn’t save the taxonomy data.

    Saving The Taxonomy Data

    This is the part of code I’m not thrilled about. You see, the code in the previous section adds a new postmeta field for theshows_genre with an array of the IDs added. And that’s it. That isn’t what I wanted. I certainly could use the postmeta data to generate the output, but I used Taxonomies for a reason. They’re incredibly useful.

    In order to save the data, I needed to take the content from the post meta and copy it into the values for saved taxonomies, but only sometimes. After kicking around the options, I decided that I would give priority to the postmeta, not the taxonomies. That would allow me to have them save the taxonomies all the time unless the post meta was empty.

    function select2_taxonomy_process( $post_id, $postmeta, $taxonomy ) {
    
    	$get_post_meta = get_post_meta( $post_id, $postmeta, true );
    	$get_the_terms = get_the_terms( $post_id, $taxonomy );
    
    	if ( is_array( $get_post_meta ) ) {
    		// If we already have the post meta, then we should set the terms
    		$get_post_meta   = array_map( 'intval', $get_post_meta );
    		$get_post_meta   = array_unique( $get_post_meta );
    		$set_the_terms = array();
    
    		foreach( $get_post_meta as $term_id ) {
    			$term = get_term_by( 'id' , $term_id, $taxonomy );
    			array_push( $set_the_terms, $term->slug );
    		}
    
    		wp_set_object_terms( $post_id, $set_the_terms , $taxonomy );
    
    	} elseif ( $get_the_terms && ! is_wp_error( $get_the_terms ) ) {
    		// If there's no post meta, we force the terms to be the default
    		$get_post_meta = array();
    		foreach( $get_the_terms as $term ) {
    			$term_id = $term->term_id;
    			array_push( $get_post_meta, $term_id );
    		}
    		update_post_meta( $post_id, $postmeta, $get_post_meta );
    	}
    
    }
    

    This is not perfect code. It’s not even very good code, I don’t think. I’m not happy that I had to break the terms out instead of just using the the content from $get_post_meta but for some reason, wp_set_object_terms() wasn’t happy with an array of terms. It was fine with the slugs, so that’s the way I went.

    The logic is basic. If there’s postmeta and it’s an array, it ‘wins.’ If it’s not, take the taxonomy data and push it into the term.

    Triggering The Save

    But how to trigger that code? And where and when?

    I wrote code for my custom post type that triggered a check every time the page was loaded. Which is why I don’t like it.

    add_action( 'init', 'select2_taxonomy_save' );
    function select2_taxonomy_save() {
    	// Force saving data to convert select2 saved data to a taxonomy
    	$post_id   = ( isset( $_GET['post'] ) )? $_GET['post'] : 0 ;
    	
    	if ( $post_id !== 0 && is_admin() ) {
    		$post_type = ( isset( $_GET['post_type'] ) )? $_GET['post_type'] : 0 ;
    		switch ( $post_type ) {
    			case 'post_type_shows':
    				LP_CMB2_Addons::select2_taxonomy_save( $post_id, 'theshows_tropes', 'my_tropes' );
    				LP_CMB2_Addons::select2_taxonomy_save( $post_id, 'theshows_tvgenre', 'my_genres' );	
    				break;
    		}
    	}
    }
    

    Obviously it’s not the best code out there. It runs too often, though at least it’s only on page loads.. It would be better if it only ran on save, however that had a problem with race conditions. I would end up with a case where the postmeta might still be blank. So having it run before the page loaded appeared to be my only hope. I also don’t like having the data stored twice, but there was a limit to how far I wanted to run with this.

    Pull requests welcome!

  • Front Facing Alerts on Posts

    Front Facing Alerts on Posts

    Once I wrote the back-end code to show admin alerts on posts, I decided to add in some front end magic.

    Since the original code was written to be called by hooking into the admin_notices action, the obvious solution was to hook into something on the front end. But where?

    Genesis

    I’m a fan of the Genesis themes. There’s a handy hook for placement called genesis_before_content_sidebar_wrap() and the call (based on last week’s code) is this:

    add_action( 'genesis_before_content_sidebar_wrap', 'MYSITE_admin_notices' );
    

    And immediately that outputs this below the menu:

    Output message above title

    Obviously there’s no CSS going on there, but that’s okay.

    Not Genesis

    That’s lovely but what if I don’t use Genesis?

    While a filter like add_filter( 'the_title', 'MYSITE_admin_notices', 10, 2 ); would work, it also calls it for every output of the title on a page. If you happen to be using a sidebar that links to your 5 most recent posts, it can get pretty messy.

    And the problem here is that WordPress doesn’t have standard template hooks like that. You can use get_header() but that puts it at the top of every page. There just isn’t a way to say “I want this above the title” and have it work universally on all themes. Bummer.

    That said, you can totally make the get_header() call work with a little CSS magic and a little extra if-checks.

    The CSS Code

    Remember our goal in life is to load as little as possible on pages, so the best thing is to check if this is even the right post type:

    add_action( 'wp_enqueue_scripts', 'MYSITE_wp_enqueue_scripts' );
    public function MYSITE_wp_enqueue_scripts( ) {
    	wp_register_style( 'cpt-shows-styles', plugins_url('shows.css', __FILE__ ) );
    
    	if( is_single() && get_post_type() == 'post_type_shows' ){
    		wp_enqueue_style( 'cpt-shows-styles' );
    	}
    }
    

    The actual CSS is up to you, but mine looks like this and is cribbed from WordPress core:

    .wrap .notice {
    	background: #fff;
    	border-left: 4px solid #fff;
    	-webkit-box-shadow: 0 1px 1px 0 rgba(0, 0, 0, .1);
    	box-shadow: 0 1px 1px 0 rgba(0, 0, 0, .1);
    	margin: 5px;
    	padding: 1px 12px;
    }
    
    .wrap .notice p {
    	margin: .5em 0;
    	padding: 2px;
    }
    
    .wrap .notice p:before {
    	margin-right: 6px;
    	vertical-align: bottom;
    }
    
    .wrap .notice-error {
    	border-left-color: #dc3232;
    }
    
    .wrap .notice-info {
    	border-left-color: #00a0d2;
    }
    
    .wrap .notice-warning {
    	border-left-color: #ffb900;
    }
    
    .wrap .notice-updated,
    .wrap .notice-success {
    	border-left-color: #46b450;
    }
    

    This basically reproduces the same CSS as you’d see on the Admin Dashboard.

    The PHP Code

    While we covered the brunt of the PHP code before, we need to make a few alterations to make sure that the output only shows on the right pages and to the right people.

    The first changes will be in this section:

    if ( $message ) {
    	printf( '<div class="notice %1$s"><p><span class="dashicons dashicons-%2$s"></span> %3$s</p></div>', esc_attr( $type ), esc_attr( $dashicon ), esc_html( $message ) );
    }
    

    While it’s possible to use is_admin() to check if a visitor is on the dashboard or not, this is going to show on both front and backend. That means a better choice is is_user_logged_in() so our code will look like if ( $message && is_user_logged_in() ) and now it only shows if you’re logged in.

    But. The code shows on every page. That meant the code showed up on the static front page because it had no code and just widgets. In order to make it only show up on the right pages, I put in a check for what was called when.

    Since I have multiple CPTs, I keep the code that is post type specific in a file that matches the name, and I have an extra file called all-cpts.php that calls common code that they all use. This helps me keep my code dry (Don’t Repeat Yourself). That makes it easy for me to use a switch based the post type and output the right code on the right pages:

    function MYSITE_CPT_admin_notices() {
    
    	if ( !get_post() ) return;
    
    	$message    = '';
    	$type       = 'updated';
    	$post       = get_post();
    
    	$content    = get_post_field( 'post_content', $post->ID );
    	$word_count = str_word_count( strip_tags( $content ) );
    
    	switch ( $post->post_type ) {
    		case 'post_type_shows':
    			$worthit = get_post_meta( $post->ID, 'shows_worthit_details', true );
    
    			if ( $worthit < '1' ) {
    				$type     = 'notice-info';
    				$message  = 'Is this show worth watching? We don\'t know. Halp!';
    				$dashicon = 'heart';
    
    				if ( $word_count < '100' ) {
    					$type     = 'notice-error';
    					$message  = 'We clearly know nothing about this show. Help!';
    					$dashicon = 'warning';
    				} elseif ( $word_count < '200' ) {
    					$type     = 'notice-warning';
    					$message  = 'This post is a stub. Please edit it and make it more awesome.';
    					$dashicon = 'info';
    				}
    			}
    			break;
    	}
    
    	if ( $message && is_user_logged_in() && ( is_single() || is_admin() ) ) {
    		printf( '<div class="wrap"><div class="notice %1$s"><p><span class="dashicons dashicons-%2$s"></span> %3$s</p></div></div>', esc_attr( $type ), esc_attr( $dashicon ), esc_html( $message ) );
    	}
    
    }
    

    Now the message shows up for logged in users only, and only on the intended pages.