Half-Elf on Tech

Thoughts From a Professional Lesbian

Tag: plugins

  • Multisite And Theme Activation Checks

    Multisite And Theme Activation Checks

    Earlier this week I talked about how you can’t actually network activate all plugins. As it happens, I have a plugin for a specific theme, and it doesn’t like being Network Activated.

    The plugin is Genesis Simple Hooks and, logically, it only works if you have a Genesis theme installed and active.

    How It Works

    When the plugin is activated it properly runs the following activation hook:

    register_activation_hook( __FILE__, 'simplehooks_activation' );
    /**
     * This function runs on plugin activation. It checks to make sure Genesis
     * or a Genesis child theme is active. If not, it deactivates itself.
     *
     * @since 0.1.0
     */
    function simplehooks_activation() {
    
    	if ( ! defined( 'PARENT_THEME_VERSION' ) || ! version_compare( PARENT_THEME_VERSION, '2.1.0', '>=' ) )
    		simplehooks_deactivate( '2.1.0', '3.9.2' );
    
    }
    

    First this looks to see if there’s no parent theme or if the parent theme version is less than 2.1.0, and if either of those are the case (that is if there’s no parent theme, and the version is less than…) it calls simplehooks_deactivate.

    /**
     * Deactivate Simple Hooks.
     *
     * This function deactivates Simple Hooks.
     *
     * @since 1.8.0.2
     */
    function simplehooks_deactivate( $genesis_version = '2.1.0', $wp_version = '3.9.2' ) {
    
    	deactivate_plugins( plugin_basename( __FILE__ ) );
    	wp_die( sprintf( __( 'Sorry, you cannot run Simple Hooks without WordPress %s and <a href="%s">Genesis %s</a>, or greater.', 'genesis-simple-hooks' ), $wp_version, 'http://my.studiopress.com/?download_id=91046d629e74d525b3f2978e404e7ffa', $genesis_version ) );
    
    }
    

    And you know what? That sucks. It never checks for Genesis as the parent theme name, based on the assumption that only Genesis themes use the define of PARENT_THEME_VERSION which probably made sense a few years ago, but doesn’t anymore. And more importantly for this explanation, you can’t network activate it because unless you’re lucky enough to be running a Genesis child theme on the main site of your network, it won’t activate.

    Worse, it will throw errors on sites that aren’t.

    How I’d Fix It

    First I made a list of the action/function calls that require a Genesis Child Theme to be active in order to run properly: simplehooks_load_textdomain, simplehooks_init, simplehooks_execute_hooks

    Then I moved them all to their own section and wrapped them in a check:

    if ( simplehooks_can_run() ) {
    	add_action( 'plugins_loaded', 'simplehooks_load_textdomain' );
    	add_action( 'genesis_init', 'simplehooks_init', 20 );
    	add_action( 'genesis_init', 'simplehooks_execute_hooks', 20 );
    }
    

    What’s simplehooks_can_run? That’s a new function I’ve created to check for the requirements:

    /**
     * Can Simple Hooks Run
     *
     * This function checks if the requirements for Simple Hooks are met.
     *
     * @since 2.3.0
     */
    function simplehooks_can_run() {
    	
    	global $wp_version;
    	$my_theme = wp_get_theme();
    	$genesis_theme = wp_get_theme( 'genesis' );
    
    	if ( ( version_compare( $genesis_theme->get( 'Version' ), '2.1.0', '>=' ) && !is_null( $genesis_theme->get( 'Version' ) ) && !empty( $genesis_theme->get( 'Version' ) ) ) && ( $my_theme->get( 'Name' ) == 'Genesis' ||  $my_theme->get( 'Template' ) == 'genesis' ) ) {
    		return true;
    	}
    		
    	return false;
    	
    }
    

    What this checks is if the parent theme is 2.1.0 or higher and, if it is, if the theme or it’s parent is named Genesis. By having it use return I don’t have to check if it’s true or false, the if-check is smart enough for me not to do it explicitly.

    Finally I changed the if check in simplehooks_activation to look like this:

    	if ( !is_multisite() && !simplehooks_can_run() )
    

    What this isn’t doing is checking for WordPress 3.9.x anymore, as it’s 2016 and that was 2014 and I’m not worried about it. If I was, I’d toss && version_compare( $wp_version, '3.9.2', '>=' ) to the if-check in simplehooks_can_use to CYA.

    This also isn’t giving you an error on Multisite. That means if you network activate the plugin and, on Multisite, go to a site that does not have Genesis running, you don’t get an error. This is by design. There’s no point in telling a site-admin “Hey there’s this Genesis thing you can’t have because you’re not using it! NEENER!”

    I’m actually using this here on this site. If you’re interested at testing it out, grab it from my Github repo.

    Pull requests welcome.

  • 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.

  • PSA: DreamObjects URL Changes

    PSA: DreamObjects URL Changes

    If you use my DreamObjects plugins, don’t worry, I’ll have them updated before September.

    What Changed?

    As part of ongoing service improvements, DreamHost made a subtle, but critical, change to how everyone accesses DreamObjects. Specifically, they changed the DreamObjects hostname to prepare for upcoming enhancements.

    Old hostname: objects.dreamhost.com
    New hostname: objects-us-east-1.dream.io

    I’m a developer who uses DreamObjects. What do I need to do?

    If you were ever using the URL objects.dreamhost.com in your site, you need to change it to objects-us-east-1.dream.io and everything will be awesome.

    Is it really that simple?

    Not always. For some plugins and code, it is that simple. For my backup plugin, I added in a new option:

    if ( !get_option('dh-do-hostname')) {update_option( 'dh-do-hostname', 'objects-us-east-1.dream.io' );}

    This way I can later make it a dropdown for people to select as they want.

    But for my CDN plugin there’s a major issue. You see, the way it works is that it updates the URLs of images within your posts. It, literally, edits your post content. And that means a change has to go back and fix all the URLs. I had to write some fairly weird code to do this. I’m still testing it fully, and I think it’ll do everything I need, except it’s not going to perfect.

    Right now it does it’s utmost best to fix any URLs on a site, however it will only fix the ones for images it can detect are on DreamSpeed. I am aware that some people with a phenomenal number of images manually copied their uploads folder to DreamObjects and ran a command like this:

    wp search-replace 'example.com/wp-content/uploads' 'objects.dreamhost.com/bucketname/wp-content/uploads'

    Or maybe they used the InterconnectDB script or another plugin. Those people you’re going to have to watch out for and inform in a useful way as to how to fix it.

    I’m an end user. Do I need to care?

    Yes. You do. Do not try to fix it on your own just yet. Not until the plugins you use have updated and said they’ve corrected the issue. Then read the FAQ or any alerts to make sure you don’t need to do anything else. If you’re not using DreamObjects as a CDN, this should be pretty painless.

    If you did, or if you moved it all manually, you will have to do it again before September 5th, 2016 or all images will break.

    Thankfully, if you’re on DreamHost, you can run the following command:

    wp search-replace objects.dreamhost.com objects-us-east-1.dream.io

    That will upgrade everything for you. If you’re not, you’ll need to use the search/replace tool of your choice.

    Again: WAIT until any plugin you use for this has been updated. I personally contacted everyone in the WordPress.org repository and informed them about it, but since I know that’s not perfect, I’m doing this too.

    This sucks!

    I know. It does. But I don’t see a better way around it.

    When will your plugins be updated?

    Before the end of May. I just want to test it on as many large sites as I can find.

  • The Despair of Licensed Updates

    The Despair of Licensed Updates

    I am a massive proponent of people making money off of plugins. I think they can and should find a way to create a business in this ecosystem we’ve created.

    There’s a problem with the approach of some of these products, and in a way we created it ourself, and it hit WordPress 4.5.

    There is a plugin, it doesn’t matter which one, that’s a premium plugin. It’s not available for free on WordPress.org. You have to buy it, get a license, enter the license into the plugin, and in that way get updates. That’s fine. But there’s a complication. Actually a couple.

    Licenses Expire and People Aren’t Informed

    That’s a big ‘no kidding’ moment, but they do expire. And people don’t always notice that their license expired. Even if you post a big sign on the dashboard and email them.

    Worse, people don’t know they have license. One of the major problems with software, when purchased for a company, is ownership. If I buy an app on the company dime, it’s their app. But when I buy an app for someone I’m building a site, and I pay for it myself, even if I charge them for it, who owns it? Who keeps the license? Who has the information for running a site?

    This is an aspect of WebDevelopment where we collapse, regularly. Not just WordPress, every single person who builds websites for someone has screwed this up at least once. Either the information isn’t clear, or it’s not there at all. Regardless, what happens in the end is you have someone who lacks the information they need to keep their company going.

    The Plugin Is Often Bundled in Themes

    This is worse than you think. The official directions for this says that if your theme bundled it, and you need an upgrade, you need to wait for the theme to upgrade or you need to buy a license yourself. That’s perfect, to me, except for the problem I mentioned before. People don’t know. I’m not sure how they should know. But those bundlers, they’re so very problematic because they remove users one more step from the information.

    If I buy a theme, and it has a library inside it, it’s the job of the theme developer to update that theme regularly, test it with WordPress before the new version comes out, and push fixes. If I buy a plugin, ditto. When the stream cross, though, is where we have the drama. Because I know I bought the theme, but I do not know that I bought this mystery plugin, hiding deep inside. Now it’s the theme owner’s job to update and make sure I get the information right away.

    Pretty Much No One Gets It Right

    Not even people I respect get this right all the time.

    Let’s say you’ve written a plugin and have decided to handle all the updates yourself. I buy it, install it, and it works, everyone’s happy. What happens when I stop paying my license? Well I stop getting updates, that’s for sure. But do I still get notifications about them? Do I get an email? Do I even get an update?

    There are some plugins that are free from pay-walled sites, but if you don’t have an active license for that free plugin, you will not get updates. At first I thought it was strange, since if I had a free plugin why wouldn’t I put it up on WordPress.org, right? But then I realized they’re creating the relationship. Once you ‘buy’ the free plugin, you have an account and information in their system. If it’s free, you’re the product.

    All that aside, it comes back to the problem of what happens if that license, free or not, lapses? You could be annoying and pop up on the settings page “Hey! The license expired!” but people hate that and ignore it. You could email, but they ignore that too. There really isn’t a great way to remind people that (a) the license expired and (b) there are updates available.

    Or Is There…?

    What if the updater kept checking, license expired or not, and when you clicked to upgrade it alerted you?

    You license for Foobar has expired. Please renew it in order to upgrade.

    What if you got this email?

    Hey, you bought Foobar back in 2014 and that license lapsed. Normally I’d never bother you, but today I’ve pushed a major security fix. Since this is a security release, I’m offering you a discount. It’s already applied to your account, just log in and you can buy the upgrade at 50% off. If you’re not using Foobar anymore, click here and I’ll have your account flagged so we don’t bother you about this again.

    How happy would you be to find out someone saved your soy bacon?

    This would require the original developers to have your information, which they probably do, and some way to track those two things. That is, did your license lapse and do you care? That’s all they need to track and only one is an ‘extra’ since I’m reasonably sure everyone tracks the license.

    Make It Easy

    If you make it easy for someone to know “This has been expired, here is one click to pay” people will pay. Yes, we love free, but we love easy even more. If you make it easy to pay, people will renew and pay. If you inform them of security issues, they will pay and upgrade. If you push them, the good way, about your updates, and make sure they know, they will be safer.

    And then, when WordPress upgrades, your users won’t hate you.

  • Fauxgo and Rickroll

    Fauxgo and Rickroll

    April Fools is today. I hate April Fools ‘jokes’ as pretty much all of them are cruel.

    Now that we have that out of the way, I’d like to tell you about two plugins I created that are pretty much useless but educational.

    Rickroll

    Released in the WordPress.org plugin directory, this plugin changes all your videos to the official Rickroll video. All. Your. Videos. The point it was made for is that you really can run a filter to do some pretty impressive things with videos. Including replace them. You can take the logic of this plugin and apply it by filtering (say) all videos embedded in comments made by a specific person. That would really mess with them. Or you could possibly call YouTube’s api, check the rating of the video, and if it’s over a certain amount, show a default instead.

    Download: WordPress.org – Rickroll

    Fauxgo

    This plugin replaces the WordPress logo with a Fauxgo. Why? Someone complained to me that you couldn’t rebrand WordPress entirely. So I did. The thing about this plugin was that it was deucedly complicated to make work right. Most of the trouble was the CSS is ‘weird.’ But once you have it installed, everything looks different ‘ish’ and suddenly you’re not sure what the logo is anymore.

    I plan to edit this and write it as using an SVG icon instead of the icon font.

    Download: Github – Fauxgo

  • Composer and WordPress’ Plugin Preflight Check

    Composer and WordPress’ Plugin Preflight Check

    When you upload a plugin to WordPress.org’s repository, it does some pre-flight checks to make sure the code is okay. More than once it’s caught a missing ; for me. But one day it caught this:

    PHP error in: myplugin/tags/0.6/aws/Doctrine/Common/Proxy/ProxyGenerator.php:
    Errors parsing myplugin/tags/0.6/aws/Doctrine/Common/Proxy/ProxyGenerator.php
    

    I stared for a moment, Googled to be sure, and sighed when I figured out that what was happening was my library was a PHP 5.6+ version and that didn’t pass the 5.4 checks.

    Regardless of how annoying this is, it’s not too hard to fix. No, I’m not going to tell you how to avoid the scan, nor am I going to talk about the stupidity of the scan. Instead I’d like to tell you how to get around this problem.

    It’s so easy, too.

    You see, my issue is that I use Composer to build my resource libraries and in my composer.json file, I have this:

    "require": {
    	"aws/aws-sdk-php": "2.7.*",
    	"doctrine/orm": "*",
    	"monolog/monolog": "*"
    },
    

    And for what it’s worth, this is great. I have it download all my libraries. Then I have it run a script to copy over just what I need to my aws folder and I have Git and SVN ignore the vendor folder. Everyone wins! The problem is that Composer sees that my laptop is running PHP 5.6 and so it installed version

    Fix PHP on Your Computer

    Since I use Homebrew on my Mac, this is super easy for me.

    $ brew unlink php56
    $ brew install php54
    

    Then when I re-run composer update it will install what I need.

    The problem here, though, is that this is tied to a specific computer, and if a coworker downloads my stuff and plans to fork it, they’re SOL and merges fail and it’s sad times all around. So with that in mind, uninstall 5.4 and go back to 5.6.

    If you have a problem with installing 5.6, run it this way:

    $ xcode-select --install
    $ brew unlink php54
    $ brew install php56
    

    Fix PHP in Composer

    I like this fix better. Put this above your require statement and you’ll force Composer to build based on PHP 5.4:

    "config": {
    		"platform": {
    		"php": "5.4"
    	 }
    },
    

    Now we’re cooking with fire. This works for everyone, it forces Composer to download 5.4 compatible libraries, and my very long transmission worked.

    Is This Perfect?

    Obviously not. There are going to be cases where your code absolutely has to be the code for PHP 5.5 and up. The latest version of the AWS SDK is v3, for example. But it’s for 5.5 only. I personally don’t feel there’s enough of a buy-in for PHP 5.5 and up yet to make an issue of it. And having no check at all won’t fly.