Half-Elf on Tech

Thoughts From a Professional Lesbian

Tag: wordpress

  • WP Comments ReplyTwo

    WP Comments ReplyTwo

    WPTavern has this cool thing where, without threaded comments, you still have a reply link, AND it creates an automatic link back to the original comment.

    Let me rewind. If you have threaded comments, then you get a ‘reply’ link on the bottom of a comment, and it lets you make a threaded reply. Yay! There are problems with this, though, as after a while, if you get nested deep enough, you can’t reply under anymore, and have to go up to click reply on the previous post and on and on.

    Threaded PipeBack eons ago, WPTavern solved this with a function, and like all great sites documented it! The problem? The documentation was busted. Now, yes, I did ping Jeff about this and asked him how it went, but in the meantime, I was impatient and looked up a plugin that almost fit my bill.

    Enter @ Reply (aka Reply To), which add in a reply link to all comments! Plus is gives you ‘Twitter like’ @replies, so when you comment, it starts “@foo:” automatically. This is just like what WPTavern has, perfect! Except… not quite.

    My problems became two:

    1. I want all comments to have a ‘reply’ link.
    2. I don’t want the hover over image.

    And I solved this with two code chunks: a plugin and a theme.

    See, by default WP stops showing you the ‘reply’ link when you can’t nest anymore. To change that, you have to edit how your theme calls comments. Or rather, you have to change the comment_reply_link() call.

    The Theme Code

    I’m already customizing my comments in Genesis so this was surprisingly simple.

    What was this:

    <div class="comment-reply">
        <?php comment_reply_link( array_merge( $args, array( 'depth' => $depth, 'max_depth' => $args['max_depth'] ) ) ); ?>
        </div>
    

    Becomes this:

    <div class="comment-reply">
        <?php comment_reply_link( array_merge( $args, array( 'depth' => 1, 'max_depth' => 2 ) ) ); ?>
    </div>
    

    Basically we’re lying to WP and saying we always show the link. I got that idea from this stackexchange post, and as I understand it, we’re tricking WP by saying that every post is depth of 1, and has a max of 2, instead of letting it figure it out on it’s own. This is probably inelegant, but I couldn’t find another way to have the reply link always on.

    The Plugin Code

    nested-commentsThis was easier, in that I took the existing code, cleaned it up and removed the reply image, and that was pretty much it. Downside is that this does not work on every site. Notably, it doesn’t work unless threaded comments are turned on. When they’re not, it does a double-refresh.

    That said, this is probably never going to be a plugin for the masses, so I’m content with having it work this way for me. Even if I only thread one comment at a time, it would let me group them together and get the @-reply. It’s already rather popular on the site I intended it for, and people like it.

    So here’s the code:

    class AtReplyTwoHELF {
        public function __construct() {
            add_action( 'init', array( &$this, 'init' ) );
        }
    
        public function init() {
    		if (!is_admin()) {
    			 add_action('comment_form', array( $this, 'r2_reply_js'));
    			 add_filter('comment_reply_link', array( $this,'r2_reply'));
    		}
    	}
    
    	public function r2_reply_js() {
    	?>
    		<script type="text/javascript">
    			//<![CDATA[
    			function r2_replyTwo(commentID, author) {
    				var inReplyTo = '@<a href="' + commentID + '">' + author + '<\/a>: ';
    				var myField;
    				if (document.getElementById('comment') && document.getElementById('comment').type == 'textarea') {
    					myField = document.getElementById('comment');
    				} else {
    					return false;
    				}
    				if (document.selection) {
    					myField.focus();
    					sel = document.selection.createRange();
    					sel.text = inReplyTo;
    					myField.focus();
    				}
    				else if (myField.selectionStart || myField.selectionStart == '0') {
    					var startPos = myField.selectionStart;
    					var endPos = myField.selectionEnd;
    					var cursorPos = endPos;
    					myField.value = myField.value.substring(0, startPos) + inReplyTo + myField.value.substring(endPos, myField.value.length);
    					cursorPos += inReplyTo.length;
    					myField.focus();
    					myField.selectionStart = cursorPos;
    					myField.selectionEnd = cursorPos;
    				}
    				else {
    					myField.value += inReplyTo;
    					myField.focus();
    				}
    			}
    			//]]>
    		</script>
    	<?php
    	}
    
    	public function r2_reply($reply_link) {
    		 $comment_ID = '#comment-' . get_comment_ID();
    		 $comment_author = esc_html(get_comment_author());
    		 $r2_reply_link = 'onclick=\'return r2_replyTwo("' . $comment_ID . '", "' . $comment_author . '"),';
    		 return str_replace("onclick='return", "$r2_reply_link", $reply_link);
    	}
    }
    
    new AtReplyTwoHELF();
    

    Most of the javascript stuff is copy/pasta for me (just left of witchcraft), and I left it barebones (no configurations) on purpose. Simple is as simple does.

    Suggestions and tweaks welcome!

  • Separate Users Are Good

    When you create a new domain on DreamHost, you can chose to make a ‘new’ user to ‘own’ the site, or use an existing one. There are pros and cons to both, but for anyone who comes from the cPanel world (where separate accounts are de rigueur), it’s pretty normal to expect your separate site to have a separate login name and password.

    Explaining how all this works on DreamHost is a little different, because we have users and then we have users and … well let me explain.

    There’s more than one kind of user

    The first type of ‘user’ you have at DreamHost is your panel user. This user is the one you make when you sign up, and it’s usually your email. Don’t share this password with anyone, okay?

    Next we have your ‘users’ which you can find in your panel. Those users are the ones who have access to things like ‘shell’ and ‘sftp’ and so on.

    Then there are also those ‘other’ users you think of, like the login accounts on your blog, or your email, or maybe even the billing account for DreamHost.

    When I talk about separate users, I’m only talking about the ones who have access to shell and stuff.

    Users own sites

    Those user accounts own sites. That means I have a specific user who ‘owns’ the folder on the server where all my web code lives. And you can see it’s that user because there it is, in the path: /home/USERID/domain.com/

    Only one user can own, and access, a domain. However a user can own multiple domains.

    So here’s what this looks likes. One user owning multiple domains:

    one user multiple domains

    And only one user can access the domain, user two cannot:

    User 2 has no access

    This is cool because if there’s a domain under User 2, and it gets hacked, there’s no way for User 1 to get hacked, even if both users are you!(Unless there’s a server wide security flaw, which yes, can happen, but we spend a lot of time trying to prevent that.)

    Logical User/Domain Groups

    If you own 50 domains (and I’ve seen users with 200!), having them all owned by one user sure seems easier, but it means if that user gets hacked, they’re all vulnerable, and you’ll probably end up having to de-hack 50 domains at the same time. Instead, it’s wiser to group your domains ‘logically.’ For example, my elftest.net domains have subdomains, all of which are owned by the same user. However my other top-level domains are each owned by their own user. But that doesn’t work for everyone.

    Recently I was helping a customer with a hacked site, and he complained that the sites he hosted for his clients were being hacked, and his clients were pissed off. I took a look and saw that all his client sites were under one user ID. I asked him if the clients had more than one domain, or if they all had their own, and he replied that each client had 4 or 5 of the domains. After cleaning up the hack, together we made new user accounts, one for each client, and moved the domains to those accounts. If possible, I always clean before moving, but in one case the customer had 75+ hacked sites, so we moved and then cleaned each one, prioritizing the accounts on the way. It took a very long time.

    109649_D_0989 The extra benefit to this is the clients can now have FTP access to their domains and do wild and crazy stuff! But we don’t want them to have FTP.

    Moving The Domain

    Obviously first you need to setup users. When I set up a new user, the first thing I do is make it secure. That means I turn off FTP, forcing SFTP only, and if needed, give them Shell access. Personally? I love shell access, so I always leave it available. If you’re using DreamPress, we have Shell turned off by default, but you can activate it.

    Secure User Settings

    There is a downside, which is that the WebFTP app won’t work. Personally? I find 99.999% of WebFTP apps to be total drek. They’re messy, kludgy, and there are some great free apps like Cyberduck which even let you connect with DreamObjects!

    Now that you have the user, we want to move the domain. This is so easy, anyone can do it. Go into Panel, click on domains, click on edit for the domain. Go to “Users, Files, and Paths” and change the user in “Run this domain under the user:”

    Changing Users

    Really, it is that simple.

  • Changing How We Develop

    Changing How We Develop

    Traditionally in open source land, we come up with an idea for something, we sit in a room and talk about it (it’s kind of like flirting), we make some code, and we test it. Many, many, times we do this in isolation, and we do it in our free time, hoping one day to have the time to make it awesome.

    Post ForkingWhat if we didn’t? What if, instead, we looked at history and remembered that some of our greatest works were brought about by patronage.

    “Artists from Michelangelo to Shakespeare all received support to create the works of art that we know today.”

    Now, finding a patron isn’t easy. It’s harder and harder to find fancy philanthropists who want to fund you for a while to write something awesome. And worse, trying to ‘schedule’ inspiration is hard. But in reality, we do this all the time. The inspiration is there for many of us, we just need the time not doing the other things.

    Aaron Jorbin’s giving this a stab by crowd-raising the money to improve WordPress Post Forking.

    WordPress Post Forking allows users to “fork” or create an alternate version of content to foster a more collaborative approach to WordPress content curation.

    That sounded weird the first time I read it, but let me explain it differently. Have you ever written a post, published it, and then wanted to edit it and have someone else check it before you post the changes? WordPress can’t do that. Once a post is live, you can’t save a change without making that change live too. But what if you could? What if someone could ‘fork’ your post, make edits, and you could review those edits and pull them in? It would be like tracking changes on a Word Doc, only cooler.

    I hope that other developers, who have great ideas, follow this patronage model going forward. After all, I never have a problem with paying for great code. I just have a problem paying for crapy code.

    ETA: It seems fitting I should repost this pic here:

    Aaron Jorbin - Haters Gonna Hate
    Aaron Jorbin – Haters Gonna Hate (by Helen)
  • Adding Per Site Multisite Options

    Adding Per Site Multisite Options

    johnc- said in IRC the other day, regarding the use of get_option API for WordPress “our standard practice is to use get_option to enable site specific features, if the feature needs to be unavailable for other sites then it gets broken into a separate plugin for clarity”

    diceSo to explain this for those who blinked a little we need some examples, and really this depends what you’re writing.

    We got to this point in the conversation while folks were watching my “Don’t Use WordPress Multisite” presentation from WordCamp SF. During the presentation, I mentioned that generally, when I want to write a function for a specific site on Multisite, I wrap it around a ‘If site 1, then …’ and put it in it’s own plugin file in mu-plugins. That leaves me with files like halfelf-functions.php so if I ever split the site out (see? I’m always thinking ahead) I know what file to copy.

    But that’s not the only way about it. Another way is to put all the functions in a file, and then wrap the actual filters and actions around a get_option call. Why would I want to do this instead of a separate function file? The question of portability comes up primarily. Exporting site options is not the easiest thing in the world on a Multisite, but there are cases, like my Capital H Dangit! function, where this would make sense. In general, I always want DreamHost to have a capital H, so since 99% of my sites have that on, I could, instead, use an option, since if I ever moved the one site that doesn’t use it, I just won’t bring the function over!

    Currently have wrapped that function around an ‘if site 2, then don’t run’ check, which works fine. But if I wanted to move that to options, it would go like this:

    
    // Add the default option of capital h to yes, and autoload
    add_option( 'helf_capital_H_dangit', 'yes', '', 'yes');
    
    if ( get_option( 'helf_capital_H_dangit' ) == 'yes' ) {
        foreach ( array( 'the_content', 'the_title', 'comment_text' ) as $filter )
            add_filter( $filter, 'capital_H_dangit', 11 );
    }
    
    function capital_H_dangit( $text ) { 
        [... code here ...]
    }
    

    This is pretty basic. It adds an option called helf_capital_H_dangit, auto-loads, and defaults to on for everyone. That was the easy part. The hard part is determining how to control it.

    One option is an interface where I can check a box on, say, General Settings > Writing > Formatting (right under the smilies check box would be perfect), but since I knew this was something I was going to want to control as the super-admin (that is, I don’t want the other people on my network to turn it off), I went in to the back end of my site at /wp-admin/network/site-info.php?id=2 (since this is site #2) and searched for ‘Helf’

    Screen Shot 2013-08-21 at 2.21.41 PM

    I edited it to ‘no’ and saved. Boom. I can use DreamHost and Dreamhost here.

    Now this isn’t something I’d do for everything. In fact, my actual halfelf-functions.php file looks like this:

    global $blog_id;
    
    if ( $blog_id == 2 ) {
    
    	remove_filter( 'the_content', 'capital_P_dangit', 11 );
    	remove_filter( 'the_content', 'capital_H_dangit', 11 );
    
    	// I Make Plugins Filters
    }
    

    All the per-site filters are right there turned off as I want them. It’s not a lot of effort that way, and it’s still easy to pick up a site and drop it somewhere else.

    Businesswoman in Front of DoorsWhich is better? I don’t know. The real test would be to do some hardcore speed checks and see if checking for the blog ID is faster than checking for an option. I think the speed benefit gains would come from only calling this when needed, but for the limited world in which this is used, it should be fine. Also, keep in mind what both johnc- and I are saying, but in different ways: remember the 80/20 rule.

    In my opinion, if you’re making a feature that will be used by 80%+ of the sites on your Network, get_option is probably the easiest bet. If you’re making a feature that’s going to be used by 20%- of the sites, then a separate plugin (with a site_id check) is the way to go. Of course, you could do an option and default it to ‘no’ as well, but that brings up an interesting level of things to remember if you ever move a site.

    If you’ve got a more efficient way of doing the same thing, or even just a different way, speak up! More options are always better.

    Oh and no, we’re not going to sneakily install this on DreamPress, but if I did, it would also capitalize DreamPress, DreamCompute, and everything else out there. But I won’t.

  • Why We Don’t Auto-Update Plugins

    Since the push of DreamPress (which I’m totally digging), the ‘One Click Install’ feature of DreamHost has become a little more obvious to people, and it’s benefits and disadvantages.

    What’s this auto-upgrade thing?

    To make this simple, if you use DreamPress or our One-Click installer, we automatically upgrade WordPress for you! It doesn’t happen the very second WP has a new version, mind you, we spread it out to not destroy our servers, but you will get upgraded unless the upgrade feature was disabled (of course, you would never disable them, right?). Any time you want to see if you have automatic upgrades enabled, or want to run your own, head over to the DreamHost Panel.

    Why not plugins and themes?

    So why do we only do this for core WordPress? Because plugins and themes are messy.

    Easy Update Button!

    The safest upgrade in the world is the minor upgrade (like WP 3.6 to 3.6.1), as it’s exceptionally rare that it breaks anything. It’s not perfect, of course, sometimes we find out that a plugin or theme was doing something in a very non-optimal way before (if you hear ‘doing_it_wrong()’ please keep in mind that is not a value judgement, just a code comment, we all do it wrong in the beginning). But rarely will this kind of upgrade break your site.

    Similarly, the major releases (3.5 to 3.6) are perhaps surprisingly stable. They’re tested, a lot. At DreamHost there are two people (me and Shredder) on the core contributor list, and we’re heavily involved in WordPress development every single day, at work and at home. We keep up with WP changes, test them on DreamHost, and work with the core team to resolve issues before they even release a beta! We’re on the job!

    “But hang on!” I hear you say. “I upgraded to 3.6 and it broke my theme!”

    And THAT is why we don’t upgrade themes.

    I know, I know, it sounded counter-intuitive. You have to look at it a different way. Your theme stopped working with WordPress 3.6. That means something in the theme is not compatible with the best practices in WordPress core. Translation: WP didn’t break, your theme had a bug.

    It sounds like semantics, or hair-splitting, and I totally get that. It also sounds like we’re passing the buck. We’re not! And we’re not trying to imply the theme (or plugin) developer who now has a broken product is a bad coder, or doesn’t pay attention to WordPress. What we mean is that themes and plugins, as they are used by much smaller segments of the WordPress community (everyone uses core, but maybe only 1000 use that theme), it just can’t be tested as robustly. This is especially true of the solo-developers. Speaking as one, I used to develop WP only in my free time, so any time WP had a new release coming up, I had to take days to test all my plugins, and pray I got everything. Invariably I missed stuff. It happens. We’re humans.

    Breaking isn’t the only reason, though. Sometimes an upgrade is messy and complicated.  Take, for example, NextGEN Gallery. When version 2.0 came out, it inadvertently broke a lot of installs. There was chaos, drama, and finally an open letter. How did this happen? It happened because NextGEN is hella complex, and it’s used in myriad different ways. It happened because plugins and themes can do anything with WordPress.

    Police_man_update.svg

    Blindly updating core is safe. It’s tested and easy to roll back. Blindly updating themes and plugins are not always easy to roll back, they’re not always easy to upgrade (some require a massive upgrade script to run), and they may require you to make other changes in your theme. For that, we just don’t.

    If DreamPress is MANAGED Hosting, like WordPress.com, how come THEY do it?

    You mean why do the plugins on WordPress.com get auto-updated? Because you can’t install any plugins on Wordpress.com! That’s all. They control everything, and simply activate various plugins depending on what package you buy. It’s not really the same thing at all, but I get why people think it is.

    I don’t care! Can I auto-upgrade anyway?

    Are you sure? Okay, then! Install the plugin Automatic Updater (by Gary P.) and set it to upgrade your themes and plugins. I personally use it on all my sites, but I’ve also personally vetted each and every plugin on my sites.

  • WCPDX: Lightning Talk

    WCPDX: Lightning Talk

    Rolling Your WordPress Support Character (Without Any Code)