Half-Elf on Tech

Thoughts From a Professional Lesbian

Category: How To

  • Static Content Subdomain

    Static Content Subdomain

    I use a lot of different tools to run my websites, and over time I’ve learned what I want is to have my static content, the files that are uploaded and are images, stored separately from my apps. So while I have the basic folders on my domain (wordpress, wiki, gallery) I have a special subdomain called static.example.com for all those images and videos.

    There are a few reasons I do this. First, I like having my images separate. Second, it allows me to establish a cookie-free subdomain for images and that shuts up YSlow’s check.

    Create The Subdomain

    Do this however your host allows. Keep in mind that some don’t allow you to traverse domain folders. If your host creates your domain as /home/user/example.com and subdomains as /home/user/static.example.com you may have to fight a little more with things depending on your setup. If possible, I prefer to put the subdomain folder inside the main web root.

    If you’re using cPanel, by default you get your static subdomain installed at /home/user/public_html/static which is how I like it. This is perfectly accessible by all things but it’s also browsable at example.com/static/ and we don’t want that. Applying a little .htaccess magic will solve this.

    # CDN
    <If "%{HTTP_HOST} == 'example.com' ">
            RedirectMatch ^/static/(.*)$ http://static.example.com/$1
    </If>
    

    Now we’re ready to go!

    Move WordPress Uploads

    This used to be really easy. Go to Settings -> Media and change things. But we removed that to stop people from blowing themselves up. Now there are a couple ways about it. I jumped right over to editing the options by going to wp-admin/options.php and look for upload_path and upload_url_path.

    Setting image location options

    I change upload_path to /home/example/public_html/static/wordpress which is where I’ve moved all my images. Then upload_url_path becomes http://static.example.com/wordpress and I’m done except for fixing my old posts. It’s actually pretty neat that once I put those paths in, the Media Settings page lists them as editable.

    Fixing the old posts takes a little trick though, and you’ll have to search/replace your posts via the database:

    UPDATE wp_posts SET post_content = REPLACE(post_content,'http://example.com/wp-content/uploads/','http://static.example.com/wordpress/');
    

    Or in wp-cli:

    wp search-replace http://example.com/wordpress/wp-content/uploads http://example.com/wordpress
    

    The gotcha here is that since I use SSL for my administration, I had to set up a new certificate for the static domain. Not a big deal right now since I can set up a self-signed, or use StartSSL until Let’s Encrypt is off the ground. It is something to consider though.

    Move ZenPhoto Uploads

    I have to start by warning you that Zenphoto doesn’t like this. When you install it, it puts your images in an albums folder, in the Zenphoto gallery install. This isn’t so bad, but you actually can move it around. You have to look in your zenphoto.cfg.php file (found in zp-data). The default location for your albums is defined by this:

    $conf['album_folder'] = '/albums/';
    $conf['album_folder_class'] = 'std';
    

    Since I want it in the static location, I tell it my folder path based on ‘web root’ and that its ‘in_webpath’ (which tells ZenPhoto to look in the root and not relative), by changing that section to this:

    $conf['album_folder'] = '/static/gallery/albums/';
    $conf['album_folder_class'] = 'in_webpath';
    

    But that means my URLs for images become http://example.com/static/gallery/albums... and I wanted http://static.example.com/gallery/albums... instead. Thankfully the .htaccess rule I used at the beginning of all this covers me there. Looking into this, I understand this is the case because unlike MediaWiki or WordPress, ZenPhoto only has one ‘location’ setting. The other two have path and URL.

    MediaWiki

    This was … weird. Technically all you have to do is set up the folders and change the following values in LocalSettings.php:

    $wgUploadPath       = "/static/wiki";
    $wgUploadDirectory  = "/home/example/public_html/static/wiki/";
    

    The thing that’s weird is that the documentation says you can do this:

    $wgUploadPath       = "http://static.example.com/wiki";
    

    And when you do, the image URLs properly call from the domain name. They just won’t load. When you dig deeper, it turns out that it’s caused by the settings for responsive images. The way it puts in srcset doesn’t seem to like this. So for now I’ve disabled it and my setup is this:

    $wgUploadPath       = "http://static.example.com/wiki";
    $wgUploadDirectory  = "/home/example/public_html/static/wiki/";
    $wgResponsiveImages = false;
    

    End Result?

    All my uploaded content is on my ‘static’ subdomain, separate from everything else, which makes version control even easier. Also now if I ever decide to move things off to a CDN, I’m pretty well set up.

    The real reason I do this is that while some of my content is uploaded via the content management systems I use (WordPress, ZenPhoto, etc), the majority is not. ZenPhoto, for example, is faster to FTP up a gig of images than it is to use a PHP tool. Ditto videos. And because of them, it’s nice to have a separate location I can give access to without allowing someone full rights on all my tools.

  • Image Compression

    Image Compression

    If you’ve ever tested your site on Google PageSpeed Insights, GTMetix, Yahoo! YSlow, or any of those tools, you may have been told about the value found in compressing images.

    Google Pagespeed has some images for me to optimize

    There are some images you can fix, like the first one on that screenshot is for an image from Twitter I downloaded. There are some you can’t fix, like the last three are all telling me the default smilies for WordPress need some shrinking.

    Either way, the short and skinny of it is that if you make your images smaller then your webpage loads faster. I know, it’s shocking. Many long-term webheads know that you can compress images best on your own computer, using ‘Save For Web’ and other command line tools for compression. That’s great, but that doesn’t work for everyone. Sometimes I’m blogging from my phone or my iPad and I don’t have access to my tools. What then?

    Before You Upload

    I know I just said but what about when you can’t resize before you upload. There are things you can do for this. Photo Compress for iOS will let you make your images smaller. So can Simple Resize. There are a lot of similar apps for Android as well.

    For the desktop, I use Homebrew so I installed ImageMagik (which is also on my server for WP to use) and toss in a command line call for it. Sometimes I’ll use grunt (yes, Grunt, the same thing I use for Bower and coding) and ImageOptim to compress things en masse.

    Of course, if I only have one or two images, I just use Preview which does the same thing more or less. Photoshop, which is still stupid expensive, also lets you do this, but for the layman, I suggest Preview for the Mac. I haven’t the foggiest what you can use on Windows.

    If you’re not uploading images via WordPress (and very often I’m not), you pretty much have to do it old-school.

    While You Upload

    Okay great, but what about WordPress?

    The best way about it is to have WordPress magically compress images while you upload them. And actually it does this out of the box. My cohort in crime at DreamHost, Mike Schroder, was part of the brain trust behind making ImageMagick a part of core WordPress. This was a massive undertaking, but it allowed WordPress to compress images better, faster, and more reliably than GD. I hesitate to say it’s ‘safer’ since the image loss (that is that weird fuzzing you get some times) is less.

    If you want to make it even better, you need to use plugins. I used to tell people to use smush.it which was a Yahoo! run service. But then they deleted it and we were all very sad. WPMU Dev, who owns a SmushIt plugin, installed Smushing on their servers and you can now use WP Smush. I’m a bit of a fan of TinyPNG, which gives you 500 free compressions a month, and that’s enough for me. I like TinyPNG because there are no options. I install it, it runs when I upload images, and done. That doesn’t mean I don’t see value in things like ShortPixel, just that it’s not really what I want. Finally there’s Kraken IO which I love just for the name, but it makes people balk because it’s not free.

    If you don’t want to use an external service, and I totally get why you wouldn’t, there’s EWWW Image Optimizer.

    I personally use either EWWW or TinyPNG, depending on the site.

    Can a CDN Help?

    Maybe. I know a lot of people love offloading their images to CDNs. I currently don’t for no reason other than I’m a bit lazy and I hate trusting someone else to host my images. But that said, you actually can (rather easily) use a CDN if you have Jetpack installed. Their service, Photon, does exactly that. Now, I don’t use Photon because my users in Turkey and China like to visit my site, but there’s nothing at all wrong with those services.

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

  • Mailbag: I Lost My Site

    Mailbag: I Lost My Site

    Don from Worcestershire started an email in a way that normally would result in a quick delete:

    Before you cut me off I am a 78 year old gambling historian, specializing in horse racing between 1850 and 2015.the age alone will convince you that my knowledge of this topic is limited.

    It’s not the age, Don, it’s that the email starts with a long, dramatic, kind of non-essential story about your life, your ideas, your dreams, your goals, and your son. By the way, I’m sorry for your loss.

    Yes, I read the whole email.

    It took a long time to sift through the drama to sort out the issues.

    1. Spam – He had a lot of spam and used Akismet (yay!) to deal with it.
    2. Bandwidth – The spam made his site hit the overages on his hosting.
    3. Domain suspension – Due to circumstances, he was late in paying his feeds and the domain was sniped.

    I feel bad. I really do. But here’s the thing, Don. Your domain is like your rent. If you don’t pay for it, you lose your home. And while this sucks a lot, and yes there were mitigating circumstances, you didn’t pay in time, and the company is legally within their rights to sell. It’s the same as your phone. No pay, no phone. It’s just that simple.

    Now I have good news.

    According to whois records, you still own your domain! So you actually didn’t lose the domain you lost the hosting plan.

    These are different things. They’re very easy to get confused.

    There isn’t a great analogy to all this, I’m afraid, but as it works, you’re paying for two things.

    First you pay for the domain. This reserves your ‘name’ on the internet. I recommend paying for it first for a year and then, if you like how things are going, pay for as long as you possibly can. I did mine for a decade at one point in time. I knew I wanted the domains and I knew I was using them.

    Once you have the domain, you need to pay for webhosting. The host is where you data is stored.

    Now I need to take a digression.

    BACK UP YOUR WEBSITE OFFLINE.

    If the backup tool you use only lets you backup to your webserver, it’s a shitty backup tool. Stop using it unless you are automatically downloading that backup somewhere else.

    Because you see, Don, what happened was that you didn’t pay for your webhosting. You didn’t pay for the storage unit that housed your data. And they can auction that off like they do on shitty shows like Storage Wars.

    So what do you do when this happens?

    If it’s just the webhost, it’s easy. Contact the webhost. http://www.whoishostingthis.com/ is a great resource to find out who your host is. Be honest but keep it short. “I’m sorry, I wasn’t able to pay on time and my site was suspended. Is there any way I can get it back?” That’s it! That’s all you have to do. If you’re lucky, they may still have all your data. You pay, they flip a switch, it’s back.

    The worst case … that’s why you need backups.

    If it’s the domain registration, though, that can be a mess. If you bought the domain through your webhost, it may surprise you to find out that the host doesn’t have control over your domain registration. The host is an intermediary. That means, if you go to your site and see a placeholder page owned by a domain registrar, it may or may not be a cybersquatter.

    You may have heard about domain hijacking or domain theft. That’s when someone changes the registration of your domain name without your permission. A hijacking is not the same as when you’ve failed to pay for your domain and the registrar slaps up a placeholder. A great many hosts put up a branded placeholder if you’ve registered a domain and not yet updated content. Sometimes it says “This domain has been registered at…”

    If it says “This domain is suspended” then the issue is with the webhost. If it says “This domain has expired” then it’s likely to be the registrar. You need to figure out who the registrar is, log in with your info, pay the fine, and get the site back.

    I strongly urge you to put a reminder in your to-do list or whatever you use to keep track of things. “Domain name renewal due on day X.” It’s like paying your rent. Don’t forget. Make reminders. Do it.

    By the way, no matter whom you talk to, don’t give them the sob story. While they do care, in as much as any human does, it rarely changes the reality of what’s going on. Shit happened, you couldn’t pay. Your personal drama is not their problem. I know how harsh that sounds, but it’s not. And the more you make it how you need an exception because you’re a special case, the more people hear it as an excuse.

    I know it’s not. You know it’s not. Except sometimes, Don, for a lot of people, it is. If I told you how many idiots complain they couldn’t pay for $4/month hosting, while stilly buying a top of the line iPhone, you’d understand why it’s draining.

    And as someone who’s fucked up before, I find that being honest where it’s my fault gets better results. “I’m sorry. I screwed up and didn’t pay. Is there anything I can do to get my content back? It matters a lot to me, and I’d appreciate anything you can do to help me.”

    Works great.

    By the way, Don, I see that you have your site back right now. You should upgrade. You’re running WordPress 2.6.1 and that’s really old and vulnerable.

  • Updating Bower with Grunt

    Updating Bower with Grunt

    The goal of automation is to make the annoying stuff I don’t want to have to remember to do easier to do. Bower is useful for updating things. Grunt is useful for running a series of commands. Using them together makes life easier.

    In my little world, everything lives in a folder called ‘assets’ and its very simple.

    Add a Package to Bower and call it in Grunt

    First I have a .bowerrc file which very simply says this:

    {
       "directory": "vendor"
    }
    

    That tells Bower where to install things. So when I run bower install jquery-backstretch --save in my asset folder, it saves backstretch to the vendor folder.

    In my gruntfile.js, I have this to pull in my backstretch arguments and the main file into one file, uncompressed, a folder level up:

    		concat: {
    			// Combine all the JS into one
    		    backstretch: {
    		    		src: ['js/backstretch.args.js', 'vendor/jquery-backstretch/jquery.backstretch.min.js'],
    				dest: '../js/backstretch.js',
    		    },
    		},
    

    Just like magic.

    Tell Grunt to Update Bower

    But while Bower pulled in the packages, I don’t want to have to tell Bower ‘Hey, make sure everything’s up to date!’ every few days. I want to make sure I’m on the latest version of a branch most of the time, for security reasons at the very least. That means I have this in my bower.json file:

      "dependencies": {
        "bourbon": "~4.2.3",
        "neat": "~1.7.2",
        "jquery-backstretch": "~2.0.4"
      }
    

    So if I run bower update I would get this:

    bower jquery-backstretch#~2.0.4 cached git://github.com/srobbin/jquery-backstretch.git#2.0.4
    bower jquery-backstretch#~2.0.4         validate 2.0.4 against git://github.com/srobbin/jquery-backstretch.git#~2.0.4
    bower bourbon#~4.2.3                      cached git://github.com/thoughtbot/bourbon.git#4.2.3
    bower bourbon#~4.2.3                    validate 4.2.3 against git://github.com/thoughtbot/bourbon.git#~4.2.3
    bower neat#~1.7.2                         cached git://github.com/thoughtbot/neat.git#1.7.2
    bower neat#~1.7.2                       validate 1.7.2 against git://github.com/thoughtbot/neat.git#~1.7.2
    bower jquery#~1.9.1                       cached git://github.com/jquery/jquery.git#1.9.1
    bower jquery#~1.9.1                     validate 1.9.1 against git://github.com/jquery/jquery.git#~1.9.1
    bower neat#~1.7.2                        install neat#1.7.2
    bower bourbon#~4.2.3                     install bourbon#4.2.3
    
    neat#1.7.2 vendor/neat
    └── bourbon#4.2.3
    

    Cool. But who wants to run that every day?

    Instead, I ran npm install grunt-bower-update --save-dev to install a new Grunt tool, Bower Update. With that code added to my gruntfile.js, every time I run my grunt update command, it first updates my libraries and then runs the processes.

    There is a downside to this. I use git to keep track of my work and either track the vendor packages in my repo (which can make it a little large) or I can remember to install the packages. There are other ways around this, like using grunt-bower-task to set up things to install if not found, update if they are found. I went with including them in my repos, which makes the git pull a bit large (it added about 6000 files), but since I properly delete my assets folder when I deploy from git, it won’t impact the size of my server’s package.

    Register a New Bower Package

    Randomly, when I initially tried to install backstretch, I forgot it was named ‘jquery-backstretch’ and did this:

    $ bower install backstretch --save
    bower                        ENOTFOUND Package backstretch not found
    

    Obviously the right fix is to use the right repo name (and bower has a great search tool to help me to that). But what if I did want to package it up as backstrech? Or if I wanted to add my own repo? Well I would have to register that package first. And that’s pretty easy:

    bower register backstretch git://github.com/srobbin/jquery-backstretch.git
    

    Your Tricks?

    Do you have Bower tricks?