Half-Elf on Tech

Thoughts From a Professional Lesbian

Tag: plugins

  • Mailbag: Multisite or Not?

    Mailbag: Multisite or Not?

    From Ian:

    Hi, I wanted to comment on one of your articles and see if any one would provide feedback, but the comments are closed. https://halfelf.org/2011/dont-use-wordpress-multisite/ I have been avoiding using Multisite as you recommend. Several have tried to get me to go that way, but it didn’t seem right. So in the cases of when a business wants to have independent sites that share the main sites content, what would you recommend?

    There was more to the email but it boils down to this. They want a site that has all the local content and all the main brand’s content.

    When you get to the point of ‘sharing’ content, Multisite is less and less of a winner. While we’re still in a pre JSON API world, which would make this a bit easier, I lean towards the basic simplicity of categories.

    I’d have a main category for ‘news’ and another for each independent site (say ‘locale1’). Then I’d craft my theme to pull in posts from ‘news’ and ‘locale1’ and style it per the brand designation of each locale.

    The plugin Groups may be what you want to control access for things.

    One site. Easy to cross-relate content. Done.

    No multisite.

  • Making a WP-CLI Plugin

    Making a WP-CLI Plugin

    I love wp-cli. It makes my life so much easier in so many ways.

    I’ve added in some basic commands to some of my plugins because it makes things easier for others. And as it happens, adding wp-cli commands to your plugins isn’t actually all that hard. But did you know you don’t have to?

    The Basic Code

    For our example I’m going to make a command called halfelf and it’s going to have a sub-command called ‘stats’ which outputs that the HalfElf is alive. And that looks like this:

    WP_CLI::add_command( 'halfelf', 'HalfElf_CLI' );
    
    class HalfElf_CLI extends WP_CLI_Command {
    
    	/**
    	 * Get HalfElf Stats
    	 *
    	 * ## EXAMPLES
    	 *
    	 * wp halfelf stats
    	 *
    	 */
    	public function stats( ) {
    		WP_CLI::success( __( 'HalfElf is currently alive.', 'halfelf' ) );
            }
    }
    

    The docblock controls the output for the help screen, which is possibly the most brilliant thing about it.

    Of course, all that does is make a command that doesn’t rely on any WordPress plugin, which is cool. Here’s a different example. In this one, I’m triggering a command to my logger function to reset the backup log or to view it:

    
    	/**
    	 * See Log Output
    	 *
    	 * ## OPTIONS
    	 *
    	 * empty: Leave it empty to output the log
    	 * 
    	 * reset: Reset the log
    	 *
    	 * wp dreamobjects logs
    	 * wp dreamobjects logs reset
    	 *
    	 */
    
    	public function log( $args, $assoc_args  ) {
    		if ( isset( $args[0] ) && 'reset' !== $args[0] ) {
    			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'dreamobjects' ), $args[0] ) );
    		} elseif ( 'reset' == $args[0] ) {
    			DHDO::logger('reset');
    			WP_CLI::success( 'Backup log reset' );
    		} else {
    			file_get_contents( './log.txt' );
    		}
    	}
    

    Because wp-cli runs as WordPress, it has access to WordPress commands and functions.

    But. Where do we put this file?

    In a WordPress Plugin

    If you want to use that code in a WordPress plugin, you put it in a file called wp-cli.php (or whatever you want) and then call it in your main WordPress plugin file like this:

    if ( defined( 'WP_CLI' ) && WP_CLI ) {
    	require_once( 'wp-cli.php' );
    }
    

    If you do that, I would recommend putting this at the top of your wp-cli.php file.

    if (!defined('ABSPATH')) {
        die();
    }
    
    // Bail if WP-CLI is not present
    if ( !defined( 'WP_CLI' ) ) return;
    

    That will prevent people from calling the file directly or in other ways.

    In the case of my DHDO code, I added in this check:

    if( class_exists( 'DHDO' ) ) {
    	WP_CLI::add_command( 'dreamobjects', 'DreamObjects_Command' );
    }
    

    That means if (for whatever reason) DHDO can’t be found, it prevents you from doing things.

    On It’s Own

    But. That first command doesn’t need a plugin, does it? So if it doesn’t need a plugin, could I still use them? The answer is of course. The wp-cli.php file is the same, but where you put it changes. This will all be done in SSH. You could do it in SFTP if you wanted.

    In your home (or root) directory we have to make a folder called .wp-cli – please note the period, it’s like your .htaccess file. Except it’s a folder. In there you’re going to make a folder called commands and in that folder we’ll put another one for our commands. Like if I’m putting my HalfElf commands out there, I’d make a folder called halfelf and in there I put my command file command.php

    That gives me this: ~/.wp-cli/commands/halfelf/command.php

    However that doesn’t act like plugins or themes or mu-plugins, you have to add the command to another file. I know, I know. Make a file called config.yml and put it in .wp-cli so you have this: ~/.wp-cli/config.yml

    The content of that file is this:

    require:
      - commands/halfelf/command.php
      - commands/some-other/command.php
    

    And that’s it! Now you’ve got some command line tools for your WordPress site!

  • Name Collisions

    Name Collisions

    Many many years ago I played MUSHes. One of the games was PernMUSH (which apparently is inactive now). PernMUSH took place on the world of Pern, and you had the chance to be a dragon rider. Which was kind of the Thing to Be ™. One of the ‘quirks’ of the game was that every character had to have a unique name, and so did each dragon. When I started playing, I didn’t really understand this. Today I know that it’s because of name collisions.

    A “name collision” is a problem not solely endemic to computers, but it comes up there an awful lot, whereby you must have a unique identifier to know what each ‘thing’ is. For example, in WordPress every post has two unique identifiers. It has a post ID, which is a number given to the post when it’s stored in the database. If you use ‘ugly’ permalinks, you’ll see this as example.com/?p=123 – that 123 is the post ID. But if you use pretty permalinks (like I do here — example.com/my-cool-post/) then you have to have only one ‘post’ with that name.

    You, literally, cannot have two posts with the same ID or name. Makes sense, right?

    On PernMUSH we had everything have a unique ID as well as a unique ‘nice’ name. But then when dragons were introduced, you had to give them unique names as well. This was not for frivolous reasons nor pretty special snowflake ones. While it was perfectly understandable to have a hundred rooms named “Bedroom,” the code for the dragons allowed them to all talk to each other and send private messages. They were, basically, our cell phones. Dragon Ath had to be able to talk to dragon Bth, and in order to ensure that worked properly without everyone having to type dtu #12724=message we had to have the code written such that someone could type dtu bth=message and that meant each name had to be unique.

    This would have been fine and dandy as it was except for one small problem. PernMUSH wasn’t the only MUSH based on Pern. There was also a game called SouCon, which took place on the Southern Continent. And transfers between the games were allowed. This added in a wrinkle that now PernMUSH and SouCon had to be sure that everyone on both games had a unique name and dragon name.

    It was quickly determined that they wouldn’t bother with human names. If J’cob on SouCon came to visit PernMUSH, which already had a J’cob, then SouCon’s J’cob would use a different name like Jy’cob. But for whatever reason it was decided that the dragon names on both games were going to be unique. Thus the “All The Weyrs List” was created. That list (which still exists at dragons.pernmu.com ) was a mostly honor system site where you would email in your ‘hatching records’ with who’d impressed and to what dragon and what color and who were the parents. The list would be updated. Then the next time anyone had a hatching, they’d search that page for the dragon names they wanted to use. If the name was there, then then couldn’t use it. Done.

    Of course this wasn’t perfect. Anything based on the honor system is bound to have a few bad eggs. After 10, 15, 20 years, the ability to give people the name they ‘want’ starts to chaff against the tacit agreement not to repeat a name. At some point, I know some games gave up and let people have whatever name they wanted, and transfers could cope.

    What does all this have to do with anything?

    On the WordPress.org servers, where we list all the plugins approved by the team, each plugin has a unique slug that cannot be changed. I have a plugin called Impostercide, which has the slug of impostercide and it’s the only one. No one else can submit a plugin with that name. For the most part, this worked fine. If someone else wanted to make a plugin with that name, they were free to do so but it just wouldn’t be on WordPress.org and that was okay.

    Then we shot ourselves in the spirit of making life easier. Today WordPress updates your plugins and themes by using an API that calls back to the wordpress.org servers. That API check sees if Impostercide on your install of WordPress is older than the one on wordpress.org and, if so, alerts you to update. You press a button and your plugin is updated. It’s magic. It’s gold. It’s great. If you’re that person who wrote your own plugin, not on wordpress.org, you can hook into the update code and have it update from other servers. It’s brilliant.

    Except what if you’re that person who has their own plugin named Impostercide? The obvious answer is that you can just rename your folder and off you go. That doesn’t fix the thousands of people who just upgraded themselves to my version, though. They’re having a bad day. Also what if someone submits a plugin called impostercide-two? Now you have the same problem all over again. Other people will tell you to bump the version to something the real Impostercide will never use. But again, that doesn’t hold up since what if Impostercide does?

    The actual fix is to tell WordPress not to check for updates for that specific plugin.

    The awesome Mark Jaquith posted about this in 2009. You can code a plugin to tell WordPress to not check for updates for it. This does put the onus on people who are writing the plugins not hosted on wordpress.org though, which is and isn’t fair. There’s a movement to allow a new plugin header to prevent these things in trac ticket 32101, which boils down to the idea that if those non-org hosted plugins can flag themselves as ‘I’m not from .org’ then the API stops trying to update them.

    I think that it would be a good idea to have an easy way for people to flag their plugins as not being hosted. The alternative would be an honor system method, where everyone registers their plugin slugs and all submissions to wordpress.org is checked against that. But that falls apart quickly the day one person forgets to do it. With a way to easily kill the API check, we can allow non-org hosted plugins to very simply protect themselves, and their users, from being stomped on.

    As for the risk that someone might edit their own locally installed copy of Jetpack to have that header because they’re tired of updates, well, we can’t stop you from shooting yourself. I just hope people are smart enough to understand that you don’t edit core and you don’t edit plugins and you don’t edit themes. You make child themes, you use other plugins, and you use filters and hooks.

  • Mailbag: What’s The Diff?

    Mailbag: What’s The Diff?

    How do you compare two plugins to see if one’s a fork or stolen? What’s the difference between a fork and a clone?

    Sometimes people like to ‘steal’ plugins. This normally happens when someone takes a premium (purchase only behind a firewall) plugin and attempts to give it away for free on WordPress.org. They tend to violate copyright when they do that, but also it’s just not a cool thing to do and I find it distasteful.

    Often we catch these since people who steal like that aren’t always very smart and we recognize code that is generally well known and popular. But more often we don’t catch it because CodeCanyon has 3400+ plugins and WordPress.org has 37k+ and that’s a lot to compare and remember. And that’s when we get an email from a plugin developer who says “So and so stole my work!”

    What do we do? We ask them for a copy of their code, in a zip, and say we’ll compare. Most developers are happy to do that. We’re a trustworthy lot, otherwise we wouldn’t be on the plugin team (yes, being a good, moral, and ethical person is very important). Once I have the zip, I download the claimed-clone and compare them line by line.

    Well. Not really.

    My toy is DeltaWalker.

    With DeltaWalker I can compare two zip files without having to open the zips and look at each line. In the below example, I’ve got Akismet 3.0 vs 3.1.1 and I can see every single change just by tossing the zips in as files to compare:

    DeltaWalker Example: Akismet 3.0 vs 3.1

    DeltaWalker is so good, it helps me compare the readmes so I can easily see that someone has just fiddled with the original and not written their own.

    What I look for is code style, formatting, and naming conventions. Rarely do two separate individuals use the same code formatting (tabs vs spaces vs tabs+space etc), so seeing their additions will jump out. Similarly, the code style, their internal logic, is often wildly different. Same with naming conventions.

    When you look at it, it will jump out at you that generally all anyone does is rename functions or classes. They remove credit and copyright information too, and sometimes they mess with the help docs. Rarely do they add anything of substance. If they do then it’s a legit fork and we’ll push them to restore credit and copyright information.

    But since it’s generally not, we will quickly see that the plugin is a direct, no feature added, copy, and remove it.

    If this happens to you, if your plugin is ‘taken’ and duplicated without any code being added, email pluginsATwordpress.org with a copy of your original plugin (and a link to perhaps prove it’s you) and we’ll look at it. If you get an email where we tell you that your plugin is a copy, take a moment to review your code and feel free to talk with us about it. A ‘one line’ change actually MAY be acceptable as a fork, but it’s rare unless it’s adding in a massive feature, or totally changing functionality.

    Above all, remember this:

    Despite the fact that all plugins in our directory are licensed under the GPL or compatible licenses, we do not allow direct copies of other plugins to be re-listed under somebody else’s name. “Forking” is acceptable only when the resulting fork is of a substantial nature, or when the original plugin is no longer updated or supported.

    Always try to contribute back to the original plugin’s authors if you wish to make improvements to the original plugin, instead of creating an entirely new version and thus creating incompatibilities and duplicated code in the repository.

    Alternatively, write your own plugin to perform the functionality you want to have, drawing on ideas from the original. Ideas can always be copied.

  • Mailbag: A Case Against (Part Of) Jetpack

    Mailbag: A Case Against (Part Of) Jetpack

    You told me to try Photon, but I noticed you’re not using it on all your sites. What gives?

    When people ask me how to speed up their sites for images, I often recommend Jetpack for the CDN boost. It’s a double edged sword, though. While Photon does two things amazingly well (resize images and put them up on a CDN), it’s hosted on wp.com which means I can’t use it.

    What? Why not? No, it’s not that I have something against wordpress.com, it’s that other people do. Like China, Pakistan, and Turkey.

    The list is probably longer. But those places, among others, block WordPress.com which means every module of Jetpack that phones home (stats, photon, tiled galleries, LaTeX, related posts, etc) cannot be active on my sites that have a large enough user-base in those places. When I leave those Jetpack features on, the site grinds to a halt for them, which is a terrible experience for my (often non-technical users).

    Now that said, I do still use the stats plugins on all my sites with Jetpack. It’s a pretty safe loader to run, and it doesn’t slow the site down terribly (see Issue #566 for the code magic). Photon on the other hand I had to disable entirely because my poor users in China were complaining they could see nothing. I can live with a little delay for loading. I can’t live with an image heavy site not working.

    So should you use Photon? Yes! Unless your visitors are blocked by WordPress.com.

  • Defines, Variables, and Plugin Dirs

    Defines, Variables, and Plugin Dirs

    If you’ve spent any time looking at PHP code, then you’ve seen defines and variables

    Defines

    A define looks like this:

    define('SOMETHING', true);
    

    This makes a global constant that can be used anywhere. In the case of WordPress, it means they can be used across multiple plugins or themes. This is very useful if you make a suite of plugins that all have the possibility of using the same API key. Then you can tell a user to put define('MY_PLUGIN_API', '123456'); in their wp-config.php file, and tell your code to check for the define.

    A define also cannot be redefined. If you call it twice, you get errors, so you should be as unique as possible when creating yours.

    Variables

    A variable, meanwhile, looks like this:

    $SOMETHING = true;
    

    Variables only exist where they are, so if I have one in one function, I may not be able to call it in another. You can make global variables if needed, and in fact if you don’t, the variable won’t be available to all functions.

    Which Should I Use?

    Keeping in mind that defines are constants and variables are, well, variables, the idea is that a constant should be used for things that should not change in the running of the code. It also makes your code easier to maintain if the things that must be constant are explicitly so.

    So what’s a good define? Actually not really this:

    define('MY_PLUGIN_VERSION', '1.0');
    

    This is a constant, it’s something you should be setting and it shouldn’t be overwritten, but actually I’d want to make it a database field to check on before upgrading. Remember, you should be changing that define on upgrade, so having it be a declared constant is a little odd. Now that said, it is a good one when you consider you don’t want someone to willy-nilly override it. Except … what if you do? What if you want someone to be able to change it back to re-run an upgrade?

    So then really not this either:

    define( 'MY_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
    define( 'MY_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
    include( MY_PLUGIN_PATH . 'lib/asset.php') ;
    

    Someone’s probably thinking that their plugin directory is a constant and, thus, should be defined like that. Maybe, but it’s a pointless define in WordPress. You already have plugins_url() (which this example isn’t using) but it’s really where people use that code that makes no sense. That’s why I included the second line there. That’s what they use it for, and it means two lines of code for one function call.

    This actually magically becomes a good define when you have a complex plugin with multiple files and you need to grab the main plugin directory in sub-files. But if your plugin is one file (and yes, that’s where I see it the most), it’s overkill.

    This is a good define:

    define('MY_PLUGIN_APIKEY', '123456');
    

    But that should never be in your code itself. Not for WordPress at least. That should be in the wp-config.php, like I mentioned before.

    Basically … there aren’t great reasons to use defines unless you want things that never change. define('MY_PLUGIN_SUPPORT_HOME','http://help.example.com'); would make sense to me, as would other contact info you want to make sure is never subverted.

    What do you think define is best for?

    I’m interested to hear what you guys like to use defines for. I’m certainly guilty of using them for some pretty silly reason.