Generate a custom _s (underscores) WordPress theme from command line

I’m building out a new theme for my blog using the _s theme. It’s an exercise in building out a responsive grid. _s is a great theme starter. Underscores.me has a nifty little tool to generate the starter theme with namespaced functions, title, description, etc… for the theme. I wanted to generate the theme directly from command line. Here’s what I used to generate the theme directory with one line (your pwd should be wp-content/themes/):

curl --data "underscoresme_generate=1&underscoresme_name=Coderrr&underscoresme_slug=coderrr&underscoresme_author=Brian+Fegter&underscoresme_author_uri=http%3A%2F%2Fcoderrr.com&underscoresme_description=A+custom+theme+for+coderrr.com." http://underscores.me >> coderrr.zip; unzip coderrr.zip; rm coderrr.zip;

There are four things that happen here:

  • Curl http://underscores.me and send post data
  • Tell Linux to place all returned data into a zip file
  • Unzip that file and create the theme directory
  • Remove the zip file

If you don’t want to format your URL encoded data, visit underscores.me and fill out the advanced form. You can extract your post data by using the Google Chrome Dev Tools network tab.

_s

Why My ISP Kicked Me Out

UPDATE: Before reading this, I wanted to note that the ISP made good on a refund. I was refunded the entire amount of installation and two months of service ($686.09). They actually corrected their error and gave me back my cash. I’m satisfied with their actions.

It was early Saturday morning and I was working on some personal projects when my connection started flaking out. I took to my Verizon tethering via iPad and didn’t think much of it until I tried logging back into my home wifi. Everything seemed in order until I tried to browse the web. I was redirected to a hacker radio website and all Google searches were popping off a 404 error at another malicious website. I tried browsing on several devices and the same thing was happening on each. I immediately disconnected all devices from the internet thinking my router had been compromised.

I quickly unplugged my router from WAN and flashed it with a fresh load of DD-WRT. I knew that would destroy whatever was hijacking the router. After about 40 minutes I plugged back in and to my shock, the same thing happened again. I check my /etc/hosts and nothing was out of the ordinary. So, I pulled out my dusty Windows laptop which hadn’t been used in ages and plugged it directly into the ethernet connection from my ISP. The same thing happened on the laptop. I should have started with that, but now I knew that it was coming from WAN.

I contacted my ISP and their 24 hour support line sent me to a voicemail several times. I sent an email to them (via tethering) and went to bed. I woke up at 7:30am and it was still redirecting to malicious websites. I sent another email and asked for a callback. At about 10:00am, the manager called and left a messaging saying they had an ‘outage’. I contacted several people I knew who use this ISP and they confirmed the same at all of their locations. For over 9-10 hours all customers in my city were being redirected to malicious websites from this ISP.

I sent another email asking them to inform their customers of the following:

  • Was handed over to the proper authorities?
  • What happened?
  • How was it fixed?

I received an email back stating that Internet security is the responsibility of the ‘individual’. This volley went on for a couple more emails. I received another email stating that the “Internet breaks at times”. When I could not even get acknowledgement that this event ever occurred, I finally started typing in caps. I said, “YOU MISSED THE POINT! YOU WERE HACKED!”. I noted that my usage of caps was me yelling. I never used an expletive. I also gave a condescending math equation for finding a common denominator. If customer A has this happening at their location, and customer B has this happening and customer C has this happening, what can we deduce from this? Apparently that hurt the manager’s feelings.

The next email I received made my head explode. “If you continue to yell at me, I will be forced to discontinue your service.”. I quickly replied “You do that and you’ll be hearing from an attorney.” (Insert email silence for a long period of time) I wrote back saying there’s nothing in the contract that I could see that included yelling and disagreeing as grounds for breaking the contract. I also called this behavior ‘fascist’. It was snarky I know, but I was upset.

After no reply, I called the manager and he happily said that his legal team was reviewing my ‘threatening’ (ALL CAPS) email and will give a reply back soon. I had to hang up on the guy so I didn’t say something foolish. In a few short minutes, the CTO of the company emailed me berating me for threatening her manager with legal action and that they would not tolerate disrespect of their employees. Again, I was a bit dumbfounded as I had not disrespected the manager outside of my CAPS and petty mathematical equation.

She included a clause of the contract, which I missed because it was fine print, that stated they can terminate me for any reason at any time at their discretion. She said I would need to find another ISP and to let them know when I found one so they could terminate the contract. She was ‘kind’ enough to allow me to leave without paying penalties. I was also told to leave the equipment on the porch as they didn’t want me conversing with their tech. I was left out in the cold and the arbitrary clause left me without any form of recourse.

They stated it’s because they are a family-owned business that they can do this. It doesn’t sound like any family I want to be a part of. This was the worst customer service experience I’ve ever had in my 32 years of living. My wife asked me, “Aren’t companies supposed to try to keep customers instead of pushing them away?” Yes, that’s what I thought. What ever happened to the customer is always right? Apparently that’s old-fashioned in this post-modern world.

I still don’t understand why they dropped me. It has to be for one of the following reasons:

  • They were covering up that they were actually hacked and I was pressing some hot buttons.
  • They were nursing their manager’s broken heart
  • They don’t like people disagreeing with them or calling their bluff

Since I live in the boonies in Texas, there are very limited options for connection. This ISP (4G) was the best and only reliable option. I had to purchase a $550 apparatus tripod and receiver that was installed on my roof. It’s 10 feet high and the signal was okay for about 95% of the time. We paid two months of service at $65/per month. The $550 is non-refundable. So, averaging out my total costs, I paid this ISP $340/month for two months of Internet usage on a two year contract. I searched all over town to try to find DSL or Cable and these services come within 800 feet of my house, but not to my house. Since I work on the web, this leaves me in a huge bind.

Thankfully, I have a Verizon 4GLTE device that will get me through month-to-month. We are getting rid of Netflix and other bandwidth sucking apps until there is a more stable Internet connection. This might be a blessing in disguise to get us more focused with a new baby coming.

The lessons I’m taking from this are many, but I’ll note a few. NEVER USE CAPS WHEN CONTACTING SUPPORT. Never give condescending arithmetic lessons to a mad person. Read the contract thoroughly before signing. Stop calling people fascist when they are exhibiting fascist behavior. When moving to a house in the country, check Internet connectivity first (The cattle ranch across the street should have been a clue).

Finally, I’m going to do what the Bible teaches and do good to those who are my enemies. After much honest, soul-searching on this, I determined that I have been the victim of an injustice for sure. I’m sending the manager and CTO a small gift card for Starbucks. I’m hoping this act of kindness will spur their conscience to not treat other customers as they treated me.

And that’s the story of my ISP kicking me out of their pond. I purposely left out their company name as they have threatened to sue me for writing about this.

Setting Proper WordPress Unix Permissions

One of the biggest black boxes of WordPress for newbies is file ownership/permissions. I’ve had numerous people ask me why they can’t use the default theme editor or install new plugins directly from wp-admin. The symptoms usually range from getting the FTP credentials screen to error messages that WordPress cannot create a specific directory, and more.

It took me a while to grasp the concept, so I hope the following explanation will help you. This, in no way, is a security tutorial. It is a simple tutorial on Unix users/groups and file permissions. You should also read Hardening WordPress. If you’re not sure about file permissions, read this article first.

Groups and Users

The first order of business is making sure you have the proper user/groups setup. I typically create one unix group per site that I host. This allows me to have granular control over who’s allowed to work in that site directory.

Add a new user

useradd newusername

Add the user to your site group

usermod -a -G domainnamegroup newusername

Find the Apache user
This is typically ‘apache’ or ‘www-data’. Note this for the next step.

ps aux | grep apache

File ownership

Now that we have a user in a group, we need to assign that group to our WordPress install. It’s critical at this point to decide whether these users can only operate in wp-content or the entire docroot. Let’s say we only want to give this group dev access to themes/plugins/uploads/etc… You will keep ownership of the core files (including wp-config.php and .htaccess) with your personal username.

Change all file ownership to your personal user and group
This is the basis for restricting users from working on core files.

cd /path/to/your/docroot
chown -R yourusername:yourusername

Change wp-content file ownership to the apache user and new domain group
This gives Apache and your dev group access to these files

cd /path/to/wp-content/
chown -R %APACHE_USERNAME%:domainnamegroup

Change file permissions

Go to your docroot

cd /path/to/your/docroot

Change all file permissions to 0664. This sets core files to read/write for your personal user and personal group and read only for the rest. This also sets user/group permissions for the wp-content directory as well.

find . -type f -exec chmod 0664 {} \;

Go to wp-content and change all directory permissions so dev users can add/delete/modify files within. This keeps WordPress from flashing the FTP credentials screen. For plugins like W3TC, you might need to add a 0755 permission temporarily to wp-content itself so the plugin can build out the file structure it needs.

cd path/to/wp-content
#Allow apache and your dev users to add/modify/delete files within wp-content directories
find . -type d -exec chmod 0755 {} \;

Automatic Core Upgrades

I’m not a big fan of allowing automatic core upgrades if there are other WP admins using the site. I prefer to keep control of this and not have unmitigated disasters (with older plugins and themes) because someone is trigger happy. The setup I described above will not all for automatic upgrades. I typically upgrade manually to keep file permissions static. However, if you want to allow for auto upgrades temporarily, you can do the following:

cd /path/to/docroot
#Allow all directories except wp-content to be modified
find . -type d \( -iname "*wp-*" ! -iname "wp-content*" \) -exec chmod 0755 {} \;

#Add the apache user group to all files
#This allows temporarily locks out your dev group while the upgrade takes place
chown -R yourusername:%APACHE_USERNAME%

#Run the upgrade
#Revert permissions back

#Make all directories except wp-content writeable
find . -type d \( -iname "*wp-*" ! -iname "wp-content*" \) -exec chmod 0644 {} \;

#All core files belong to you
chown -R yourusername:yourusername

#Give your dev group access to wp-content directories/files again
cd /path/to/wp-content/
chown -R %APACHE_USERNAME:newdomaingroup

Caveat

Not all systems are created equal. These settings might not work well on a shared environment. Contact your host if standard permissions don’t work. Command line gives you much power, use it wisely. I take no responsibility for your command line work.

Recursive FTP get for command line

If you’re like me, you hate using a FTP GUI for doing simple gets. Sometimes, GUIs get in the way and bog the transfer down. I prefer to take out the middle man and use command line. Here’s a nifty snippet I picked up along the way that lets you recursively download entire directories. The files are stored in their identical structure in your working directory.

wget -r --user %user% --password %password% ftp://%server%/full/path

If you want to stick with pure FTP commands, you can use the following:

#Turn off confirmation for getting each file
ftp>prompt
ftp>mget *

WordPress Rewrites Without Duplicate Content

There are times when you need to surface content in a different context. For instance, you might have an events section and you want to have your related live-blog posts reside within the context of that specific event URI structure. The problem lies in surfacing the same content in the original location and in a rewritten location. Search engines frown on this. You can easily sidestep this and stay search engine friendly by setting up simple redirects.

Continue…

Automatic git status emails

This is a basic, elementary script to audit a git working copy. This is useful to keep a watchful eye on cowboy coding on a client server.

Save the following inside your git repo as .git-status.sh. Make sure to add ‘.git-status.sh’ to your .gitignore file.

#!/bin/sh
DOMAIN = "http://whatever.com"
SENDTO = "your@email.com"
MODIFICATIONS=`git status -s`
if [ ! -z "$MODIFICATIONS" ]
then
        SUBJECT = "git changes from $DOMAIN"
        EMAIL = "$SENDTO"
        BODY = "/tmp/emailbody.txt"
        echo "Changes found on the following paths:\n" > $EMAILBODY
        echo "$MODIFICATIONS" >> $EMAILBODY
        mail -s "$SUBJECT" "$EMAIL" < $EMAILBODY
        echo "$MODIFICATIONS"
fi

To automate, add a crontab:

30 15 * * * /path/to/your/script/.git-status.sh 2>&1>> /dev/null

Continue...

Send a WordPress Auth Cookie with HTTP API Requests

This snippet I created lets you hit your site with a remote request that includes your current auth cookie. I use this to circumvent caching plugins that use output buffering when I need to store a page load for analysis.

Testing Plugins and Themes on Multiple WordPress Versions

If you’re a theme or plugin developer, it’s essential to support at least WordPress 3.3+. Smaller sites and blogs have an easier time upgrading to the latest WP version. However, upgrading large corporate sites becomes a major operation because the codebase is usually collaborative and has many dependencies built on earlier versions. With that said, it’s good to be inclusive of those users as well by adding backwards compatibility.

I typically test my plugins on the major releases. If users have issues on minor releases, I’ll generate an install for that version on my local environment and debug from there.

On my local, I surface all WP versions under one domain, whatever.com. Each WP version is a subdomain. So, WP 3.3 is found at 3.3.whatever.com and so on. It’s easy to copy a vhost and a /etc/hosts entry for a new WP instance or use a software like MAMP Pro to do it for you. Another approach is to place each version in a subfolder off the domain docroot. You can find all of the WordPress versions at the core SVN repo. If you want to use GIT, you can find a synced SVN > GIT repo here. Simply fetch whatever branch corresponds to your desired version number.

To install my plugin, I checkout the trunk of my plugin into the plugins folder of each WP version. I use Versions App and title the bookmark as the version number. If you’re on Windows, you can use TortoiseSVN as well. I find SVN to be annoying on the command line, so I use a GUI. To note, the reason I use trunk checkouts and not symlinks for my plugin folders is because symlinks don’t play nice with plugins_url() from my experience. It’s a good practice to stay away from using constants directly to circumvent the symlinks issue and use API functions instead.

I keep all of tagged versions of WP in one database with different table prefixes. This keeps everything nice and tidy and in one place. For example, WP 3.3 becomes wp_33_ and WP 3.4.2 becomes wp_342_. Another benefit to this approach is you don’t have to create a new database each time you want to spawn a new WP instance. I also title each WP instance site title with the version number as another indicator of which version I’m developing.

Finally, I keep a copy of the latest nightly build to test for future compatibility as well. To get the latest nightly, simply do a SVN checkout of WP trunk.

Get file permissions, owner and group with PHP

Here are a few functions to help you when interacting with files:

Find the owner of a file:
An array of information about the owner is returned.

function foo_get_file_ownership($file){
	$stat = stat($file);
	if($stat){
		$group = posix_getgrgid($stat[5]);
		$user = posix_getpwuid($stat[4]);
		return compact('user', 'group');
	}
	else
		return false;
}

Get the four digit file permissions number:
A permissions string is returned. Example: 0755

function foo_get_file_perms($file){
	return substr(sprintf('%o', fileperms($file)), -4);
}

Convert permissions number to Read Write eXecute format:
Directory Example: 0755 converts to drwxr-xr-x
File Example: 0664 coverts to -rw-rw-r–

function foo_convert_perms_to_rwx($perms, $file){
	$rwx = array(
		'---',
		'--x',
		'-w-',
		'-wx',
		'r--',
		'r-x',
		'rw-',
		'rwx'
	);
	$type = is_dir($file) ? 'd' : '-';
	$owner = $perms[1];
	$group = $perms[2];
	$public = $perms[3];
	return $type.$rwx[$owner].$rwx[$group].$rwx[$public];
}