Half-Elf on Tech

Thoughts From a Professional Lesbian

Tag: wordpress

  • Make WordPress Gay

    Make WordPress Gay

    In September, WordPress.com changed their admin bar from the normal black or blue to a rainbow color. Why?

    Australia will be holding a national survey on marriage equality over the next two months. To show our support as a company for marriage equality, we’re showing the rainbow bar in the WordPress.com admin bar to all logged-in Australian visitors. You can read more about the marriage equality campaign here: http://www.equalitycampaign.org.au/

    And this was super cool. They’d done it before when Gay Marriage was legalized in the US and it’s much appreciated as a show of solidarity. But… The menu bar only shows if you’re visiting WordPress.com from Australia. This causes two problems for me:

    1) I’m in the US
    2) I self host

    Don’t worry, there’s a solution.

    The ‘Official’ Solution

    The code for what WordPress.com does can be found on the Github repository for Calypso. But that’s all mixed in with a lot of extra ‘stuff’ that has to be there for their services, like geo-location and so on.

    The rest of us don’t need that, and since Gary (the fellow who wrote the ticket) is a friend of mine, I asked him if the code was available. It is.

    Rainbow Bar Code

    <?php
    
    /*
     * Plugin Name: Rainbow Bar!
     */
    
    function rainbow_bar() {
    ?>
    	<style type="text/css">
    		#wpadminbar {
    			background: linear-gradient(
    				to bottom,
    				#e24c3e 0%,
    				#e24c3e 16.66667%,
    				#f47d3b 16.66667%,
    				#f47d3b 33.33333%,
    				#fdb813 33.33333%,
    				#fdb813 50%,
    				#74bb5d 50%,
    				#74bb5d 66.66667%,
    				#38a6d7 66.66667%,
    				#38a6d7 83.33333%,
    				#8c7ab8 83.33333%,
    				#8c7ab8 100% );
    		}
    
    		#wpadminbar,
    		#wpadminbar .quicklinks > ul > li {
    			-webkit-box-shadow: unset;
    			-moz-box-shadow: unset;
    			box-shadow: unset;
    		}
    
    		#wpadminbar .ab-top-menu > li > a {
    			background-color: rgba( 50, 55, 60, .85 );
    		}
    	</style>
    <?php
    }
    add_action( 'wp_before_admin_bar_render', 'rainbow_bar' );
    

    Install that to get this:

    Rainbow Pride WP Admin Bar

    Small Changes

    In order to make it look ‘right’ for me, I changed two things

    I removed this:

    	#wpadminbar .ab-top-menu > li > a {
    		background-color: rgba( 50, 55, 60, .85 );
    	}
    

    And I added this:

    	#wpadminbar .ab-item, #wpadminbar a.ab-item, #wpadminbar > #wp-toolbar span.ab-label, #wpadminbar > #wp-toolbar span.noticon,
    	#wpadminbar .ab-icon, #wpadminbar .ab-icon:before, #wpadminbar .ab-item:before, #wpadminbar .ab-item:after {
    		color: #000;
    	}
    

    Caveats

    Even with my changes, the color for my Jetpack stats looks wrong. It’s too washed out. And that’s because it’s apparently hardcoded into the plugin in a way I can’t overwrite. I can live with that problem.

    The other issue is that this will only show for logged in users (unless you’re using some code to always show the admin bar). That begs the question, of course, of why would you do this if only logged in users?

    If you’re running a queer themed website that happens to use WordPress … like, say, Autostraddle, then this makes perfect sense. For me, it just makes me feel happy to see that pride rainbow all the time.

  • Housing Large Media Files

    Housing Large Media Files

    For the most part, the WordPress media library is fine. It falls down when we start needing to upload large files, though, for a variety of reasons. When we look at files like large PDFs or movies or podcasts, it’s really not a great solution to upload through WordPress itself. It’s slow, it’s clunky, and worst of all, those large file downloads can slow your site.

    The ‘right’ fix is to offload large media to servers that are built for this sort of thing. And in this case, I’m talking about Amazon AWS or DreamObjects.

    Of course, if you search for solutions like this, you’ll be disappointed. You will mostly find plugins that are geared towards syncing your media library with the cloud services. To be honest, the more I think about doing that, the less I feel like it’s a sustainable idea. Unless the CDN is super fast, it could actually make your site worse off by adding another domain to download from.

    I Don’t Trust Simple CDNs

    I’ve always been skeptical of CDNs in general. When there’s a shared library, it makes sense for everyone to call the same library. That keeps the world in sync. But your own media? The reason a CDN is good is that you can distribute your content across multiple locations. Provided you can actually, you know, do that. And keep them all in sync.

    Before hosting, I worked at a bank, and one of the headaches we had was pushing software updates across multiple servers and locations. After all, you can’t just upgrade the Chicago servers and not the LA and Atlanta ones. Plus you have to do them all at the same time, or make sure Jane in Idaho isn’t in the middle of depositing money when we reboot her server.

    Knowing how crazy all that is, I worry about keeping data in sync across all the servers. What happens when media is updated? Is the CDN built so that my primary location properly triggers updates for everything else, and the data is updated? No matter what, I’m sure I’ll end up with some data out of sync for at least a little while.

    In short, CDN synchronization isn’t simple and anyone who tells me it is, is selling something.

    So Why A CDN At All?

    Big files.

    The goal of a CDN is to speed up delivery of content without slowing down your website. For most images on a website, this isn’t a huge issue. But for those big files, it sure is. And uploading them to the could means three things:

    1. No lost disk space
    2. No lost bandwidth (if someone’s watching a movie for example)
    3. No lost speed (see the aforementioned movie)

    The rest of your CDN ‘needs’ can be handled properly by caching. I prefer server side, but as you like it. This means if I upload my large files to the CDN, I can link directly to them in my post content. Everyone wins.

    Except Uploading Sucks

    The common solution is to manually upload the file via a client like Cyberduck or Transmit, copy the URL, and then paste it into a blog post. Yuck. What I need is a file manager for the cloud. And that doesn’t seem to exist for WordPress.

    So I made something. DreamHost Objects Dropzone lets me upload files to DreamObjects, through WordPress, without touching the file server at all. It’s not perfect. It can be slow when trying to get stats on all the items in a bucket, and I don’t quite have an interface to make it easy to insert links and content into posts. Yet.

    Something to look forward to though.

  • Loading Common Libraries Smartly

    Loading Common Libraries Smartly

    When you’re writing code, there’s a desire to not reinvent the wheel. This is a good desire. If someone else has made the code you need, using it is a better use of your time. But there’s a right way and a wrong way to include those common libraries, especially in WordPress themes and plugins.

    PHP Is Dumb

    Okay, PHP isn’t dumb, but it’s a very top-down/left-right kind of language. The whole reason we tell people to name functions and classes uniquely in WordPress plugins and themes is that if two people name a function the same thing, PHP gets dumb and errors, telling you it can’t define the same function twice.

    This means that commonly used PHP libraries should also be uniquely named. To their credit, most do. But still, what happens if two WordPress plugins or themes include the same library? That same error comes back about not defining the same function twice!

    What this means is that when using a common library, it’s important that you enqueue it safely and smartly.

    Check If The Library Exists

    The magic is that you need to detect if the code is already included and not re-include it. PHP gives you two straightforward ways to do this: class_exists and function_exists.

    It’s much easier for me when the code is wrapped nicely in a class. Assuming you have a library file that includes the class but doesn’t call it, you can check like this:

    <?php
    if ( !class_exists( 'MyLibraryClass' ) ) {
        include( 'path/to/library.php' );
        $mylibraryclass = new MyLibraryClass();
    }
    

    If it does include the class (with a new MyLibraryClass() in the library) then you can leave that bit out.

    When you have a library that perhaps predates classes, you’ll want to pick the primary function and call it like this:

    <?php
    if ( !function_exists( 'my_library_function' ) ) {
        include( 'path/to/library.php' );
    }
    

    What About The Weird Stuff?

    There are two things I left out. What about namespaces and javascript libraries?

    First of all, it’s impossible to check for the existence of a namespace. You see, a namespace doesn’t really exist. What does exists are the structures within the namespace. That means if you wanted to check for the class ClassName in the namespace of MyName you have to do this:

    class_exists( '\MyName\ClassName') );
    

    Second, if you’re looking for javascript libraries, and you’re using WordPress, you don’t have to. Just enqueue it.

    Schnieder from One Day at a Time (2017) going 'Mind blown'

    The WordPress function wp_enqueue_script() registers the script but does not overwrite and enqueues it.

    Now there is a trick to all this. If you use the enqueue command properly, then this always works. By this I mean if you do it this way, it’ll work:

    wp_enqueue_script ( 'chartjs', 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.bundle.min.js' );
    

    But… A lot of people do this:

    wp_enqueue_script ( 'my-plugin-chartjs', 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.bundle.min.js' );
    

    And that means WordPress will load both chartjs and my-plugin-chartjs which sucks.

    Also you don’t need to append -js (or -css) to your enqueues, as WordPress will do that for you.

  • You Don’t Know Paths

    You Don’t Know Paths

    A common issue with plugins is where people insist they must call wp-load.php directly so that they can call WordPress functions and data when WordPress isn’t loaded. When I push back on that code, the common rationale is that there’s an external service that needs to ping a reliable URL to run code.

    You Don’t Know Where WordPress Is

    When you hardcode in paths, or assume that everyone has WordPress in the root of their domain, you cause anyone using ‘Giving WordPress it’s own directory’ (a very common setup) to break. In addition, WordPress allows users to change the name of the wp-content folder, so any plugin that tries to guess where it is would break on any site that choses to do so.

    This cuts both ways. First your clever script that tries to go to example.com/wp-content/plugins/your-plugin/yourfile.php is going to fail if someone moves that folder. Second your clever file that looks for wp-load.php by going up a couple folders will also fail.

    You can’t know where WordPress is. The only way to know all that would be to do things from within WordPress.

    Never Call Files Directly

    The only files you should ever call directly in your code is your code’s files or if you’re checking for the existence of another plugin or theme. This is the only time it’s smart to be hardcoding in paths of other code directly into your code.

    if ( is_plugin_active( 'plugin-directory/plugin-file.php' ) ) { } 
    

    Since most people call these files to have access to WordPress ‘without loading WordPress,’ the actual correct fix is to tie processing functions (the ones that need but don’t have access to core functions) into an action hook, such as “init” or “admin_init”.

    Code you add to WordPress should be inside WordPress, only accessible to people who are logged in and authorized, if it needs that kind of access. Plugin’s pages should be called via the dashboard like all the other settings panels, and in that way, they’ll always have access to WordPress functions.

    What’s The Right Answer?

    Okay. There’s one case where it makes sense that you might possibly need to call a URL directly. If you’re hooking WordPress up to a service and you want to have a good URL to call, then yes, you would be well served by having one of the following:

    • example.com/?myplugin=runstuff
    • example.com/myplugin/

    Don’t those look nice? There are two main ways to get there.

    External Rules

    An external rule is basically you adding an redirect rule for a URL to point to a file. You could do this in .htaccess or nginx.conf but if you do it in WordPress, it looks like this:

    add_action( 'init', 'myplugin_external_rules' );
    function myplugin_external_rules() {
        global $wp_rewrite;
        $plugin_url = plugins_url( 'yourfile.php', __FILE__ );
        $plugin_url = substr( $plugin_url, strlen( home_url() ) + 1 );
        $wp_rewrite->add_external_rule( 'myplugin$', $plugin_url );
    }
    

    Now the obvious issue here is that you’re still calling a php file directly, which means you don’t get any access to WordPress functions. Boo. Thankfully there’s another answer.

    Internal Rules

    While more complicated and weird, this is a better solution.

    First you need to actually make the rule:

    add_action( 'init', 'myplugin_internal_rules' );
    function myplugin_internal_rules() {
        add_rewrite_rule( 'myplugin', 'index.php?myplugin=runstuff', 'top' );
    }
    

    This means that those two example URLs I pitched before are the same thing.

    Next you need to tell WordPress not to stomp all over the query variables:

    add_filter( 'query_vars', 'myplugin_query_vars' );
    function myplugin_query_vars( $query_vars ) {
        $query_vars[] = 'myplugin';
        return $query_vars;
    }
    

    You have to do this because otherwise WordPress won’t know what to do with the query variable.

    Finally we tell WordPress what to actually do with your variable:

    add_action( 'parse_request', 'myplugin_parse_request' );
    function myplugin_parse_request( &$wp ) {
        if ( array_key_exists( 'myplugin', $wp->query_vars ) ) {
            include 'yourfile.php';
            // Call your functions here!
            // function_name_thingy();
            exit();
        }
        return;
    }
    

    You’ll notice that // your functions here code is waiting for your code. That’s because this time we’re including the PHP file and then in there you’ve theoretically written your code in a nice little function. And that function? Uses WordPress functions because it is one.

  • Flushing Another Post’s Varnish

    Flushing Another Post’s Varnish

    The other day I talked about updating another post’s meta. That’s all well and good, but what happens when you’ve cached that other page?

    Most cache tools work via the simplistic understanding that if you edit a page, you want to empty the cache for that page. The problem with my clever post-meta updating is that doesn’t actually update the post. Oh, sure it updates the data, but that doesn’t perform an update in a way that most plugins understand.

    Thankfully this can be done programmatically in a few different ways, depending on what plugin is used.

    1. Tell Varnish to empty the page’s cache any time post meta is updated
    2. Add a force-empty command to the function that updates the meta

    Using Varnish HTTP Purge

    Since I’m running on DreamPress, my plugin of choice is Varnish HTTP Purge. This is a very simple plugin that has no real interface by design. Mike Schroder and I picked it for use because of it’s beautiful simplicity, and when it was abandoned, adopted it. I’m rather well acquainted with it and at the behest of others, I wrote in code where people could add extra URLs to flush as well as extra events to trigger. There’s even a wiki doc about it.

    However. That’s not what I did here because I wanted to limit when it runs as much as possible.

    When To Empty Cache

    There’s a part of caching that is always difficult to manage. When should a page be emptied and when should a whole section of pages be emptied? The obvious answer is that we should retain a cache as long as possible, emptying select pages only when necessary.

    With WordPress posts and pages it’s pretty easy to know “Update the post, empty the cache.” But when you start talking about things like the JSON API, it’s a lot harder. Most plugins handle the JSON API for you, but if you’ve built your own API (like say /wp-json/lwtv/) and built out a lot of custom APIs (like say stats or an Alexa skill) you will want to flush that whole darn thing every single time.

    The Code

    Okay so how do we do that easily?

    In the function I showed you the other day, I added this right before the re-hook at the end:

    if ( class_exists( 'VarnishPurger' ) ) {
    	$varnish_purge = new VarnishPurger();
    	$varnish_purge->purgeUrl( get_permalink( $post_id ) );
    }
    

    That makes sure the Varnish plugin is running and calls a URL to flush. Done. If you want to have it do more URLs, you can do this:

    if ( class_exists( 'VarnishPurger' ) ) {
    			
    	$purgeurls = array( 
    		get_permalink( $post_id ) . '?vhp-regex',
    		get_site_url() . '/page1/',
    		get_site_url() . '/custom_page/',
    		get_site_url() . '/wp-json/lwtv/?vhp-regex',
    	);
    			
    	foreach ( $purgeurls as $url ) {
    		$this->varnish_purge->purgeUrl( $url ) ;
    	}
    }
    

    And then you can add in as many as you want.

  • Updating Another Post’s Meta

    Updating Another Post’s Meta

    Over on LezWatch TV we have two post types, shows and characters. In an added wrinkle, there’s data that exists on the show pages that is updated by the character pages.

    That means when I save a character page, I need it to trigger a series of updates to post meta of whatever shows the character belongs to. But because of the complexities of the data being saved, I need to run it when the show saves too.

    The Code

    add_action( 'do_update_show_meta', 'lezwatch_update_show_meta', 10, 2 );
    add_action( 'save_post_post_type_shows', 'lezwatch_update_show_meta', 10, 3 );
    add_action( 'save_post_post_type_characters', 'lezwatch_update_show_meta_from_chars', 10, 3 );
    
    function lezwatch_update_show_meta( $post_id ) {
    
    	// unhook this function so it doesn't loop infinitely
    	remove_action( 'save_post_post_type_shows', 'lezwatch_update_show_meta' );
    
    	[Update all the post meta as needed here]
    
    	// re-hook this function
    	add_action( 'save_post_post_type_shows', array( $this, 'lezwatch_update_show_meta' ) );
    
    }
    
    function lezwatch_update_show_meta_from_chars( $post_id ) {
    	$character_show_IDs = get_post_meta( $post_id, 'lezchars_show_group', true );
    	if ( $character_show_IDs !== '' ) {
    		foreach ( $character_show_IDs as $each_show ) {
    			do_action( 'do_update_show_meta' , $each_show['show'] );
    		}
    	}
    }
    

    This runs three actions, saving shows and characters, and also a custom one that the character page will call. It needed to be split like that because some characters have multiple shows.