Half-Elf on Tech

Thoughts From a Professional Lesbian

Tag: coding

  • Dumping ms-files

    Dumping ms-files

    Trash Can On It's SideNOTE: You do not, under any circumstances, have to do this to continue using WordPress Multisite. I just wanted to see if I could.

    I have been toying around with this. Since WP 3.5 doesn’t use ms-files anymore, I wanted to see how much of a pain this would be. While I was at the DMV (I now have a California Drivers License), I started sketching out the methods and possibilities for how one might do this as safely as possible, without bolluxing up your site. I got about 90% successful with it, so this is something I would never suggest you do on a massive, live site. If you compare it to what I did to move images from uploads to blogs.dir for the main site, it’s far more complex and annoying.

    Do you have to do this? Again, no! Everything will work just fine. Can you do it? Sure. Why did I? Support. I already know all the mishegas with blogs.dir, but the new location, and it’s lack of ms-files, promises other, weird, errors. I want to repeat, you don’t need to do anything. The Core Team was fantastic with the work they did to turn off ms-files for fresh 3.5 and up installs is nothing short of phenomenal. This was just to assuage my curious nature, and learn more about the way things work.

    You ready? Here we go.

    I decided to move the images at http://test.ipstenu.org/ to start with, so everything will use that as my example. This is after I played with it on a local site and got my steps mostly solid. I knew that live is always different than test, so I backed up the DB first (always, always, backup first!) and went to town.

    Move the images

    This is obvious. Move your images from blogs.dir/SITENUM/files/ to /uploads/sites/SITENUM/ (or make an alias). I went and did it via command line in the /uploads/sites/ folder, doing this:

    $ cp -r ~/public_html/wp-content/blogs.dir/10/files .
    $ mv files 10
    

    Lather, rinse, repeat. I could have scripted it, but I was working out the kinks until I had two left.

    Edit all sites

    Upload Path, Upload URL Path and Fileupload Url. You can blank them all out.(Corrected thanks to Nacin.)

    Blank me out

    Since you’re blanking it out for everyone you can probably do this via SQL, but since I was doing the sites one at a time, I did them one at a time.

    Fix the Database
    Search/replace each posts table for each site, changing /files/ to /uploads/SITENUM/

    UPDATE wp_10_posts SET post_content = REPLACE (
    post_content,
    '="http://test.ipstenu.org/files/',
    '="http://test.ipstenu.org/wp-content/uploads/sites/10/');
    

    Full Trash, ColorWhy did I do it that way? Because of this blog. I talk a lot about code here, and I know I’ve talked about moving files around before. If you don’t do that, you’re okay with a less precise search, but why bother? This works, it’s safe, and I’d use it again.

    That got annoying really fast. I went and grabbed my favorite Search And Replace for WordPress (and any other database) tool. Seriously I love that. I used that to follow up, change everything, and it actually worked really well for me.

    Another DB Fix!

    One of the changes in 3.5 was turning off rewriting. This took me forever and a day to find. After I did that, my images showed up fine, but the little buggers kept uploading to /files! Turns out it was all because of the site option ms_files_rewriting

    The way I got around this was by putting the following in my wp-config.php file:

    define( 'UPLOADBLOGSDIR', 'wp-content/uploads/sites' );

    And then I ran this in SQL to turn off ms_files_rewriting. Or so I thought. More in a second.

    INSERT INTO `my_database`.`wp_sitemeta` (`meta_id`, `site_id`, `meta_key`, `meta_value`) VALUES (NULL, '1', 'ms_files_rewriting', '0');
    

    I came up with that after reading through /wp-includes/functions.php line 1515.

    For most sites, this worked, but in my later work, I determined that it actually wasn’t working. It was ignoring this. I don’t know why, but every test I did merrily ignored this setting, so I finally growled and wrote this mu-plugin function:

    function stupid_ms_files_rewriting() {
            $url = '/wp-content/uploads/sites/' . get_current_blog_id();
            define( 'BLOGUPLOADDIR', $url );
    }
    add_action('init','stupid_ms_files_rewriting');
    

    It’s stupid simple, it’s probably not a good idea, but it works for the three sites that have the stupids.

    Finish up .htaccess.

    .htaccess, remove the ms-files.php line for ms-files, or comment it out. This is pretty simple.

    Empty TrashWhy not move the main site to /uploads/?

    Because of the way I fixed the uploadblogsdir. It defaulted everyone to /sites/ and after an hour I said fuck it.

    Any weird problems?

    Yeah, two sites (this one and my grandmothers) decided that they wanted to be repetitious and spat out URLs like this: wp-content/uploads/sites/8/sites/8/

    Since that wasn’t right at all, and I was a little too eggnoggy to parse why, I did this:

    RewriteCond %{HTTP_HOST} ^taffys\.org
    RewriteRule ^wp-content/uploads/sites/8/sites/8/(.*) /wp-content/uploads/sites/8/$1 [L,R=301]
    

    I swear I have no idea why three sites got stuck with /files/ and two more decided to double down, Vegas style, but frankly I’m pleased I got through this far on my own.

    I can’t stress enough that you do not have to do this!

  • WordPress Cancel Post Button

    WordPress Cancel Post Button

    Someone asked about this in the forums. I can see why folks would find it useful, so here’s a simple addition to add a ‘Cancel Post’ button to your publishing metabox.

    This plugin checks what post-type you’re on and redirects you correctly for pages and CPTs.

    To Install

    Make a file called cancel-button.php and put it in your mu-plugins folder (if you don’t have one, just make it in /wp-content/). In that file, paste the following (yes there’s no ending PHP tag, it’s okay, you don’t need it):

    <?php
    
    /* 
    Plugin Name: Cancel
    Plugin URI: https://halfelf.org/
    Description: Adds a 'cancel' button to posts, next to Publish.
    Version: 1.0
    Author: Ipstenu
    License: GPL2
    
    */
    
    add_action( 'post_submitbox_misc_actions', 'author_in_publish' );
    
    function author_in_publish() {
        $screen = get_current_screen();
        $cancel = get_admin_url();
        if ( $screen->id == 'post') {$cancel = "edit.php"; }
        else { $cancel = "edit.php?post_type=$screen->id";}
        
        echo "<div class=\"misc-pub-section\"><a class=\"button\" href=\"".$cancel."\" id=\"post-preview\">".__('Cancel Post')."</a><input type=\"hidden\" name=\"wp-cancelpost\" id=\"wp-cancelpost\" value=\"\"></div>";
    }
    

    This will add the button for all your sites on a network, if you happen to use Multisite.

  • Command Line Mac Trash Tricks

    Command Line Mac Trash Tricks

    RM TrashWarning! I’m going to talk about the ‘rm’ command which is a super-deadly command in the linux world. No matter what, never ever ever consider running it unless you’re certain you know what it does!

    I review a lot of plugins, which means I download them all to my laptop, review all the code, possibly install them, and then delete. This means, on any given week, I have 5000 items in my trash. And this is without unzipping! (Yes, we get a lot of plugins, and TextWrangler lets me review most of them in their zips.)

    When I forget to empty my trash every day, I end up waiting hours for the GUI empty to run unless I use rm -rf from inside the ~/.Trash/ folder. The real command is this:

    $ rm -rf ~/.Trash/*
    

    I like this because it’s crazy fast compared to the GUI, and

    But sometimes I actually just want to commandline my trash. I’ll be banging on things in Terminal and a very simple ’empty trash’ command would be nice, right? OSX Trash lets me type trash -l to see what’s in my trash, and trash -e to run the normal empty command. It’s better than a lot of other scripts, because if I type trash filename and there’s already a file with that name in the trash, it behaves like Mac Norm. That is, it’ll rename my second file ‘filename date’ and I won’t have file conflicts!

    The only thing it’s missing is a ‘trash -p’ command, which would let me run the force rm and just dump it all. Yes, I know rm works, but if you’ve ever typed it in the wrong window, you know why it’s a terrifying command. Still, back to the age old rm commands, what happens when you have that annoying locked file error? Like me, you probably kvetch about quitting everything to delete.

    More command line magic!

    $ cd ~/.Trash
    $ chflags -R nouchg *
    $ rm -rf *
    

    Finally, to make this full circle, I made a dead simple alias to prevent me from fat fingering the rm too much:

    alias trashdump='rm -rf ~/.Trash/*'
    

    Fast, efficient, and potentially deadly, but less than manually typing it in all the time. Deleted 2000 files in seconds, versus minutes.

  • Handling Negatives

    Handling Negatives

    Positivity is two way streetEvery once in a while, someone raises a stink about how their WordPress plugin or theme got a bad review or comment on the forums and how the mods should delete it.

    We rarely do.(We have, on occasion, done so, but usually only under sever provocation, and often that ends with the developers making the demands being blocked for being jerks.)

    The issue at heart is not ‘OMG! Someone hates my plugin!’ but how do you handle the negative remarks. I mention this in passing (multiple times) in my ‘How to Support Plugins’ presentation, but text is really a terrible medium to communicate. You know there’s a difference between these two sentences:

    “I want a unicorn,” she said, bubbling with delight and hope.

    and

    She jabbed a finger in my direction. “I want a unicorn!” Her voice pitched in a low, guttural, growl.

    But the reason you know that the first one is nice and the second a little threatening is the context. Prose lends itself to this quite well, because authors take the time to explain what’s being felt and evoked. A help ticket, on the other hand, is pretty dry and plain stuff. And because we’re humans, we attempt to put meaning into the words, often ones the author never intended. We’re programed to look for the meaning, hidden or otherwise, and we always interperate based on our mood and our situations.

    This means when you see a ‘complaint’ like this, you have a reaction:

    Ipstenu’s Really Awesome Plugin has a security hole. If you go to /wp-content/plugins/really-awesome/readem.php?=../../../wp-config.php then you can totally read the contents of your wp-config!

    Obviously as a developer my first reaction should not be to yell at this user and remind them to email me about my plugin.(Protip, developers, always put your email or a contact link in your plugin! If they can’t find you, then you don’t get to bitch they didn’t contact you. At the very least, put it in your source code.) And even though people should email plugins@wordpress.org they don’t always know that, and it’s okay. You can’t know what you don’t know, after all. No, my first reaction, as a dev, is to plug the hole! It’s that obvious. Fix the problem, push a new version, and then come back to the post and reply:

    Wow, thank you for letting me know. I’ve fixed that and released a new version. If you find things like this again, please email me at ipstenu@….. Again, thank you for reporting this.

    Then in my plugin, you’ll see a credit in the changelog and/or the version history:

    Version 2.0 – Security update thanks to RandomUser. readem.php let you read any php file on your install. Please upgrade ASAP.

    That’s it.

    The idea isn’t to hide any mistakes I make, or act out of fear and desperation, but to take a hold of the problem, resolve it, and move forward. Check out WPSecureNet and their list of plugins. Normally they’ll only post when an exploit is closed, but sometimes they report while vulnerable. There’s no shame in being listed there, unless you haven’t patched your plugin yet, and realisticly there’s no shame in being listed anywhere as having a bug.

    Look, bugs happen. We can wish we’d never stub our toes, but we will. We can wish we’d never stab our toes, but someone will find a way to do that too. Humans are imperfect. We make mistakes, we don’t see things that are ‘obvious’ and it happens. Consider the number of books you get when typos: that’s after an author, an editor, and a test reader has proofed the book. With most code, you have fewer eyes than a book, and bugs still slip in. Clearly it’s going to be impossible to prevent any and all bugs, and once we accept that, then we can move on to taking these moments as less painful.

    It is painful to get a security report, or even a bug report. Even if this is ‘just’ a hobby, you’ve spent hours of your free time banging away, trying to make something awesome, and now you found out it just wasn’t. No matter how experienced you are, you take this personally, even for just a split second. Getting over that hurdle, that fear that everything is ruined, isn’t easy. And this is why I keep saying that you shouldn’t worry as much about the bugs as how you handle them.

    Your users, be they just the folks who use the plugin or people who pay you, will follow your lead. If you’re calm, collected, and honest, then they’ll value you and your product. They’ll appreciate you and what you can do. By taking the negatives and turning them positive (sorry, that’s cheesy) you will improve your relationship with users, clients, and the community.

    I want that unicorn!But what about those angry ‘You suck!’ posts? Oh, they happen, and with a bit more frequency than we’d all like. Often people like to lump them with those posts where people complain about your plugin, only to have the wrong plugin… Look. The way you handle those negative people is the way you will set the tone for your entire online life. You can react aggressively, or you can handle it calmly and rationally. Personally, when I see someone rage on about how the original poster is a moron, and they don’t know what they’re talking about, I put them on my blacklist. I will never again use their plugins or themes or any code, if I can help it. I’ll sooner fork it than anything else, because the best way I can vote is with my feet.

    What about you? How do you handle the angry unicorn lovers? And what do you do when the devs go postal on you?

  • Multisite Per Site Registration

    Multisite Per Site Registration

    This post is dedicated to TJ List, who donated to help me get to WCSF. He’s the last post of this ilk, so I’ll stop having these announcements, but I wanted to say thank you all, one more time. My new job starts the 27th, and you all helped!

    Edit: If you want a plugin, see Join My Multisite

    Sometimes people want a way to sign up for the network on specific sites, and not go back to the main site. And I often tell people ‘You need to make a custom page template with the code from the signup page in it.’

    But how? There’s going to be a lot of code in this post, so at the end I’ll give you a link to the whole file you can download and play with. We’re going to go through the steps, however, since we may not be using the same theme, and you’ll need to understand the process.

    Make your Page Template

    First you make a new page template. I always call mine page-template-signup.php because well named files/code don’t need as much inline documentation.

    Since I wanted to build this off of TwentyTwelve, I grabbed the full page template, and pulled that over:

    <?php
    /*
    Template Name: Signup
    */
    
    get_header(); ?>
    
            <div id="primary" class="site-content">
                    <div id="content" role="main">
    
                            My Content Here
    
                    </div><!-- #content -->
            </div><!-- #primary .site-content -->
    
    <?php get_footer(); ?>
    

    Obviously on a first pass, nothing much is going on here. The next step is to open up wp-signup.php and take a look. There’s a lot going on here, but we don’t need all of it.

    First let’s look at what we need to add to the header area. Most of this is just styling and a little tweakage to stop search engines from thinking people need to really find this page, so let’s add the following right above get_header();

    add_action( 'wp_head', 'wp_no_robots' );
    
    // Fix for page title
    $wp_query->is_404 = false;
    
    function wpmu_signup_stylesheet() {
    	?>
    	<style type="text/css">
    		.mu_register { width: 90%; margin:0 auto; }
    		.mu_register form { margin-top: 2em; }
    		.mu_register .error { font-weight:700; padding:10px; color:#333333; background:#FFEBE8; border:1px solid #CC0000; }
    		.mu_register input[type="submit"],
    			.mu_register #blog_title,
    			.mu_register #user_email,
    			.mu_register #blogname,
    			.mu_register #user_name { width:100%; font-size: 24px; margin:5px 0; }
    		.mu_register .prefix_address,
    			.mu_register .suffix_address {font-size: 18px;display:inline; }
    		.mu_register label { font-weight:700; font-size:15px; display:block; margin:10px 0; }
    		.mu_register label.checkbox { display:inline; }
    		.mu_register .mu_alert { font-weight:700; padding:10px; color:#333333; background:#ffffe0; border:1px solid #e6db55; }
    	</style>
    	<?php
    }
    
    add_action( 'wp_head', 'wpmu_signup_stylesheet' );
    &#91;/php&#93;
    
    Just below <code>get_header();</code> we're going to add <code>do_action( 'before_signup_form' );</code>
    
    Then in the 'My Content Here' section, you're going to copy everything from <code><div id="content" class="widecolumn"></code> down to <code><?php do_action( 'after_signup_form' ); ?></code> and put it in there. It's a lot of code, I know. I'm not including it here because of that. Again, check the attached file.
    
    Finally search your file for all instances of <code>wp-signup.php</code> and change them. To what? To whatever you're going to name your page in the next section. I knew I wanted to name my page signup, so I replaced it with that.
    
    <h2>Make Your Page</h2>
    
    <a href="https://halfelf.org/wp-content/uploads/sites/2/2012/08/page-template.jpg"><img src="https://halfelf.org/wp-content/uploads/sites/2/2012/08/page-template-300x222.jpg" alt="Picking Page Template" title="Picking Page Template" width="300" height="222" class="alignleft size-medium wp-image-2509" /></a>Next you make a page. This is easy. You go in to your site, you make a page (I called mine Signup) and you put in <em>no</em> content. Pick the page template called 'signup' and save. I always opt to turn off comments and pingbacks on this page for what I hope are obvious reasons. Don't bother putting in any content to the post. Because we didn't leave in the loop, it wouldn't display.
    
    If that's something you want, then you would leave the loop info in where I had 'My Content Here'
    [php]
    
                            <?php while ( have_posts() ) : the_post(); ?>
                                    <?php get_template_part( 'content', 'page' ); ?>
                                    <?php comments_template( '', true ); ?>
                            <?php endwhile; // end of the loop. ?>
    

    That’s an ‘as you like it’ sort of thing. If you need to leave messages, etc, it’s great.

    And that’s kind of it. Oh, you’ll want to mess with your CSS, but that’s all you need to get started! It even keeps you on the page.

    How it looks…

    Since there are multiple registration options, here’s how they look:

    With Registration Disabled

    User accounts may be registered

    Logged in users may register new sites

    Only Logged In Users Can Make Sites

    Both sites and user accounts can be registered

    Adding Users to Sites

    I know, I said we were done. There’s one more thing. What happens if you want that per site registration to automatically add people to the site they’re on? That is, if someone goes to halfelf.org/signup/ I want them to be added to halfelf.org as a user. The problem with this is when you register for a user account, you may have noticed there’s an ‘extra’ step to confirm. This is where you get that email that says ‘click here…’

    The ‘good’ thing is the URL you get looks like this https://halfelf.org/wp-activate.php?key=0ef92965187a50be and it will take you back to the right site to login! The ‘bad’ thing is it doesn’t actually add you to the site. It will add you any time you create a site, but if you just make a login ID, you get this:

    No Access Is Bad

    As you can see from that URL, it’s sending me back to wp-activate.php, which is something we can’t change in our signup template. If you know you want to add all users to all sites, then you want Multisite User Management, but that isn’t always the case. So what do we do? Option one is to grab a plugin that makes us approve all users, then we can add them as they’re requested. But you don’t want to do manual work, I’d guess.

    The trick is to add an action onto user registration, but only run it per site. So you’d want this in an mu-plugin, and since halfelf happens to be blog , I do this:

    <?php
    global $blog_id;
    if ( $blog_id == 2 ) {
    
      function helf_activate_user( $user_id, $password, $meta )
      {add_user_to_blog( '2', $user_id, get_site_option( 'default_user_role', 'subscriber' ) );}
      add_action( 'wpmu_activate_user', 'helf_activate_user', 10, 3 );
    }
    ?>
    

    That says ‘Hey, if I’m on blog ID 2, when people register, kick them to be a subscriber on this blog. I have mine in a plugin called halfelf-autoregistration.php and it works rather nicely. If someone registers on ipstenu.org, even if they try to login to halfelf, they can’t login because their registration process was on the other site.(I suppose if someone was clever, they could take https://halfelf.org/wp-activate.php?key=0ef92965187a50be and change it to https://ipstenu.org/wp-activate.php?key=0ef92965187a50be to register there, but there’s no change in ‘risk’ between being a network psudeo subscriber and a real subscriber. If I was adding in editors, I’d probably change my mind here and manually validate every new user. I’m trusting in the fact that most people aren’t that clever.)

    Do you have any tricks that help you do this?

    Oh and here’s the file to download. It’s in .txt for security reasons.

    Download Here

  • Capital H Dangit

    Capital H Dangit

    My new gig at DreamHost comes with a minor ‘d’oh!’ and that is it’s a capital H. Which I seem to be incapable of remembering. So I wrote a function. I stole capital_P_dangit() and swapped the WordPress for DreamHost. To save myself from embarrassment, here it is:

    // Capital H in DreamHost
    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 ) {
        // Simple replacement for titles
        if ( 'the_title' === current_filter() )
            return str_replace( 'Dreamhost', 'DreamHost', $text );
        // Still here? Use the more judicious replacement
        static $dblq = false;
          if ( false === $dblq )
            $dblq = _x('&#8220;', 'opening curly quote');
          return str_replace(
            array( ' Dreamhost', '&#8216;Dreamhost', $dblq . 'Dreamhost', '>Dreamhost', '(Dreamhost' ),
            array( ' DreamHost', '&#8216;DreamHost', $dblq . 'DreamHost', '>DreamHost', '(DreamHost' ),
          $text );
    }
    

    Now. Here’s where it gets fun. No ‘hacks’ posts will be affected by this code! Otherwise how would I show it to you here? Normally this is where you would just run remove_filter( 'the_content', 'capital_H_dangit', 11 ); in the functions file for your theme. Due to the way I’ve wrapped my various functions into mu-plugins, the down and dirty way was to wrap the above block of code with a check for if ( $blog_id != 2 ) { { ... }.

    Most of the time you won’t care about things like this. I just needed it so I could demonstrate code. I’ve done the normal filter remove here so I can also say ‘Wordpress’ in my code related posts. For proof this works, I assure you, 100%, that I typed in ‘Dreamhost’ over in my post about quitting my job and going to work for them.

    Sorry about that, Simon!