Half-Elf on Tech

Thoughts From a Professional Lesbian

Category: How To

  • Mailbag: Homogenous websites?

    Mailbag: Homogenous websites?

    I have a site for my business that has multiple physical locations that have online booking for each location. Right now we basically have separate websites that look very similar for each location (except for some content) with separate domains [URLs redacted]. Is this what you meant by homogenous websites? If WPMU ins’t a good option, can you steer me in the right direction for how you would design this type of site?

    Yes. That’s pretty much exactly what I mean when I call a site “homogenous.”

    You can’t see it, but each URL I removed had identical design. Same layout, pretty much the same splash page.

    When I talk about ‘sameness’ with websites, I really do mean exactly this. Each site on the network has the same information, the same about page, and actually pretty much the same everything except for the booking page.

    Right now, each site has a locationdomain.com/book-now URL. Without looking at the code, I’m going to guess that the book-now pages are inserts. Either templates or shortcodes, but something that doesn’t need to be on the domain URL to be unique. Just some content in the page.

    And I would have maindomain.com/book-location Or better still maindomain.com/location/LOCATION/book

    That second example looks weird, I know. You see, I’d do it with Custom Post Types for each location. My CPTs would be pages and their slug would be /location/. Then each page would be LOCATION, giving me URL formats like /location/lexington/ and so on.

    My thought process is that if the majority of the content of each site is the same, and the design of all sites are the same, then I don’t need a multisite unless there’s a specific need to silo data.

    There are very few cases, in a homogenous network, where you need to silo data. Exceptions are pretty much all based on legal requirements.

    And if you have a song in your head, here it is:

  • NUX: Setting Up Ghost Pro

    NUX: Setting Up Ghost Pro

    NUX stands for “New User Experience” and I’ve been dabbling in it recently with WordPress, trying to understand where we fail for new users. My friend did a comparison for his company of other similar tools and told me that Ghost’s was the worst. I didn’t believe him, so I decided to check it out.

    Ghost is a simple, powerful publishing platform. It’s dead simple. It’s basic. And it’s weirdly hard and complex. The code is simple, but much like WordPress, it’s hit the wall of explaining new concepts to people. Ghost Pro is their ‘managed hosting’ version, where you sign up and get a blog.

    Of note, this is not talking about the self hosted Ghost application.

    Registration Is Easy As Pie

    This is easy. You go to the main page of Ghost.org, you pick a username and password, you press ‘test it out.’ That then asks if you want the download or to make a new site, and don’t worry about the credit cards yet. You get 14 days free. If you pick the new site, it asks for the site name, the URL you want (it’ll be something.ghost.io) and then…

    Ghost prompting you for critical information about your site to be, like your name and your password?

    It’s weird that it wants my name and password, and I do wonder if it’s making another account on the system. Do I now have a user account and a ‘network’ account?

    Writing A New Post Is A Lie

    Once you have your site, you’re dumped here:

    Ghost's dashboard where it prompts you to make a new post

    They even have an animated ‘Write A Post’ button there, which is great. Except it’s a lie. That link kicks you to the https://something.ghost.io/ghost/1/ page which is the ‘Hello World’ type post and you can edit it. Except you’re not told you can edit it. You’re just told your site is live.

    First up, that “Write A Post” button should have been “Complete Setup”.

    Second, when I do complete setup, I should have a nice popup to tell me “Your site is setup! This is your first post. You can edit it…”

    There is a nice EDIT button, but that should have been animated too. That takes you to the editor, which is realtime and actually quite nice.

    Ghost's Post Editor

    Continuing Setup Is A Five

    If you go to https://ghost.org/setup, it will tell you there are five steps to setup.

    1. Create an Account (you’ve done that to get this far)
    2. Writing a post (you have to write a new one, not edit the existing one)
    3. Picking a Theme
    4. Add a domain
    5. Share your work

    Write A Post? Let’s Try.

    The setup is an editor only on the left, with a preview on the right. Fine. Click click type. Then I wanted to add an image, so I tried the old drag & drop from WordPress. Nope! Looking at the Help, I found this:

    When adding images to your Ghost blog, you start by either pressing Ctrl+Shift+I or by typing in ![]() into your post editor. You will then see an image box show up on your markdown preview.

    That was fairly easy to find, but then I got this:

    Ghost wants me to link to an image, not upload it

    It took me a moment to realize I could click on that to get the uploader interface. In fact, not until I hovered over and saw ‘No File Chosen’ did it register. The little link icon on the bottom left made sense, but there was nothing that told be “Click here and upload.”

    Scheduled Posts … Why?

    I decided to try scheduling a post. Since by default the save button on the bottom right is ‘Save Draft’ and I knew by hovering that I could do a ‘Publish Now’ there, I assumed the little gear to the left was for extra things:

    Ghost's Post Now buttons

    And lo, it did show me a lot of options, that were just a bit too long for my 15″ monitor:

    Ghost's Publishing Options

    There I was able to pick a future date, but instead of changing to ‘Schedule Post’ the button remained ‘Publish Now’ which was rather disconcerting. Picking publish, it worked just as it was supposed to, though, so there’s that.

    Themes Don’t Fly

    Time to pick a theme! From the getting started flow, I pressed the button for ‘Marketplace’ because I don’t need to watch a video, right?

    Ghost, go to theme marketplace

    That button takes you back to your Ghost dashboard. From there you have to click on the link at the top of the page for the Marketplace. Then you download the theme’s zip, go to the settings page for your blog, and upload the zip there. Very weird. Very odd.

    Overall? Not Yet.

    I like it. It’s easy to write once you figure out a couple things, but the disjointed behavior of where you go to do things is confusing and a bit of a headache. For a brand new user who’s never have a website, it fails when you compare to WordPress.com except in the arena of posting content. It’s simple for that. It’s the management levels where it fails.

  • Git Flow

    Git Flow

    Late as always to these git things.

    In the ever increasing madness to the method of making code easier to do, we come to the automation of git-flow. This is a library of git subcommands that helps automate some parts of the flow to make working with it a lot easier if you’re using Vincent Driessen’s model.

    This model is not bad, if a little confusing at first:

    The git-flow model

    Jeff Kreeftmeijer already has a good primer for it so these are just my quick notes to add in things that should have been obvious. The basic concept for the model is that you have two main branches, master (the ‘current’ version) and develop (where you’re working on the next version).

    Install git-flow

    You have to install it to use it. I use Homebrew so it was brew install git-flow and that was that.

    Add it to your repo

    Again, you have to add it to have it. git flow init is run inside the repo and you’re good, except you may not be.

    Since I’m starting from an existing repository, which was up to date in master, I made a new branch: git checkout -b develop

    Then I ran git flow init which is not the same as it would be on a new repo:

    $ git flow init
    Which branch should be used for bringing forth production releases?
       - REL_3.3
       - REL_3.3.1
       - REL_3.3.2
       - master
    Branch name for production releases: [master] master
    
    Which branch should be used for integration of the "next release"?
       - REL_3.3
       - REL_3.3.1
       - REL_3.3.2
    Branch name for "next release" development: [master] develop
    
    How to name your supporting branch prefixes?
    Feature branches? [feature/]
    Release branches? [release/]
    Hotfix branches? [hotfix/]
    Support branches? [support/]
    Version tag prefix? [] REL_
    

    Should you happen to do as everyone says and ‘accept the defaults’ you get this:

    Branch name for "next release" development: [master]
    Production and integration branches should differ.
    

    Also if you don’t have a develop branch, it punts you out.

    Branch name for "next release" development: [master] develop
    Local branch 'develop' does not exist.
    

    So a couple ooops along the way. It’s not

    Using A Feature

    First you make a feature:

    $ git flow feature start utility_1.0.3
    Switched to a new branch 'feature/utility_1.0.3'
    
    Summary of actions:
    - A new branch 'feature/utility_1.0.3' was created, based on 'develop'
    - You are now on branch 'feature/utility_1.0.3'
    
    Now, start committing on your feature. When done, use:
    
         git flow feature finish utility_1.0.3
    

    Eventually you’ll be done and use that finish command, which merges it all back into ‘develop’ and changes you back to that branch. Which is good for reasons we get to in a moment. Since I work on multiple computers, I do this at the end of my workday:

    $ git push --all
    Total 0 (delta 0), reused 0 (delta 0)
    To ipstenu@example.com:/home/ipstenu/repositories/theme-wordpress.git
     * [new branch]      develop -> develop
     * [new branch]      feature/utility_1.0.3 -> feature/utility_1.0.3
    

    So now when I do my pull later on the other computer, it’ll be easily usable. Provided I remember to install git-flow and init it on my other laptop. But it’s a little more complicated than just that.

    Releasing a Release

    You can only run this from the develop branch, which makes the previous command pretty awesome, right? So now we’ll do this: git flow release start RELEASE

    Once we’re good to go, it’s git flow release finish RELEASE

    What About Two People?

    This is where we want to use publishing. Technically I’m collaborating with myself across two computers, so I always publish my feature to my remote server so it can be used by my other self: git flow feature publish MYFEATURE

    To pull the feature down, git flow feature pull origin MYFEATURE is just as logical. And that’s actually how I handled pulling the feature utility_1.0.3 onto my work laptop.

    $ git checkout master
    Switched to branch 'master'
    Your branch is up-to-date with 'origin/master'.
    $ git branch develop
    $ git flow init
    [snip .. same as before]
    $ git flow feature pull origin utility_1.0.3
    Created local branch feature/utility_1.0.3 based on origin's feature/utility_1.0.3.
    

    There’s a good cheat sheet for help about this called Git Flow Cheatsheet.

    Now there’s nothing but to do it.

  • Slow Site Troubleshooting: Database Edition

    Slow Site Troubleshooting: Database Edition

    So your WordPress is slow and you’ve already done the needful. You’ve checked your plugins and themes, you’ve put caching in place, you’ve checked for hacks, but it’s still slow, especially on the back end of WordPress?

    It may be your database.

    More specifically it may be your wp_options table. When your options table gets very, very large, it gets very, very slow. WordPress regularly queries that when you’re logged in, and it’s not indexed. DB indexes are used to locate data fast, without searching every row in the tables. This sounds sensible in many ways, but we don’t

    Indexes are used to quickly locate data without having to search every row in a database table every time a database table is accessed. Indexes can be created using one or more columns of a database table, providing the basis for both rapid random lookups and efficient access of ordered records. We don’t use ’em in the options table for a variety of reasons, but mostly that it slows things down.

    So in lieu of making indexes on your own, what can and should you do to debug things?

    Optimize Your Database

    Got WP-CLI?

    wp db optimize
    

    Otherwise you can use phpMyAdmin to check tables with overhead and clean ’em up. I don’t use a plugin, I think asking WP to optimize itself is a little weird, but you certainly can. The point here is to keep it clean.

    Cache that Database!

    Caching makes things better. Right? Kind of right. Mostly right. I use memcacheD (which I often typo as memecached) and that plus an object-cache.php file can cache your DB calls so they’re faster, which is great. Unless your wp_optimize table is too big.

    Your cache has two major issues. First, there’s going to be data you don’t want to cache (like private, sensitive information), but also when your cache is too big, or it’s trying to save data bigger than it is, it can make things slower by trying to cache, crashing, and repeating that over and over. That gets worse when we talk about the temp data generated by _transient entries in your database.

    Check The Size

    Your options table really shouldn’t be large. My biggest, busiest, site is only using 142K. It’s a site that’s old (8+ years now), it’s had many iterations and themes and plugins. You’d think it would be filthy with leftover code, because we all know plugins don’t always clean up. Nope. I did rebuild the DB once, in 2009, when I rebuilt the entire site from scratch, but that was 5 years ago and a lot has changed since then with plugins and themes. The next biggest site, a Multisite Network, has a wp_options of 100k. The biggest I’ve had is one of 500kb and that’s on a test site where I install and delete plugins daily.

    You get the point I trust. These things should be small. At most, I’m going to have a lot of _transient entries. But that’s actually issue here.

    Clean The Transients

    There’s a story here about why WordPress doesn’t really clean your transients. Why? Well as the name implies, transient data is meant to be transient. It should be temporary data, saved and used briefly, and then it should go away. A lot of developers, as it happens, were storing it for long term caches. Like checking when the last upgrade ran, or when a cron kicked off. So while for one, glorious, month we did nuke them all on upgrade, now we only delete expired transients, which doesn’t help as much as it could. As Nacin said:

    This leaves much to be desired, but we don’t want a core update to be blamed for breaking a site that incorrectly assumes transients aren’t transient.

    Basically people doing things wrong in a way we couldn’t adjust for.

    There are plugins like Delete Expired Transients and Transient Cleaner (which has the cutest header image). Those can be used to clean out your old transients.

    If you want to go whole hog, there’s a command to clean the whole thing out with SQL:

    DELETE FROM `wp_options` WHERE `option_name` LIKE ('%\_transient\_%')
    

    Of course you want to run a db optimize afterwards to actually flush out the rows.

    As always WP-CLI has features like wp transient delete-expired and wp transient delete-all.

    What if that doesn’t help?

    Then you should check the Database to see exactly what the biggest value is. In this case, I ran wp db cli to leap into the database and then this:

    mysql> SELECT option_name , length (option_value) AS blah FROM wp_options ORDER BY blah DESC LIMIT 5;
    +--------------------------------------------------+----------+
    | option_name                                      | blah     |
    +--------------------------------------------------+----------+
    | cron                                             | 12194468 |
    | _transient_feed_d117b5738fbd35bd8c0391cda1f2b5d9 |   223838 |
    | _transient_feed_ac0b00fe65abe10e0c5b588f3ed8c7ca |    98184 |
    | _transient_is_cached_instagram_images_self       |    97315 |
    | mytheme_storage                                  |    18354 |
    +--------------------------------------------------+----------+
    10 rows in set (0.02 sec)
    

    CRON is 12 megs. That would be the problem. Of course the only way I know of to fix that would be to totally trash cron and let it start over.

    Is That It?

    When you see a ginormous wp_options table, what do you do?

  • Mailbag: Debugging the Dread “No Permissions”

    Mailbag: Debugging the Dread “No Permissions”

    Sometimes customers hunt me up here. That’s okay. I’d rather you opened a ticket since I like to eat, sleep, and hang out with my wife, but in this case they also had an interesting problem.

    When I log in, I get “You do not have sufficient permissions to access this page.”

    I found the support ticket and solved it, and then I mailed the DreamPress Ninja’s with this methodology. It relies on wp-cli, so bear with me.

    The first thing to do is check the user list:

    user@server:~/example.com$ wp user list
    +----+-----------------+--------------------------------+-----------------------------+---------------------+---------------+
    | ID | user_login      | display_name                   | user_email                  | user_registered     | roles         |
    +----+-----------------+--------------------------------+-----------------------------+---------------------+---------------+
    | 2  | bobby           | Bobby Done Nightly             | bobby@example.com           | 2014-02-12 17:44:28 | administrator |
    | 3  | darren          | Darren Done Rightly            | darren@example.com          | 2014-09-29 17:49:11 | contributor   |
    | 4  | ethan           | Ethan Done Wrongly             | ethan@example.com           | 2014-10-27 21:01:07 | subscriber    |
    | 5  | jimmybear       | Jimmy Eaten By A Bear          | jimmy@example.com           | 2013-12-16 14:45:18 | author        |
    | 1  | iamempty        | Admin Account                  | admin@example.com           | 2015-03-05 01:30:09 |               |
    | 6  | sonnyboy        | Sonny Boyd                     | sonny@example.com           | 2014-10-27 22:13:08 | contributor   |
    +----+-----------------+--------------------------------+-----------------------------+---------------------+---------------+
    

    Notice how imaginet has NO role? That’s the problem. So let’s give it a role!

    user@server:~/example.com$ wp user add-role 1 administrator
    Success: Added 'administrator' role for imaginet (1).
    

    Check again and all is happy!

    user@server:~/example.com$ wp user list
    +----+-----------------+--------------------------------+-----------------------------+---------------------+---------------+
    | ID | user_login      | display_name                   | user_email                  | user_registered     | roles         |
    +----+-----------------+--------------------------------+-----------------------------+---------------------+---------------+
    | 2  | bobby           | Bobby Done Nightly             | bobby@example.com           | 2014-02-12 17:44:28 | administrator |
    | 3  | darren          | Darren Done Rightly            | darren@example.com          | 2014-09-29 17:49:11 | contributor   |
    | 4  | ethan           | Ethan Done Wrongly             | ethan@example.com           | 2014-10-27 21:01:07 | subscriber    |
    | 5  | jimmybear       | Jimmy Eaten By A Bear          | jimmy@example.com           | 2013-12-16 14:45:18 | author        |
    | 1  | iamempty        | Admin Account                  | admin@example.com           | 2015-03-05 01:30:09 | administrator |
    | 6  | sonnyboy        | Sonny Boyd                     | sonny@example.com           | 2014-10-27 22:13:08 | contributor   |
    +----+-----------------+--------------------------------+-----------------------------+---------------------+---------------+
    

    If the roles had be totally empty, it’s a case where the database is looking for the ‘wrong’ table prefix and that’s a little messier. There, you’ll want to grab the table prefix from the wp-config.php file:

    $table_prefix  = 'wp_hsy671e_';
    

    Then go into the database and look at wp_hsy671e_options for a field called wp_hsy671e_user_roles – if you don’t see one, that’s the problem. Check for one named wp_user_roles and rename it.

    If you DO see the right user_roles, then the problem is all the users are pointing to the wrong table. You can just run wp search-replace wp_user_roles wp_hsy671e_user_roles to force it back.

  • The Ebb and Flow of Automation

    The Ebb and Flow of Automation

    Repetitive tasks suck. Let’s be honest here, we hate doing them most of the time. But also we’ll forget things and mess up our day. With code that’s pretty terrible.

    Over time, I’ve managed to automate a lot of things. WordPress upgrades itself, including plugins and themes. My code changes are auto-deployed via Git when I update them. I use a cool Coda plugin to automatically generate css from multiple files.

    I’ve now added Grunt to the mix. Grunt runs tasks for you so you can be lazy (efficient) but it’s not perfect. One of the things I love about my other tools is I don’t have to think about them. WordPress takes care of itself, git has hooks to know what it’s doing, and Coda monitors my files. I don’t have that for Grunt.

    What you have, instead, is ‘grunt watch’ which is great but it only runs while you’ve told it to watch. You have to remember to activate it every time. And I understand why. Watching is a rather expensive (computer wise) tool. But it annoys me because I already struggle to remember that the way a tool I use works, I have to run this to checkout the code: git checkout [tag_name] -b [my_branch_name] (which for some reason my brain thinks is ‘better’ than scripting a zip).

    So why would I use Grunt?

    On a site I run, I have a folder filled with scripts that are used on four separate CMS tools. One is WordPress. I’m often adding new scripts and I like to have the scripts separate because that’s easier for me. But that also means I have to edit all my CMS to add in the new JS. But with Grunt I don’t.

    Instead, I have one simple Grunt file that says “Any js file in /content/code/js/dev/ should be combined into one mondo js file and then uglified to compress it down.”

    Installing Grunt

    I didn’t have Node installed, but I do use homebrew, so this was it for me:

    brew install node
    npm install -g grunt-cli
    

    Okay. Now what?

    My First Project

    I knew I wanted to make a folder in my /content/ folder for this, so I made /content/project/. In that folder I ran the command npm init and answered the questions to generate my package.json file. That was the easy part actually. After that had to decide what I wanted to do.

    1. Concatenate (combine) all my JS files into one.
    2. Compress those files to make them minified.
    3. Watch the files while I was working on them and automate that.

    In order to do that, I need to install three grunt ‘plugins’: concat, uglify, and watch:

    npm install grunt-contrib-concat --save-dev
    npm install grunt-contrib-uglify --save-dev
    npm install grunt-contrib-watch --save-dev
    

    Doing it that way, with the save dev call, automatically adds it to my package.json file so it knows to call that.

    Next I had to make a filed called Grunfile.js and put my actual code in it. I’ve got a copy of the file below but you can break it up into some pretty basic sections. Here’s a sample file:

    module.exports = function(grunt) {  
    
        grunt.loadNpmTasks('grunt-contrib-uglify');
    
        grunt.initConfig({  
            pkg: grunt.file.readJSON('package.json')
    
            uglify: { ... }
    
        });
    
        grunt.registerTask('default', [] );  
      
    };
    

    The first call is to load the npm (node) tasks, which means I’m telling it ‘include these plugins’.

    The second call is my init command which runs when the ‘grunt’ command is used. In there I’ll put the name of my json file, where I’ve defined all my dependancies (not all get loaded by the Grunt file you see) and then I’ll make a ‘case’ for my code. That’s what the uglify: { ... } bit is.

    The last is registering the default tasks. At this point, if I want to do anything I would have to type grunt uglify to do anything, so what you can do instead is make it this:

    grunt.registerTask('default', ['uglify'] );
    

    Now when I run ‘grunt’ it will automagically uglify. If you have multiple steps, put them in order of what you want to happen, and off you go.

    At this point, I’m now a GruntJS rookie, but I can see why it’s amazing. One can use this in plugin development to make it easier to mush up all your code into something smaller to load.

    My Scripts

    For those wondering, here are my scripts:

    package.json

    {
      "name": "jfo-content",
      "version": "1.0.0",
      "description": "JFO Content Scripts",
      "main": "Gruntfile.js",
      "dependencies": {
        "grunt": "^0.4.5",
        "grunt-contrib-uglify": "^0.7.0"
      },
      "devDependencies": {
        "grunt-contrib-concat": "^0.5.0",
        "grunt-contrib-uglify": "^0.7.0",
        "grunt-contrib-watch": "^0.6.1"
      },
      "author": "Mika Epstein <ipstenu@halfelf.org>",
      "license": "WTF"
    }
    
    

    Gruntfile.js

    module.exports = function(grunt) { 
    	
    	grunt.loadNpmTasks('grunt-contrib-uglify'); 
    	grunt.loadNpmTasks('grunt-contrib-concat');  
    	grunt.loadNpmTasks('grunt-contrib-watch');
    	
    	grunt.initConfig({
            pkg: grunt.file.readJSON('package.json'),
    
    		concat: {  
    		    dist: {  
    		        src: '../code/js/dev/*.js',
    		        dest: '../code/js/content.js',  
    		    },  
    		}, 
    
    		uglify: {
    		    build: {  
    		        src: '../code/js/content.js',
    		        dest: '../code/js/content.min.js',
                        }
    		},
    
    		watch: {  
    		    scripts: {  
    		        files: '../code/js/dev/*.js',
    		        tasks: ['concat', 'uglify'],  
    		    }
    		}
    
    	});
      
        grunt.registerTask('default', ['concat','uglify'] );  
      
    };
    

    post_update (git)

    I added in a line here to delete the package folder from my live site. That’s where all my Grunt stuff lives and I really don’t need it. It also nukes the dev folder. Sometimes. In some cases I leave that alone, since it’s not like I’m worried about space.

    #!/bin/sh
    export GIT_WORK_TREE=~/public_html/content/
    git checkout -f master
    rm -rf ~/public_html/content/package/
    rm -rf ~/public_html/content/code/js/dev/