Half-Elf on Tech

Thoughts From a Professional Lesbian

Tag: wordpress

  • Being Smart Cures Headaches

    Being Smart Cures Headaches

    While reviewing WordPress plugins, I often kick a plugin for calling file locations poorly. This usually happens when they’ve hardcoded their plugin name or (worse) wp-content into paths.

    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 wp-content, so you would break anyone who choses to do so. And when this happens, I always link them to how to figure out the constants, which leads them to the function plugins_url().

    In many ways, plugins_url() is a panacea, a silver bullet, because it can take this:

    <?php
    echo '<img src="' . plugins_url( 'images/wordpress.png', __FILE__ ) . '" > ';
    

    And magically turn it into this:

    <img src="http://www.example.com/wp-content/plugins/my-plugin/images/wordpress.png">
    

    Even better, if you put the plugin in the mu-plugins folder, it would know to be this:

    <img src="http://www.example.com/wp-content/mu-plugins/images/wordpress.png">
    

    That makes it insanely flexible and wonderful.

    This allows me to happily rename a plugin folder to my-plugins_off to force disable it and will reward me with this error:

    The plugin my-plugin/myplugin.php has been deactivated due to an error: Plugin file does not exist.

    I like that error. I like that it happens no matter what, once I’ve renamed that plugin folder and I refresh a page that uses the plugin, it’s gone. Of course, sometimes it’s not enough, but most of the time, if you know what plugin’s being a doofus, you can fix it with that.

  • Don’t Reinvent the Wheel

    Don’t Reinvent the Wheel

    In WordPress, I punt plugins now and then for doing weird things that can best be described as reinventing the wheel.

    Any time a plugin replicates functionality found in WordPress (i.e. the uploader, jquery), it is frowned upon. It presents a possible security risk since the features in WordPress have been tested by many more people than use most plugins. Simply put, the built in tools are less likely to have issues.

    This was always something a little theoretical. I hadn’t yet run into someone who had broken their code with an upgrade of WordPress just because we updated the core wheel. Until November 20th.

    The shortcode API in WordPress was updated. It was decided that in order to stop breaking on PHP 5.4.8 and under, we needed to apply wptexturize() to shortcodes. This had a fun side effect when people didn’t properly register shortcodes, which of course brought up the logical question … why wouldn’t you register your shortcode?

    What I generally see in plugins is someone’s using a filter to look for their shortcode instead of registering it, so the post content is, in it’s entirety, parsed. That always struck me as foolish, since posts can get pretty long if Chris Lema or I are writing. And the reason people would do it was also odd. Either they were being lazy, they didn’t know about shortcodes (which I can understand, you can’t know everything), or they were trying to get around an ‘issue’ with nested shortcodes.

    For what it’s worth, the shortcode parser correctly deals with nested shortcode macros.

    [tag-a]
       [tag-b]
          [tag-c]
       [/tag-b]
    [/tag-a]
    

    That works fine. This won’t:

    [tag-a]
       [tag-a]
       [/tag-a]
    [/tag-a]
    

    Now I want to note, I’m using a php shortcode around those tags. So yes, it works great. But the problem would be if I wanted to show you the php code in a php shortcode… Doesn’t work that way, and it’s a limitation of the context-free regexp parser used by do_shortcode(). We went for speed over levels, and it can’t match each opening tag with its correct closing tag.

    Obviously the ‘right’ answer is ‘don’t nest same-named shortcodes’ but instead, some plugin authors have chosen the strategy of not registering shortcode names. That way the parser doesn’t try to mess with it. Sounds great, right?

    Not with wptexturize() running.

    [tag-a unit="north"]
       [tag-b size="24"]
          [tag-c color="red"]
       [/tag-b]
    [/tag-a]
    

    That turns into this:

    [tag-a unit="north"]
       [tag-b size=&#8221;24&#8221;]
          [tag-c color=&#8221;red&#8221;]
       [/tag-b]
    [/tag-a]
    

    Basically the code is understood to be code and not a quote. That means it would simply output the tags as code and not the tag content.

    And you see why this is a problem.

    There are two answers to fix this. One is to turn off wptexturize for your pseudo-shortcodes (via the no_texturize_shortcodes filters which are not complete, sadly, if you need to unfilter shortcode variables) and the other is to use the Shortcode API as it was intended. I would, personally, suggest you use the API since that prevents your code from breaking like this if another security update happens.

    Which brings us back to why reinventing the wheel is generally foolhardy in robust, well maintained systems. WordPress is constantly being improved, fixed, patched, and secured. The more you work around it by making your own way, the harder it is to fix things when WordPress makes a change. You’d think that people who make their own wheel would be attentive to anything that might break it, but they rarely are. They make their way, forget about it, and get annoyed when their code breaks and blame WordPress for not warning them. That’s a dirty secret about Open Source. No one’s going to tell you that a change will break your code. They expect you to be paying attention.

    The absolutely worst part about all this is that your users will have to decide to either stop using your code (which isn’t always an option) or not to apply an upgrade. In the case of 4.0.1, this is dangerous. This is why I will keep telling you not to reinvent the wheel, unless the wheel really needs it. And if the wheel needs a reinvention, you should consider submitting a patch to WordPress core, because if it’s that bad, everyone should know.

  • No Notice For Upgrades?

    No Notice For Upgrades?

    About a week ago I realized I’d not see an email with the subject “[Ipstenu.Org] Background updates have finished” in a long time. A really long time. Like so long that when I looked in my email trash, there wasn’t one.

    This was bad, because on Ipstenu.org I’m running trunk WordPress, which means I should get two emails a day about this. I ran to my server and scanned logs for 30 days. Nothing. And that was really bad.

    I’ve been using Advanced Automatic Updates for a long time, and I love it, it works well, but it wasn’t working ‘right’ for some reason, because it wasn’t emailing me on that one site, so I uninstalled it. The problem there is that without it, how would I upgrade plugins and themes? Since I know I can control the upgrades with filters, I decided to make an mu-plugin to handle just that.

    And that was when I saw I already had an mu-plugin called upgrades.php with this:

    WordPress site: https://ipstenu.org/
    The following plugins were successfully updated:
    * SUCCESS: Akismet

    Perfect!

    I don’t know how fun that story was, Benny, but that’s what happened.

  • Multisite Favicons

    Multisite Favicons

    One of the challenges with WordPress Multisite is that everything is on the same install. This means something that is rather trivial, like a favicon, becomes exceptionally complex and tiresome.

    On a normal site, when I want to make a favicon, I just toss the file into my main HTML folder, next to that .htaccess file, and I’m done. Now, for me it’s a bit of an extra step because I use StudioPress’ Genesis theme, and it applies its own favicon. It’s probably the only thing about Genesis that makes me annoyed, though it’s right next to when my host does that for me. Still, there’s a trick with Genesis.

    Because of Genesis

    Genesis is my parent theme, and it defines my favicon. So I have to delete its and put in mine:

    // No, you may not have your favicon
    remove_action('genesis_meta', 'genesis_load_favicon');
    
    add_filter( 'genesis_pre_load_favicon', 'halfelf_favicon_filter' );
    function halfelf_favicon_filter( $favicon_url ) {
    	return 'https://halfelf.org/code/images/favicons/halfelf.ico';
    }
    

    And it’s that simple.

    But I’m talking about Multisite, and I want to have a different favicon per site, I end up facing a new challenge.

    Because of Genesis and Multisite

    I still need to tell Genesis to shove it, but then I need to check what site I’m on and call the right favicon per site and this works very well. I’m using it today (you can see the code on that other link). But what if there was another option? What if I didn’t have to code it? Because the real problem with that code is I have to do it! If someone wants a new site to have their own favicon, I have to go in and add it to my mu-plugin, save it, commit it, and push it. Ew.

    Jetpack Site Icons

    Jetpack has a newish feature called Site Icon. With that module activated, I can allow each site to upload their own site icon and it will make a mobile icon too where I won’t have to.

    Uploading my Space Invader

    For a Multisite running Jetpack auto-activated on all sites, this is perfect. And yes, I do that because it saves me a headache of having to set it up later. I allow all sites to connect on their own, but I can also control all that from the network admin.

    So what does that old code look like now?

    With Genesis and Jetpack

    Now my code is simple. For everyone, I kill the default favicon, and then I set my own:

    // No, you may not have your favicon
    remove_action('genesis_meta', 'genesis_load_favicon');
    
    global $blog_id;
    
    add_filter( 'genesis_pre_load_favicon', 'example_favicon_filter' );
    function ipstenu_favicon_filter( $favicon_url ) {
    	return 'http://example.com/images/favicons/example.ico';
    }
    

    And the best part is if I set a site icon in Jetpack, it uses that instead.

    Best of both worlds.

  • Including Assets

    Including Assets

    I’ve noticed a trend in plugin reviews that people are including third party assets with their code.

    This is great. Heck, I do it on a couple of mine. Why should I reinvent the wheel when I’m including code? If I need to use some existing code like a GPL friendly lightbox file, I’ll just grab Colorbox and be happy. But have you ever really looked at your assets?

    Github repo for Colorbox has a lot of files

    The above image shows you all the files and folders included in Colorbox. If I was including the entire git repository in my plugin, it would take up about a meg of space. The unminified js file is only 29 KB. For me, it’s a no-brainer again. Grab just the js file (making sure the header has the license info and the URL) and toss that in my code. Why would I include a meg of data I’m not using just for the ‘ease’ of a git clone?

    And really I think that’s what’s happening. We’re all so excited to use a git pull or a submodule for the ease of inclusion, we stop thinking about exactly what we’re including in our code and how much weight we’re accidentally adding to it by dumping the whole library in there. Some libraries, like Complexify are really small. Then you have Twemoji, which weighs in at 135 MB, give or take.

    Do you really need the whole thing in your plugin, or can you just call their JS remotely from their CDN? That’s one of those things I’d certainly recommend for most people. Sure, I’m the biggest complainer about people off-loading code, but I also am reasonable and logical. Having everyone load a 130+ meg plugin isn’t sustainable, when Twitter’s made a nice way to do it remotely. That’s a service like a font that’s okay and understandable. After all, I hate that WordPress has to call the new fonts on the dashboard from Google, but at the same time including all the fonts for all the languages would make WordPress 11 megs more. That’s just crazy.

    Mind you, I had the shower idea that we could make WordPress download the fontpack when it downloaded the language packs, but we’re not anywhere near that yet. Still, that would kill two birds with one stone in my mind. It’s also illustrating the point of being thoughtful and reasonable about the assets we include in our plugins. You don’t need all the documentation files for a js plugin or most php ones either. It’s certainly easier to just drop the whole package into place, but it’s not always best. If one of those example files has a vulnerability, great, you just shot your user base in the foot.

    Limit what you put in your plugins. It’ll be easier that way.

  • Why I Pushed Back On Your Plugin

    Why I Pushed Back On Your Plugin

    The talk at WordCamp Ventura was actually called “Top 11 Reasons Your Plugin Was Rejected.” It was supposed to be ten, but Konstantine said “You should make it 11!” and I did but forgot to update the info for him. Bonus reason! You can see all the slides at helf.us/wcventura2014 right now. Video will be up on WordPress.tv soon.

    The truth is, there were only two reasons I actually would ever reject your plugin. If you never replied to our emails about any of the other reasons, we’ve probably rejected you. And if you’re claiming to be someone you’re not in a way we can’t prove, we’ve also rejected you. Let me cover the reasons here, for posterity. My slides are fully accessible, but people learn in different ways.

    And to understand how I made this list, I use aText to auto-reply to things. I just made a note of which ones I used to most, flipped ’em in reserve order, and thus I had a list.

    Trademarks

    This is the funny story one where I rejected Facebook because they used a gmail address and a DropBox URL for their plugin. I looked at it, thought they couldn’t possibly be Facebook, rejected, and got egg on my face. I’d do it again, though, because the point here is that if you’re making a plugin for a company and you’re using the company name, you need to make it clear and easily provable that you are them.

    If you’re doing contract work for, let’s say Pandora, to make an official plugin, you should make a new account on WordPress.org for that. Pandora-Dev or even just Pandora, using a pandora.com email address of course, and then making the plugin downloadable from pandora.com does a lot to make us think you’re real. If you don’t? I may reject you right away.

    Missing readme

    Otto used to let me pend a plugin because there was no readme. The number got so high, he made me stop. Now we only do this if you’re acting as a service or calling files remotely. Akismet and Disqus would require a readme, as would Twitter, but “Hello Dalai” would not. Of course you always should make a readme, but we don’t force you to do so.

    Phoning Home

    Akismet ‘phones home’ to it’s own servers to process your comments as spam or ham. That’s not what I mean here. We don’t permit plugins to phone home to your own server unless they’re acting as a service.

    A plugin that simply validates a license and ‘unlocks’ functionality that’s already in the plugin is not a service. A plugin that sends an email or message to your own servers when the plugin is installed and/or uininstalled is also not a service. A plugin that calls out to maxcdn or Amazon S3 for your JS and CSS files is also not a service.

    When you call out to another server you’re generally doing one of the following:

    1. Intentionally tracking the use of your plugin
    2. Creating an unnedded dependancy on another server being accessible in the interest of ‘controlling’ your css/js/images or keeping your plugin smaller
    3. Trying to control who uses your plugin

    None of those are okay. Think about it as a user. If you’re behind a firewall and can’t access Google (which is not an unrealistic situation), and your plugin requires Google’s version of the html5 shiv script, you just broke your plugin for probably the people who needed it most. Well done.

    Don’t do this. Make everything local to your plugin. It will actually run faster.

    Updates

    Related to phoning home is a plugin who calls back to it’s own servers for updates. Would you be surprised to learn that WordPress.org hosted plugins can already serve updates from WordPress.org? Of course not. You’ve seen that helpful alert on a site before. This is usually a hold over from self-hosting a plugin, or hosting it on Github, but it still needs to be removed.

    One of our guidelines is that you actually use the WordPress.org hosting to, you know, host your plugin. It’s not a directory for spamming, it’s for using to deploy too.

    Encryption

    Using encryption methods like base64() or p,a,c,k,e,r to hide your code or otherwise make it human-unreadable breaks our rule of “No obfuscated code.” It’s fine to compress your code and minify, but it’s not okay to hide it. All code must be human readable for inclusion in this repository.

    This is because we feel the spirit of GPL means that anyone can easily look at your code to reverse engineering what it does and expand on it. Easy being subjective of course, but you get the idea. Hiding your code is useless. It doesn’t help the community at all.

    GPL

    Speaking of… I don’t care your thoughts on GPL. I really don’t. What I care about is this: In order to be hosted on the WordPress.org repository for plugins, every single bit of code in your plugin must be compatible with the GPLv2 (or later) license. This isn’t negotiable, it’s not up for debate. It’s above and beyond what the GPL requires, and we know that. Which is why I just don’t care how you think we’re ‘interpreting the GPL wrong.’ That isn’t my point at all.

    You want to be hosted on WordPress.org? Your code must be 100% compatible with GPL. Deal with it.

    Calling wp-load or wp-blog-header or wp-config

    Including wp-config.php, wp-blog-header.php, wp-load.php, or pretty much any other WordPress core file that you have to call directly via an include is not a good idea and we cannot approve a plugin that does so unless it has a very good reason to load the file(s). It is prone to failure since not all WordPress installs have the exact same file structure.

    Usually plugins will include wp-config.php or wp-load.php in order to gain access to core WordPress functions, but there are much better ways to do this. It’s best if you tie your processing functions (the ones that need but don’t have access to core functions) into an action hook, such as “init” or “admin_init”.

    If you’re trying to use it because you need to access WordPress functions outside of WordPress, we’d actually much rather you didn’t do that at all. Your plugin should be inside WordPress, only accessible to people who are logged in and authorized, if it needs that kind of access. Your 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.

    Security

    Sanitize your post calls, sanitize your SQL queries, use the right code, and be smart. Don’t reinvent the wheel. This is the hardest one to get right and we miss a lot of things, but we want all instances where $_POST data is inserted into the database, or into a file, to be properly sanitized for security. And when you’re making database calls, it’s highly important to protect your code from SQL injection vulnerabilities via prepare() or other functions.

    This is just hard but we do try very hard to catch them all.

    Including your own copy of jQuery

    Please stop this.

    WordPress includes its own version of jQuery and many other similar JS files. If your code ‘requires’ an older version, fix your code to work with the one that comes with WordPress. Having ‘your own version’ means you run the risk of breaking everyone else’s plugins and themes, which makes you a bad person. Stop it. Use the ones with WordPress. It keeps your code smaller and your plugin smaller. Also if everyone uses the built in ones, then the first time one of you enqueues it, the faster everyone else’s code will run. Really. It’s magic.

    Calling wp-content directly

    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 and you make me cry. In addition, WordPress allows users to change the name of wp-content, so you would break anyone who choses to do so.

    Oh, and don’t worry about supporting WordPress 2.x or lower. We don’t encourage it nor expect you to do so, so save yourself some time and energy. Remember! If we stop supporting WP 3.8 and older, then people have to upgrade to use our plugins! Everyone wins!

    Not answering an email

    If you never reply to an email we sent you about why we pending your plugin, yes you got rejected. In order to keep the queue low and manageable, we reject plugins after seven days from our first reply (not your submission). This can also happen if you’re working with one of us on your plugin code and it takes a long time. This happens when a plugin is complicated, or if there’s a language barrier, or if we just can’t understand why you’re not making the changes we asked for.


    Did I reject your plugin and you still don’t understand why? Have a weird question about plugins? Ask away in the comments!