Half-Elf on Tech

Thoughts From a Professional Lesbian

Tag: wordpress

  • WordPress Multisite Control

    WordPress Multisite Control

    When you write a plugin for WordPress Multisite, you have three options for how to let users control the plugin options. It comes down to the manipulation of the ways we have to activate a plugin on Multisite, which are per-site or network only.

    I’m a firm adherent of having the network control as much as it logically should, but allowing each site to pick unique features. Never should someone be shocked to find out they’re on a network. A network is, after all, a collection of WordPress sites. Now your collection may or may not be related, but at the end of the day, someone should never be surprised to find out the site they signed up for is on a network.

    With this in mind, I separated the ‘control’ of the plugins into three groups.

    Network Only

    A Network Only plugin is one that should be controlled via the Network Admin. While the Settings API is a terrible bag of wet hair for Multisite, if you have a network plugin, then it should be for the network. The plugins that have no interface at all should be network activated. This is really simple, but in general if you’re adding this feature to your network, you probably want it on for everyone. There are some rare exceptions, but in general, network only is the key.

    Most network only plugins are clever enough to use Network: true in their plugin headers, which makes this much easier. If you think your code should only be activated by the network, use that.

    Per-Site

    A per-site plugin is activated on each site, controlled from each site, and the network admins have no authority save uninstalling the plugin. These plugins are things that each site should decide how to use. When I look at my own sites, I have a few that are like this. Like @Reply Two – when you look at it, you’d think it should be network only, but since it requires some per-site configuration with regards to comments, it’s best left as optional for each site.

    There is no Network: false setting, I’m afraid.

    Network Only Activation with Per-Site Control

    Here’s where it gets sticky, and plugins like Jetpack actually handle this better than most others. Take, for example, something like a plugin that adds features to a specific theme. If that theme isn’t active, the plugin shouldn’t error out. But a lot of us code our plugins to say “If this other plugin or theme isn’t active, don’t activate.” That sounds like a great idea except when you want to have it network activated. In those cases, the checks get weird and don’t run as expected.

    And then you have to consider what should control what. I mentioned Jetpack because it has a network admin screen.

    Jetpack's Network Override

    There you can enforce connections from your network admin, or not, as you see fit.

    Which One Is Right?

    While I’ve postulated this is very simple, it’s not. For example, when you have Jetpack, do I want everyone to edit every setting or just some? I’d want them to have the ability to use the CSS editor per-site, but maybe not VaultPress or Stats. The checks for that code is not as logical as it should be. The whens for running those checks, the priorities and weight given to who is more important, is not obvious.

    I would say that the Network Admins should have final say. But many people don’t agree with me on that. Many people think each site on a network would be best to exist on it’s own and stand alone, a part of a secret.

    And that too deserves room for thought.

  • Debugging Unexpected Output

    Debugging Unexpected Output

    It’s going to happen one day. You’re going to get that weird error when you activate your plugin to test it and you will have no idea what it means.

    What error?

    The plugin generated 306 characters of unexpected output during activation...

    Unexpected output.

    The problem is WordPress doesn’t tell you what did it, where, or why, not even if you have debug turned on. You do have debug turned on, right? Well I was stumped on this one. I checked all my PHP calls to make sure I didn’t have whitespace, I looked for any files that were accidentally saved UTF-8, and I checked and double checked my diffs between the good and bad versions.

    What I ended up doing was writing a function that saved the error and output it.

    /* For debugging only */
    if (defined('WP_DEBUG') && true === WP_DEBUG) { 
        function myplugin_activated_plugin_error() {
            update_option( 'myplugin_error',  ob_get_contents() );
        }
        function myplugin_deactivated_plugin_error() {
            delete_option( 'myplugin_error' );
        }
        add_action( 'activated_plugin', 'myplugin_activated_plugin_error' );
        add_action( 'deactivated_plugin', 'myplugin_deactivated_plugin_error' );
        
    	function myplugin_message_plugin_error() {
    	    ?>
    	    <div class="notice notice-error">
    	        <p><?php echo get_option( 'myplugin_error' ); ?></p>
    	    </div>
    	    <?php
    		}
    	if( get_option( 'myplugin_error' ) ) {
    		add_action( 'admin_notices', 'myplugin_message_plugin_error' );	
    	}
    }
    

    That results in a pretty kind of output:

    A pretty version of my errors!

    I like to use the pretty display so I can easily read what, exactly I messed up and how.

  • Encrypting Source Code Doesn’t Make It Safer

    Encrypting Source Code Doesn’t Make It Safer

    I’d love to think that’s all I have to say on the matter, that you all will read the subject, go “Yup!” and we’re done.

    The reality is that I have to argue this, regularly, with people.

    Here’s the code from a plugin out there:

    <?php ${"\x47L\x4fB\x41\x4c\x53"}["w\x73\x78\x6e\x69\x66\x69\x6f\x71\x6c"]="\x73l_s\x65arch\x61bl\x65\x5fc\x6f\x6cu\x6d\x6e\x73";${"\x47L\x4fBAL\x53"}["\x66\x6b\x78xg\x63\x6ap\x68\x6d\x6ft"]="\x73\x6c\x5fdb";${"\x47\x4c\x4f\x42AL\x53"}["\x65\x62\x67\x79\x6b\x66\x64"]="\x69\x73\x5f\x73\x6c\x5fca\x74e\x67\x6f\x72\x69\x7a\x61\x74\x69\x6fn_\x63\x6f\x6c\x75\x6dn";${"\x47\x4c\x4fBA\x4c\x53"}
    

    The whole file is like that. The developer explained it was done that way for ‘security’ — it would make things harder to hack. I pointed out that’s simply not true.

    Here’s what having encrypted, hashed, packed code does:

    1. It makes your build process take longer.
    2. It adds another failure point into your code.
    3. It makes it harder for the end users, other developers (who write plugins), web hosts to debug, and you to debug.
    4. It makes you look like a developer with evil intents.
    5. It sets an expectation with users that this kind of code is ‘normal’ in WordPress.

    Recently Sucuri posted about a redirect hack that works by putting junk code in your header.php file which looks rather similar:

    Malicious injection in your header.php

    The issue here is that an end user, your normal WordPress user, cannot tell the difference between the somewhat safe code I quoted before and this code. They see ‘gibberish’ where as I know they can use a hex decoder to translate ["w\x73\x78\x6e\x69\x66\x69\x6f\x71\x6c"] into ["wsxnifioql"] … which is still pretty terrible.

    Well written code, well named functions, are self-explanatory. You see a function called redirect_404_pages() and you have a pretty good idea of what it’s for. You see a function named wsxnifioql() and good luck knowing what the heck that’s for. This goes back to the claim that the code is more secure. It’s not. It’s needlessly complicated, and as I shoed with the hex decoder tool, it can trivially be decrypted and read.

    So what is the real point of hiding your code? Who are you trying to protect? What’s ‘safer’ about any of this?

    The answer is that it’s about about you, you, you. You don’t want someone to take your great idea.

    That’s it. And that’s foolish.

    WordPress is GPLv2 (or later). Furthermore, to be hosted on WordPress.org, your code cannot be encrypted or hidden or otherwise non-human-readable. The basic reason is that WordPress’ success is due to it’s understandability and extendability. Anyone can read WordPress’ core code, parse it, learn from it, and enhance it. When you take that away from users, you isolate your code and prevent people from extending it.

    This person, this developer, charges upwards of $1000 for the add ons to their code. Yes, a plugin that costs over a grand. It sounds economically sound to try and lock things down so people don’t steal their intellectual property. We can all understand that impetus. I support it. I also feel that part of being in an open source community is being aware of how your actions impact the world at large.

    Because WordPress is open and because there is a standard expectation of non-encrypted code (except by evil-doers), the burden moves to developers to not hide their code that is installed on users’ servers. The code that is deployed to an end-user is expected to be human readable. This comes at a risk. I have a copy of a theme I bought, and I could give it away to anyone I wanted. They may not get updates, which means I have to be aware of the risk I’m introducing to my friends when I give them something like a premium theme or plugin.

    Similarly, what are the risks of telling people it’s okay to install plugin code in uploads instead of the plugins folder? What are the risks of allowing people to think that encrypted code is generally okay? In and of themselves, neither action seems particularly dangerous. PHP code is PHP code, right? If it runs, you’re good. But the reality is not so. By installing code in uploads I’ve made it so it’s no longer fully protected by WordPress and ‘standard’ security practices. I’ve also made it riskier that my code would even run, since many hosts prevent executable code from running out of that folder for security.

    So how do I meet the (assumed) criteria of not having someone rip off my code?

    You don’t. Your machinations aren’t preventing it now, and they won’t prevent it tomorrow. Hexcode is easily parsed. Even the Zend framework has to be able to be reversed to be run, so a dedicated person will always find a way around it. And the majority of your users aren’t going to be the problem. It’s those extremes. So what you’ve done is wasted time, effort, and money to annoy the majority to stop the minority. Let people inspect your code. If someone steals it, there are laws to help you handle them. Use them. Theft is theft. The GPL may allow them to take your code, copy and expand on it, but it doesn’t let them violate your copyright.

    All the work you’re doing to hiding your code is about as useful as preventing right-click on images. It doesn’t protect the end users, and it doesn’t protect your intellectual property.

  • Making More With Less Work

    Making More With Less Work

    The other day, I added meta data of an icon to a custom taxonomy. When you need to add one or two filters to one or two items, it’s simple.

    add_filter( 'manage_edit-{my_taxonomy}_columns',  'my_terms_column_header' );
    add_action( 'manage_{my_taxonomy}_custom_column', 'my_terms_column_rows', 10, 3  );
    

    Replace {my_taxonomy} with your taxonomies, call it a day. But when you add in two, or three, or four, you don’t want to make four, or six, or eight lines of code. And when you’re calling the same array over and over, it’s time to get smart.

    Make An Array

    I originally only want to do this custom work in one taxonomy, so it was no sweat to repeat a little code. But then I realized I wanted to see it in four. Maybe more. So I picked out my array:

    $icon_taxonomies = array( 'tax_cliches', 'tax_chartags', 'tax_gender', 'tax_sexuality' );
    

    I made this outside the two functions, because I knew I was going to need it in multiple places. It would necessitate me tossing global $icon_taxonomies; into every function that needed it, but that was okay. Better that then trying to make sure every time I updated an array, I did it in all the places. Once is better.

    Make Your Loop

    The easiest thing was to trigger this on admit_init. Grabbing my global, I ran a simple for-loop from that array and added a filter based on the taxonomy named stored in the array:

    add_action( 'admin_init', 'my_add_taxonomy_icon_options' );
    function my_add_taxonomy_icon_options() {
        global $icon_taxonomies;
        foreach ( $icon_taxonomies as $tax_name ) {
    	add_filter( 'manage_edit-'.$tax_name. '_columns',  'my_terms_column_header' );
    	add_action( 'manage_'.$tax_name. '_custom_column', 'my_terms_column_rows', 10, 3 );
        }
    }
    

    Is It Really Less?

    A clever person will count the lines in this code (10 when you add in the variable) and think “Mika, you wrote two extra lines!”

    A more clever person will count the lines and think “At 5 taxonomies begins the break even point.”

    I know. From the outset it looks like I’m taking more time to over-engineer the solution to a very simple problem. And while I am, I’m also making it easier not to make typos and not to forget a step. I can make one change, one word, and update multiple places becuase that array is also used back in the my_register_taxonomy_metabox() function.

    'taxonomies'       => array( 'my_cliches', 'my_chartags' ),
    

    I removed the array and tossed in the new code.

    Yes, that’s adding two lines, but it removes my need to think “Where does this all go?” down to one place. And yes, I documented this in-line. The code header has a nice docblock explaining the whole section, what each variable is, and what each function does.

  • Custom Terms and CMB2

    Custom Terms and CMB2

    Let’s say you have some custom taxonomies in WordPress. And let’s say you want to add a special field to them for an icon. How would you do it?

    There’s a cool plugin by someone I know that can do this, called WP SVG Icons and it works great. But it wasn’t quite what I needed.

    You see, I had 832 really awesome SVG icons I wanted to use. In order to use Evan’s plugin I’d have to convert them into a font using Icomoon. I already had about 300 in a font, which is cool, but I was in the thought to move these from fonts to pure SVG anyway since they’ll show up in better detail if you don’t have retina screens. Also they’re smaller and loaded on demand and … well it is what it is.

    I decided the simplest solution was as follows:

    1. Upload all the SVG to the server
    2. Add a custom term meta field to the taxonomies for ‘icon’
    3. Show the icon on the Terms List Page
    4. Show the icon on the Term Edit page

    And yes, it works.

    Where Do The Icons Go?

    I put them in my theme. They’re pretty specific to the theme itself, but usually things that are related to custom data ends up in mu-plugins since I want them to exist outside of the theme. For the record, I also have CMB2 outside the theme, so the code is all there, but the images are in the theme. You can decide how you want.

    Adding Custom Term Meta

    There are a lot of ways of doing this, but as I’m using CMB2, well, I’m using CMB2 so it looks like this:

    add_action( 'cmb2_admin_init', 'my_register_taxonomy_metabox' );
    function my_register_taxonomy_metabox() {
    	$prefix = 'my_termsmeta_';
    
    	$cmb_term = new_cmb2_box( array(
    		'id'               => $prefix . 'edit',
    		'title'            => 'Category Metabox',
    		'object_types'     => array( 'term' ),
    		'taxonomies'       => array( 'my_cliches', 'my_chartags' ),
    		'new_term_section' => true, 
    	) );
    
    	$cmb_term->add_field( array(
    		'name'		=> 'Icon',
    		'desc'		=> 'Name of the image you want to use, without filetype (i.e. carrot)',
    		'id'		=> $prefix . 'icon',
    		'type'		=> 'text_medium',
    		'before_field'	=> 'cmb2_before_field_icon',
    	) );
    }
    

    You’ll notice before_field there. That’s what will let me do the 4th item on my list. The description tells you how to use the field. Don’t worry, I do sanity checks later on.

    Show The Icon On The Terms List

    I wanted the icon to show after save so that people would be able to know what they’d added and change it if needed.

    add_filter( "manage_edit-{my_taxonomy}_columns", "my_terms_column_header" );
    add_action( "manage_{my_taxonomy}_custom_column",  "my_terms_column_rows", 10, 3  );
    
    function my_terms_column_header($columns){
        $columns['icon'] = 'Icon';
        return $columns;
    }
    
    function my_terms_column_rows($value, $content, $term_id){
    	$icon = get_term_meta( $term_id, 'lez_termsmeta_icon', true );
    	$iconpath = get_stylesheet_directory().'/images/symbolicons/'.$icon.'.svg';
    	if ( empty($icon) || !file_exists( $iconpath ) ) {
    		$content = 'N/A';
    	} else {
    		$content = '<span role="img" class="cmb2-icon">'.file_get_contents($iconpath).'</span>';
    	}
        return $content;
    }
    

    You can see where I’m checking if the icon has been set and if the file exists. What this does is first generate a column for the icon and then populate the content based on the term information.

    Icons shown on the list of terms

    There you can see the icon, looking rather nice.

    See The Icon When You Edit

    This is a little different. I had to check the output the of the field value, which is set by CMB2, and then base the rest of the code off that.

    function cmb2_before_field_icon( $field_args, $field ) {
    	$icon = $field->value;
    	$iconpath = get_stylesheet_directory().'/images/symbolicons/'.$icon.'.svg';
    	if ( !empty($icon) || file_exists( $iconpath ) ) {
    		echo '<span role="img" class="cmb2-icon">'.file_get_contents(get_stylesheet_directory_uri().'/images/symbolicons/'.$icon.'.svg').'</span>';
    	}
    }
    

    Again I double check the file actually exists, just in case you typo.

    The edit term page

    I put it before the field because that looked better to me, but there are other places this could go.

    Results?

    I started this because, originally, it was all done with CSS. That meant if people added in new terms, someone with admin ability had to go in and edit things. This way, people can not only see what they’ve added, but you can get a quick view of what doesn’t have icons set. It’s instructive and has visual feedback.

    The ‘next level up’ would be to only allow selection from a dropdown.

    For the least amount of hustle, add this to your my_register_taxonomy_metabox function:

    	$icon_array = array();
    	foreach (glob( get_stylesheet_directory().'/images/symbolicons/*.svg' ) as $file) {
    		$icon_array[ basename($file, '.svg') ] = basename($file, '.svg');
    	}
    

    And then replace 'type' => 'text_medium', with this:

    	    'type'             => 'select',
    	    'show_option_none' => true,
    	    'default'          => 'custom',
    	    'options'          => $icon_array,
    

    And voila. Done. Now you’re even harder to get wrong.

    names as dropdowns

    It even looks good.

  • Null and Zero

    Null and Zero

    This is an amusing anecdote.

    When I was working on my cross-check of shows and characters, I got everything working right except one thing. I noticed my count of ‘shows with death’ made a weird pie chart. You see, the number of shows was off by one compared to the total number of shows. I went back and forth, trying to figure out why it was doing that, and in the end I decided that I’d output the list and manually count to see what I was missing.

    I counted, by hand, the shows that I got with the list of ‘all characters are dead’ and that number matched what the code output. Then I subtracted that from the number of ‘shows with any death’ to get the ‘some characters are dead’ count, and that too was okay. This meant that, for some reason, the list of shows where no one died was wrong. But it was subtraction! How could it be wrong!

    Well as it turns out, I forgot that null and zero aren’t the same to a computer, but they tend to be to a human’s brain.

    Now I know this! I know that zero is a number, it’s a value of the known quantity of zero. And I know that ‘null’ is a non value, meaning that it’s a data point that cannot yet be known.

    You can’t math on null. And I knew this. Earlier in my code I’d written $foo = 0 outside of my loop where I check and, as needed, increment it with $foo++ specifically because you can’t add to null.

    Nothing from nothing, carry the nothing…

    But. In my calculations, I checked the following:

    1. If a show has been flagged with ‘death,’ count all the characters.
    2. For each character, if they’re dead, add 1 to the death count.
    3. If the number of characters is the same as the death count, add this show to the list of ‘kill ’em all.’
    4. If the number of characters is greater than the number of dead, and the value of either isn’t 0, add the show to the list of ‘kill some.’

    Then, separately, I checked “If the show has not been flagged with death, add the show to the ‘kill none’ list, because that’s a 1/0 check. And when you don’t think too deeply about that, it sounds fine, right? If they aren’t marked with death they must kill none. And if they don’t kill them all, and they don’t kill some, then the number should be the same as the ‘kill none’ list.

    The problem was that I had one show, just one, where all the characters were alive but it had been flagged with death. This was not an error. The show did kill off a character, but I’d not added the dead character yet. Which meant it ran through the checks like this:

    1. Flagged with death, we count all the characters (8).
    2. If the character is dead, add one to death count (death count is 0).
    3. If the number of characters (8) is the same as the dead (0), add to ‘kill them all’ (false).
    4. If the number of characters (8) is more than the dead (0) (true), add the value of either isn’t 0 (false), add them to ‘kill some.’ (false).

    Which isn’t wrong at all. It just meant that the show didn’t get listed on ‘kill none’ because it was flagged with dead, and it didn’t get listed on ‘kill them all’ because 8 is more than 0, and it didn’t get listed on ‘kill some’ because while 8 is more than 0, dead was 0.

    Oh silly me. The value of death characters was ‘null.’ They simply didn’t exist in a place where they should have.

    The fix? I added the dead character to the site and everything was fixed. But code wise, could I prevent this? I certainly should, since I know better than many that you can’t predict what users are going to do. So it begs the question of why was I checking if character count and dead count were not 0 to begin with? It was, originally, because my ‘no deaths’ list was screwed up and I threw that check in to omit cases where a show was flagged as death but didn’t have death.

    The accuracy of these things depends entirely on your data. Garbage in, garbage out. The best fix would be to check ‘if a show has death and it has no dead characters, adjust the dead count down by one’ but also ‘if a show has no death, but it has dead characters, adjust the live show count down by one.’ I haven’t done that yet. I will soon.