Half-Elf on Tech

Thoughts From a Professional Lesbian

Tag: wordpress

  • Review: Ninja Forms 3

    Review: Ninja Forms 3

    I wanted to have this out last week, in time for their release of Ninja Forms 3, but with WordPress 4.6.1 dropping the day before and me breaking the site I use Ninja Forms on, well let’s just say my week ran away with me. See the previous post about how lesbians eat data for more.

    Anyway. Ninja Forms 3. It took these guys a year to get it all right, and from where I sit, they did an amazing job. They managed to upgrade with minimal loss of data. And since we’re talking about forms, that’s a huge deal. You see, when you do a major overhaul of how forms are built, you’re changing how the data is stored. You’re also (possibly) changing the shortcakes in the posts where the forms are located.

    Making matters ‘worse’ for Ninja Forms, they have add-ons. Now they have to make sure these hundreds of add ons will work with the new version of Ninja Forms, and you can bet that not every developer will be responsive. I know that one for a fact.

    What they did was nothing short of phenomenal. You see, they ended up making two plugins and a migrator, wrapped it all up into one plugin, and released it. I cannot stress how incredibly hard that is. And they did it right and well.

    Here’s how it worked. I had version 2.9.x and I pressed that happy upgrade button to go to version 3. Nothing changed. I had an alert telling me that “3 is coming!!” which I knew, so I clicked on the link and was taken to a page asking me if I wanted to upgrade. Hold on a second. I was asked to use the new version.

    Right then and there I realized they’d not just included some 2.9.x stuff in the plugin as a fall back, no no. They had two plugins. That’s twice the work, but more to the point, they had an upgrader and a downgrade in there. I clicked upgrade, migrated my forms, and that was it. From the visitor’s end, nothing had changed, and that’s how we like it.

    For me though, using it was a mind trip. The very first thing I noticed was that it took up my whole screen. Now I’m not a fan of that in general. I want WordPress to look like WordPress. I want access to all my things so I can right click and open a link in a new tab and multitask. And worse, most full screen WordPress tools don’t look like WordPress. The disparity of those kinds of changes bothers me a lot.

    Shockingly, Ninja Forms felt like WordPress. It looked and felt like everything else in my dashboard. I’ve only seen this a few times, like with WooCommerce, where the ‘non’ WordPressy pages were still WordPress. This is hugely important to me. The more we make a consistent user environment for WordPress, the better we make the entire WordPress experience for our users.

    I hadn’t messed much with Ninja Forms since I first set it up but now I had all sorts of fun things to play with. The drag and drop interface was as slick as it looked in the videos. This has reinvigorated my interest in things and I’ll be adding some more custom forms to the site soon.

    Okay, so what don’t I like?

    The colors don’t match my site. This is so random, I know, but I like to use different WP Admin themes for different sites. This site is purple, another is blue, and the one using Ninja Forms is 80s Kids – bright blue and pink. I wish that Ninja Forms picked up my admin colors and used that to make their interface.

    I’m disappointed about tabbing as well. I can’t press tab and go between fields when I’m editing a form. I try to use my mouse as little as possible when I enter data. If I’m typing in information, like ‘term name’ and ‘value,’ then I want to press tab to jump to the next field.

    Finally, and this is going to sound weird, I don’t like that the form editor makes the rest of their settings pages look plain. They did such a kick ass job, I look at the ‘regular’ settings and think they missed something. I know, it’s petty.

  • Lesbians Eat Data

    Lesbians Eat Data

    The original title was “Lesbians Broke Jetpack” but it turned out to be even more complicated than all that. And thankfully more rare.

    This concerns three things.

    1) The website lezwatchtv.com
    2) Jetpack for WordPress
    3) ElasticSearch

    On Sept 6th, Jetpack released a new version – 4.3 – and I promptly upgraded. When I did, I started getting weird emails from my server of a “Suspicious process running under user lezwatchtv” and the content looked like this:

    Executable:
    
    /usr/bin/php-cgi
    
    Command Line (often faked in exploits):
    
    /usr/bin/php-cgi /home/lezwatchtv/public_html/wp-cron.php
    
    
    Network connections by the process (if any):
    
    tcp: MYIP:39734 -> JETPACKIP:443
    

    Being a proper code-nerd, I backed out a few things and tried again. Same error. I went into my process watcher and saw five processes calling wp-cron.php for that domain, but no others on the server. I killed the processes and turned off WordPress cron. Everything was fine. Then I installed WP Crontrol and manually kicked off cron jobs until it happened again.

    The culprit was a ‘runs everyone one minute’ job by Jetpack, which struck me as bewildering.

    		if ( ! wp_next_scheduled( 'jetpack_sync_cron' ) ) {
    			// Schedule a job to send pending queue items once a minute
    			wp_schedule_event( time(), '1min', 'jetpack_sync_cron' );
    		}
    

    The sync job is meant to update your data on Jetpack’s servers, which makes sense, and running every minute will copy up everything that changed in each minute. It seemed a little heavy to me, and disabling it stopped my run-away cron jobs. That meant the sync was failing. I reached out to a Jetpack tech and explained the situation. He re-ran the sync manually and it stalled.

    We determined the likely issue was that the job was, for some reason, hanging and unable to finish, so it would just stay active forever. And ever. And since it would see that the sync had never done, it would start up all over again until, finally, my server killed the five (yes, five) processes and sent me an angry text about it. Yes, my server texts me.

    At this point I emailed support with full details and got a very insightful reply from Brandon Kraft:

    I’m interested in if there’s an issue with the server connecting with WP.com (seems unlikely given your other sites sound fine), if there’s a large amount of postmeta or something like that that is throwing a wrench into the system, or something to that effect. We’ve isolated some odd cases where when there is either a lot of postmeta or something yet undetermined in postmeta breaks things in a way similar to what you saw.

    DING!

    See there are 40 posts, 22 pages and then 1246 Custom Post posts on LezWatchTV.

    906 posts are ‘characters’ and all characters have three separate taxonomies, two plain text post-meta values, and two serialized. 340 posts are ‘shows’ with two taxonomies, three plain text post-meta values, three integer (plain text) post-meta values, one true/false, six HTML, and one serialized data.

    So if I was going to point at “a site with lot of weird post meta” I would pick this site.

    I spent a few hours on the 7th (the day after the release) beta testing their 4.3.1 version. We tried a patch for the bug where full sync wasn’t giving up on wp error. That helped a little, but the error kept happening, limiting itself to two or three processes. I pointed to a special API, I ran some weird wp shell commands, and all we came up with was that at 190 or so ‘chunks’ out of 443, my server would stop sending messages to Jetpack’s servers.

    Eventually I zipped up a copy of the theme and plugins and a sanitized DB (all secret information removed) and sent it over for them to play with. And they reproduced it! That was good. It meant it wasn’t my server, but it was my setup and the way Jetpack’s sync worked.

    Like everything that has to sync, Jetpack plays the game between ‘sync it all super fast’ and ‘don’t kill the server.’ The way they sync the posts, they apply filters to render the content, including embeds. Because it does that with embeds, it triggers update_post_meta to update the _oembed_time_{long_base64_string} value, so it can know when to update the embed code for best caching.

    Wasn’t I just talking about post meta the other day? Why yes! I was talking about optimizing post meta for search! The interesting thing about that is, since I’m using ElasticPress, it scans all my post meta for updates so it knows what to save as searchable data. That means when Jetpack triggers the update, it triggers ElasticPress, and all hell breaks loose.

    But why did this happen now? Because I turned on “Sitemaps” for Jetpack. And when you enable (or disable) a Jetpack Module, it triggers a full sync. This happened to be the first time I’d done that since installing ElasticPress.

    I did what any responsible person would do, and wrote this all up and submitted a bug report with ElasticPress. Sadly for now I’ve disabled ElasticPress until this can be resolved. I can probably turn it back on safely, since I won’t be triggering a full sync any time soon, but since I don’t want to accidentally crash things, I’ve left it off.

    And how was your week?

  • Updating Multiple Posts’ Meta

    Updating Multiple Posts’ Meta

    I had 328 posts that I needed to add a meta field value to. Thankfully they all had the same value at the moment, so what I really needed was to tell WP “For all posts in the custom post type of ‘show’ add the post_meta key of ‘tvtype’ with a value of ‘tvshow’.”

    That sounded simple. It wasn’t.

    Googling “Updating multiple posts” or “Bulk Update Multiple Posts” (with WordPress in there) was frustratingly vague and told me to do things like the bulk edit from the post lists page. Well. Sure. If I added my post meta to the bulk editor (which I do know how to do) and felt like updating them 20 shows at a time, I could do that. Heck, I could make my page list 50 and not 20, and do it in 5 ‘cycles.’

    But that wasn’t what I wanted to do. No, I wanted to figure out how to do it faster forever, so that if I had to update 32,800 posts, I could do it in the least CPU intensive way.

    PHP

    If I was to do this in PHP, it would look like this:

    $ids = array(
    	'post_type' 	=> 'post_type_shows',
    	'numberposts'	=> -1,
    	'post_status'	=> array('publish', 'pending', 'draft', 'future'),
    );
    
    
    foreach ($ids as $id){
        add_post_meta( $id, 'tvtype', 'tvshow' );
    }
    

    I picked add_post_meta instead of update_ because while the update will add the meta if it’s not found, I didn’t want to update any of the shows I’d manually fiddled with already. And to run this, I’d have to put it in an MU plugin and delete it when I was done.

    Which… Yes. That could work. I’d want to wrap it around a user capability check to make sure it didn’t run indefinitely, but it would work.

    WP-CLI

    Doesn’t a nice command line call sound better, though? Spoiler alert: It’s not.

    I knew I could get a list of the IDs with this:

    $ wp post list --post_type=post_type_shows --fields=ID --format=ids
    

    That gave me a space-separated list

    And I knew I could add the meta like this for each show:

    $ wp post meta add 123 tvtype tvshow
    

    But who wants to do that 328 times?

    The documentation for wp post meta update said “Update a meta field.” A. Singular. Now it was possible that this could be for multiple posts, since the information on wp post update said “Update one or more posts” and “one or more” means one or more. But the example only had this:

    $ wp post update 123 --post_name=something --post_status=draft
    

    Notice how there’s no mention of how one might handle multiple posts? In light of clear documentation, I checked what the code was doing. For the update function, I found this:

    	public function update( $args, $assoc_args ) {
    		foreach( $args as $key => $arg ) {
    			if ( is_numeric( $arg ) ) {
    				continue;
    			}
    

    The check for if ( is_numeric( $arg ) ) is the magic there. It says “If this is an ID, keep going.” And no spaces. So the answer to “How do I update multiple posts?” is this:

    $ wp post update 123 124 125 --post_name=something --post_status=draft
    

    Great! So can I do that with post meta? Would this work?

    $ wp post meta add 123 124 125 tvtype tvshow
    

    Answer: No.

    So I took that list, used search/replace to turn it into 328 separate commands, and pasted them in (in 50 line chunks) to my terminal to update everything.

    Yaaaay.

  • Post Meta, Custom Post Meta, and Statistics

    Post Meta, Custom Post Meta, and Statistics

    I do a lot with weird stats. I do a lot with CMB2 and custom meta. I do a lot with CMB2 and weird post meta.

    Is it any surprise I wanted to add my new year dropdown data to my site for some interesting statistics?

    Of course not. But I started small. I have a saved loop called $all_shows_query

    // List of shows
    $all_shows_query = new WP_Query ( array(
    	'post_type'       => 'post_type_shows',
    	'posts_per_page'  => -1,
    	'no_found_rows'   => true,
    	'post_status'     => array( 'publish', 'draft' ),
    ));
    

    This really just gets the list of all the shows and holds on to it. The meat of the code runs with this:

    $show_current = 0;
    
    if ($all_shows_query->have_posts() ) {
    	while ( $all_shows_query->have_posts() ) {
    		$all_shows_query->the_post();
    		$show_id = $post->ID;
    		if ( get_post_meta( $show_id, "shows_airdates", true) ) {
    			$airdates = get_post_meta( $show_id, "shows_airdates", true);
    			if ( $airdates['finish'] == 'current' ) {
    				$show_current++;
    			}
    		}
    		// More IF statements are here.
    	}
    	wp_reset_query();
    }
    

    As mentioned, there are more if statements, like if ( get_post_meta( $show_id, "shows_stars", true) ) {...} which gets data for if a show has a star or not. And if ( get_post_meta( $show_id, "shows_worthit", true) ) {...} which checks if a show is worth it or not. I was already doing a lot of this, so slipping in one more check doesn’t hurt.

    To show the data, without Chart.js, it looks like this:

    	<h3>Shows Currently Airing</h3>
    
    	<p>&bull; Currently Airing - <?php echo $show_current .' total &mdash; '. round( ( ( $show_current / $count_shows ) * 100) , 1); ?>%
    	<br />&bull; Not Airing - <?php echo ( $count_shows - $show_current )  .' total &mdash; '. round( ( ( ( $count_shows - $show_current ) / $count_shows ) * 100) , 1); ?>%
    	</p>
    

    With Chart.js, it looks like this:

    <canvas id="pieCurrent" width="200" height="200"></canvas>
    
    <script>
    // Piechart for Currently Airing Data
    var pieCurrentdata = {
        labels: [
            "On Air (<?php echo $show_current; ?>)",
            "Off Air (<?php echo ($count_shows - $show_current ); ?>)",
        ],
        datasets: [
            {
                data: [ <?php echo '
    	            "'.$show_current.'",
    	            "'.($count_shows - $show_current ).'",
    	            '; ?>],
                backgroundColor: [
    	            "#4BC0C0",
    	            "#FF6384",
    	            "#E7E9ED"
                ]
            }]
    };
    
    var ctx = document.getElementById("pieCurrent").getContext("2d");
    var pieTriggers = new Chart(ctx,{
        type:'doughnut',
        data: pieCurrentdata,
        options: {
    		tooltips: {
    		    callbacks: {
    				label: function(tooltipItem, data) {
    					return data.labels[tooltipItem.index];
    				}
    		    },
    		},
    	}
    });
    </script>
    

    Right now the data’s a bit off, since I’ve only updated 200 of the 325 shows, but it’s enough to get the information.

  • Passing Variables in WordPress Templates

    Passing Variables in WordPress Templates

    I don’t theme, generally speaking. I’m not bad at it, I just don’t have that particular aspect of ‘design’ in my head where I can create out of nothing. What I can do (and do do) is tweak the heck out of themes. This means I’m sure, if you theme, you know all this and think I’m a bit silly. That’s okay.

    One thing I hate about code is duplicated effort. I don’t mind including the same library in 100 different projects, but I hate when I have to use the exact same code over and over in multiple files. Like I did in my theme.

    I talked about using Chart.js in my theme before, and in that work I made use of get_template_part() to show the includes. It actually doesn’t look like that any more. Here’s the original code:

    function lez_stats_footer() {
        get_template_part( 'stats' );
    }
    

    And here’s the current code:

    function lez_stats_footer() {
    	if ( is_page( 'stats' ) ) {
    		get_template_part( 'inc/statistics', 'base' );
    	}
    	if ( is_page( 'death' ) ) {
    		get_template_part( 'inc/statistics', 'death' );
    	}
    	if ( is_page( 'characters' ) ) {
    		get_template_part( 'inc/statistics', 'characters' );
    	}
    	if ( is_page( 'shows' ) ) {
    		get_template_part( 'inc/statistics', 'shows' );
    	}
    }
    

    While get_template_part( 'stats' ) calls the file stats.php, the call to get_template_part( 'inc/statistics', 'shows' ); gets /inc/statistics-shows.php instead.

    This lets me organize things a little better. My /inc/ folder is filled with this:

    archive-characters.php
    archive-shows.php
    excerpt-characters.php
    excerpt-shows.php
    sidebar-characters.php
    sidebar-shows_archive.php
    sidebar-shows_single.php
    statistics-base.php
    statistics-characters.php
    statistics-code.php
    statistics-death.php
    statistics-shows.php
    

    As the help doc says, this makes it easy for a theme to reuse sections of code and that’s actually what I’m doing.

    I have a call to get_template_part( 'inc/archive', 'characters' ); in multiple files, including some page templates but also in my category and taxonomy lists.

    if ( get_post_type( get_the_ID() ) === 'post_type_characters' ) {
    	get_template_part( 'inc/archive', 'characters' );
    }
    
    if ( get_post_type( get_the_ID() ) === 'post_type_shows' ) {
    	get_template_part( 'inc/archive', 'shows' );
    }
    

    That means if it’s a show, use one format, and if it’s a character use another.

    But…

    One of the things I do is a little weird. On the single display pages for shows, I loop through all the characters related to the show and display them. There’s custom post meta to link the two, and what I do on the single page (single-post_type_shows.php) is grab the value for the post-meta of ‘shows’ on every single character page, and if the show ID is the same as the post ID of the page you’re on, add the character to an array.

    This is not very efficient. I know. It’s basically running an extra loop on every ‘show page’ load and collecting this query. What’s important here though is the array:

    $havecharacters[$char_id] = array(
    	'id'      => $char_id,
    	'title'   => get_the_title( $char_id ),
    	'url'     => get_the_permalink( $char_id ),
    	'content' => get_the_content( $char_id ),
    );
    

    This is important because I needed to pass this data through to my template part. On the template page for characters, I call get_template_part( 'inc/excerpt', 'characters' ); and it works fine because it knows exactly what page it’s on and calling get_the_post_thumbnail() will work because of what show it is. But when I call it from the shows page, though, it tried to use the values based on the show page, not the character.

    What I needed to do was pass the parameters of the character array to the excerpt. And you can’t do that with get_template_part but you can with a different call.

    include(locate_template('inc/excerpt-characters.php'));

    This passed through my array, which let me call it with a nice for-loop :

    if ( empty($havecharacters) ) {
    	echo '<p>There are no characters listed for this show.</p>';
    } else {
    	foreach( $havecharacters as $character ) {
    	?>
    		<ul class="character-list">
    			<li class="clearfix"><?php include(locate_template('inc/excerpt-characters.php')); ?></li>
    		</ul>
    	<?php
    	}
    }
    

    And now my excerpt template can use $character['title'] and everything looked great on the show pages.

    They did not, however, look so great on the character page, which used get_template_part remember. This was simply because the array for $character was empty. In order to make this work for the single character page, I had to recreate the array. Only I did it a little differently.

    if ( empty($character) ) {
    	$character = array(
    		'id'      => get_the_ID(),
    		'title'   => get_the_title(),
    		'url'     => get_the_permalink(),
    		'content' => '',
    		'ischar'  => true,
    		'shows'  => get_post_meta( get_the_ID(), 'lezchars_show', true ),
    	);
    }
    

    The reason I wiped out the content (which held get_the_content in the original array, remember) is that on a single character page, WordPress knows to show the content, so I don’t need to double that work. I also added in new array items for ischar and shows so I could check if ( $character['ischar'] !== true ) {...} in some places and show slightly different content depending on what page I’m on.

    Whew.