All things related to web

Missed WordPress Schedule

WordPress - missed schedule

It seems with every new WordPress version there is the same issue. For one reason or another, post scheduling stops working. Exact cause is varied but most commonly it is the caching plugin playing games.

Usual solutions for this are either manually calling wp-cron.php via wget or getting WP Scheduled Plugin. I believe most sites, including mine, need another plugin as much as pig needs a wig. I am not judging if you are into either of it, but I recommend limiting both activities.

Using curl or wget to manually execute wp-cron.php might also not work on sites that are properly secured and have most of php disabled in .htaccess to start with. Yes, you can always make an exception, but there is a better way.

First step is common, just disable standard WordPress cron behavior in wp-config.php:

define('DISABLE_WP_CRON', true);

Then either use crontab -e from command line or your web provider’s task scheduling web interface (CPanel or similar) to add following command:

/usr/bin/php -q /home/user/www/wp-cron.php

This will call upon PHP to manually execute wp-cron.php bypassing Apache and .htaccess completely. Notice that you must use full paths as cron jobs are ran in limited environment.

For my needs, a daily frequency (@daily or 0 0 * * *) is actually sufficient as I schedule my posts always for midnight. Those needing more precise time might decide to go hourly (@hourly or 0 * * * *) or even more often.

Random Blog Description for WordPress

Ages ago, back when I created my first dynamic ASP web pages, they had an ever changing tagline. Some taglines were funny, some were sad, some were crazy, but I enjoyed them as homage to the, now forgotten, BBS era. As I moved from one hand-built platform to another, I kept this feature alive.

I started blogging much later on the Google’s Blogspot and it wasn’t possible to get dynamic taglines there. Later, when I moved the whole blog to WordPress and merged it with my original pages, end result was more of a blog. And thus taglines were no more. They were relegated to manually changing Skype status to entertain a friend or two. Until now.

My goal was to create the simplest and reasonably performant way of selecting a random tagline from flat text file.

One approach fitting with WordPress would be to create plugin but I opted not to. Since I really wanted to change tagline once a day, plugin would be probably a bit of overkill. Instead I opted to (ab)use fact WordPress already has tagline-like field called Blog description and all we need to do is change it to text of our choice.

Of course, before we even come to that step, we have to extract tagline from file. Fortunately Linux offers shuf utility to randomly select one line of many. All needed is to give it a plain text file. Of course, we should escape all single quotes to avoid any SQL issues. If we (hopefully correctly) assume text file with taglines is under your control, simple escaping is sufficient:

TAGLINE=`shuf -n 1 ~/taglines.txt | sed "s/'/''/g"`

With tagline in hand we can go and change blog description directly:

mysql --execute="UPDATE wp_options SET option_value='$TAGLINE' WHERE option_name='blogdescription';"

While this will change description, if you use caching plugin, it won’t be enough. You also need to clean cache. The easiest approach is to simply delete cache folder. As we do it only once per day, this won’t be too much of a hit. Different caches might use different locations, but for W3 Total Cache I use here, following is enough:

rm -R ~/www/wp-content/cache

All left to do is getting this script to be executed daily by either using web interface of your web provider or setting it up in crontab manually.

PS: Instead of using shuf, you can use sort -R ~/taglines.txt | head -1.

PPS: Full script I use is here:

#!/bin/bash

MYSQL_USER=^^WordPress MySQL user^^
MYSQL_PASSWORD=^^WordPress MySQL password^^
MYSQL_HOST=^^WordPress MySQL host^^
MYSQL_DATABASE=^^WordPress MySQL database^^

TAGLINE=`shuf -n 1 ~/taglines.txt | sed "s/'/''/g"`

mysql --host=$MYSQL_HOST --user=$MYSQL_USER --password=$MYSQL_PASSWORD --database=$MYSQL_DATABASE --execute="UPDATE wp_options SET option_value='$TAGLINE' WHERE option_name='blogdescription';"

rm -R ~/www/wp-content/cache 2> /dev/null

Not All EOLs Are Created Equal

As I was playing with the Wordpress shortcode methods, I came upon an interesting problem.

I wanted to change text in the particular line of the shortcode content and thus standard PHP explode and implode methods seemed like a best bet:

add_shortcode('something', 'something_callback');

function snippet_pre_shortcode_callback($atts, $content = null) {
    $lines = **explode('\n', $content)**;
    $lines[1] = "My line 2";
    return **implode('\n', $lines)**;
}

Nice and simple solution that didn’t work. The big content string I was sure had some lines, wouldn’t split. It took me a while to notice the error - single quote in PHP does not specify character but string with a minimal escaping. Most of the time they behave same as the double quotes which actually allow for much richer escaping.

Most annoying was that I knew this “feature” from before. However, too much work in the proper programming languages kinda made me overlook this trivial error multiple times while debugging. After taking a “frustration break” and coming back after 5 minutes, mistake was obvious.

The easy solution would be to swap one quotes for another. However, in this case a bit nicer solution exists - use the damn PHP_EOL constant:

function snippet_pre_shortcode_callback($atts, $content = null) {
    $lines = **explode(^^PHP_EOL^^, $content)**;
    $lines[1] = "My line 2";
    return **implode(^^PHP_EOL^^, $lines)**;
}

PHP, who wouldn’t love you… :/

Batch Optimizing Images

The same image can be saved in multitude of ways. Whether it is camera phone or editing application, usually goal is to save image quickly without caring for each and every byte. I mean, is it really important if image is 2.5 MB or 2.1 MB? Under most circumstances bigger file is written more quickly and slightly bigger size is perfectly acceptable compromise.

However, if you place the image on a website, this suddenly starts to matter. If your visitors are bandwidth-challenged, it makes a difference between the load time measured in seconds or tenths of seconds. However, if you start optimizing, you can spend way too much time dealing with this. If you are lazy like me and don’t want to change your flow too much, there is always an option to save unoptimized files now and optimize later.

For optimizing images I tend to stick with two utilities: OptiPNG for PNG and jpegoptim for JPEG files. Both of them do their optimizations in a completely lossless fashion. This might not bring you the best savings, especially for JPEG images, but it has one great advantage - if you run optimization over the already optimized images, there will be no harm. This means you don’t need to track what files are already optimized and which need work. Just run the tools every once in a while and you’re golden.

I created the following script to go over each image and apply optimizations:

@ECHO OFF

SET  EXE_OPTIPNG="\Tools\optipng-0.7.5\optipng.exe"
SET EXE_JPEGTRAN="\Tools\jpegoptim-1.4.3\jpegoptim.exe"

SET    DIRECTORY=.\pictures

ECHO = OPTIMIZE PNG =
FOR /F "delims=" %%F in ('DIR "%DIRECTORY%\*.png" /B /S /A-D') do (
    ECHO %%F
    DEL "%%F.tmp" 2> NUL
    %EXE_OPTIPNG% -o7 -silent -out "%%F.tmp" "%%F"
    MOVE /Y "%%F.tmp" "%%F" > NUL
    IF ERRORLEVEL 1 PAUSE && EXIT
)

ECHO.

ECHO = OPTIMIZE JPEG =
FOR /F "delims=" %%F in ('DIR "%DIRECTORY%\*.jpg" /B /S /A-D') do (
    ECHO %%F
    %EXE_JPEGTRAN% --strip-all --quiet "%%F"
    IF ERRORLEVEL 1 PAUSE && EXIT
)

And yes, this will take ages. :)

OpenGraph

Illustration

When you want to share your posts on social networks, having a help with formatting cannot hurt. For that purpose I used to be a happy user of Facebook Open Graph Meta Tags for WordPress. However, with version 1.3 something went kaboom and I started getting “failed to open stream” errors. After a few attempts of repair I decided it was a time to search for a new plugin.

I tried quite a few of them and, while they all worked with Facebook, Google+ presented a challenge. Some outright didn’t support it, some supported it only in non-free version and quite a few of them were huge SEO systems that would bring my poor installation to its knees. Thus I figured it was a time to make my own. I mean, how hard can it be?

Well It was quite easy to get a basic callback working:

add_action('wp_head', 'medo_opengraph_wp_head_action');

function medo_opengraph_wp_head_action() {
    if (is_single() || is_page()) {
        $post = get_post();

        $title = $post->post_title;
        $type = 'article';
        $url = get_permalink($post);
    } else {
        $title = get_bloginfo('name');
        $type = 'website';
        $url = home_url();
    }

    echo '<meta property="og:title" content="' . $title . '" />';
    echo '<meta property="og:type" content="' . $type . '" />';
    echo '<meta property="og:url" content="' . $url . '" />';
    echo '<meta name="twitter:title" content="' . $title . '" />'; //Twitter
    echo '<meta itemprop="name" content="' . $title . '" />'; //Google+
}

With this simple code I would get posts/pages having their proper title and everything else simply having a generic title alongside my main URL. But that was not really enough - my images were missing. Ignoring crazy array return parameters, code was pretty straightforward:

foreach(get_attached_media('image') as $media) {
    $imageAttrs = wp_get_attachment_image_src($media->ID, 'full');
    $image_url =  home_url($imageAttrs[0]);
    $image_type = $media->post_mime_type;
    $image_width = $imageAttrs[1];
    $image_height = $imageAttrs[2];
    break;
}

echo '<meta property="og:image" content="' . $image_url . '" />';
echo '<meta property="og:image:type" content="' . $image_type . '" />';
echo '<meta property="og:image:width" content="' . $image_width . '" />';
echo '<meta property="og:image:height" content="' . $image_height . '" />';
echo '<meta itemprop="image" content="' . $image_url . '" />'; //Google+

And the last one was to get excerpt for the post description. Here wp_trim_words came in really handy:

$description = $post->post_excerpt;
if (strlen($description) == 0) { $description = wp_trim_words($post->post_content); }

echo '<meta property="og:description" content="' . $description . '" />';
echo '<meta name="twitter:description" content="' . $description . '" />'; //Twitter
echo '<meta itemprop="description" content="' . $description . '" />'; //Google+

Did it went as smoothly as it could? Not really. WordPress Codex was less than helpful, rarely telling you anything more than a function syntax. Coding was more trial and error than anything else. But I cannot really complain. Even with all tryouts it took me less than two hours to get plugin working with all the functionality I needed. I would consider that a success.

And, like a masochist I am, I didn’t stop after those two hours. I got some profile options for Facebook ID in. And then I built a settings page. Made code a bit cleaner. Reordered thing or two. Before I knew it, couple of hours passed and I had something one might call a proper plugin. And it is available for download. Just unzip it in wp-content/plugins and you are golden.

Two Factor Authentication for WordPress

Illustration

Beside getting HTTPS working, probably the most important security feature you can get for free on WordPress is two factor authentication.

How does two-factor authentication work? In addition to your usual user name and password, you get to enter a 6-digit number changing every 30 seconds or so. Since that number is based on a key only you should know, you can consider it as another password. However, due to its constant change nature, anybody snooping only gets to know your login for next 30 seconds or so. After that time has passed previously captured code becomes useless. Two factor authentication essentially makes fact your password is known irrelevant.

It is not a fool-proof protection - somebody can just steal your key in addition to your password. However, since key itself is never transmitted over wire, it makes things considerably more difficult for attacker. And it will definitely make common every day non-targeted password attacks irrelevant.

Even if you run without HTTPS (which I don’t recommend) and you have to login over public wireless (scary!) this will keep anybody snooping from getting full account details he might need to login. Yes, there is possibility of somebody using your authentication cookie but, as long as you logout, you can rest assured that nobody can login after you. In a plain-text world there are many other attacks somebody might try against you but two factor authentication closes the most obvious doors.

I personally use Two Factor Auth plugin for this purpose. Although it officially doesn’t support WordPress 4.1 I found it works perfectly fine. Installation is WordPress-simple and by default you will get a pretty usable system of getting codes mailed to your users when they attempt login.

However, each user gets an opportunity to enable “third party” delivery type. That will give QR code you just scan into e.g. Google Authenticator and you mobile phone can generate codes every time you need them. System of generating these codes is completely standardized and I am sure you can find your favorite application - whether is on desktop, mobile phone, or even a watch.

It is a small change that will help security a lot.

PS: If you have Google mail and two-factor authentication is not enabled, what are you waiting?

Missing Updated With Suffusion

Illustration

One good indicator of web page health is its status in Google Webmaster Tools. Although I probably don’t go there often enough, I do try to keep-up with warning it gives me. This time it was complaining about errors in my blog’s markup.

Quick look into Google’s Structured Data Testing Tool just confirmed that my blog’s Suffusion theme was indeed missing proper information. How can I add it in?

Solution was rather easy once I found proper place. In ./wp-content/themes/suffusion/custom/post-header.php I found line that controls display of time:

<div class="date"><span class="month"><?php the_time('M'); ?></span> <span class="day"><?php the_time('d'); ?></span><span class="year"><?php the_time('Y'); ?></span></div>

Since its original output was definitely not microformats compliant, I took simple step of extending it:

<div class="date **value-title" title="<?php the_time('o-m-d'); ?>"**><span class="month"><?php the_time('M'); ?></span> <span class="day"><?php the_time('d'); ?></span><span class="year"><?php the_time('Y'); ?></span></div>

By putting value into title, I allowed Google and microformats to be happy and my theme kept its look&feel.

My WordPress

Illustration

Every WordPress installation is a unique creature as far as customization goes. Mine starts with latest WordPress installation combined with a great free theme called Suffusion. Beauty is, of course, in the eye of the beholder but I believe to be one of best themes out there. It is extremely configurable and you can find it works as well on multimedia-rich sites as on simple home pages as this.

As on all WordPress sites, plugins are plentiful and they change with seasons. Here is my list and reason for them:

Akismet

This is part of probably every blog out there. It will not catch every spam but it will help you deal with most annoying ones out there.

Broken Link Checker

Linking to other websites is path toward hell as far as I am concerned. While it seems as a good idea at first, it can lead to a lot of broken links years down the road. Sometimes nothing can be done about it - resource is simply gone - but this plugin at least makes you aware of it.

CloudFlare

If you use CloudFlare and you love statistics, you will want to install this plugin too. Without it every user would seem to come from the same set of proxied IP addresses and all that per-country log analysis would be for nothing. :)

Facebook Open Graph Meta Tags for WordPress

Simple plugin that does its job - gets your website sharing links for Facebook and Google+ have proper excepts and look decent. It is not fancy and there aren’t many things you can change but I find myself liking all defaults anyhow. I was using NGFB Open Graph+ before but with time it became annoying dealing with its advertisements for bigger Pro edition. Not only that you got a huge banner on your admin pages (sacrilege!) but they started removing features with newer editions (bait, hook & switch). I won’t use it again any time soon.

Fast Secure Contact Form

Simple solution for contact forms. It was a bit annoying to configure, but it worked flawlessly since.

Google XML Sitemaps

Google web crawler occasionally might need a bit of hint as what page is considered more important in your view. 99% of time everything will work properly regardless, this is just to cover all bases.

Limit Login Attempts

Protecting against brute-force password cracking is probably something that should be already built-in to WordPress. But this simple plugin will do to. Security must-have.

Nonsingular noindex

This is a custom plugin I built to avoid Google indexing search and category pages. As blog grew, it became annoying to see search pages in Google results higher than actual page and I had to do something about it. Since I haven’t found any plugin readily available, I decided to build one.

Online Backup for WordPress

If you love your site, you will backup it. And you will backup it offsite. Mailing it to GMail account is perfect for me and this plugin does it without issues.

Simplest icon link

Just a simple plugin to add Apple touch icon to website. Probably there is dozen other plugins that do the same, but I decided to roll my own.

Snippet pre

Never finished plugin for source code highlighting. Since I found every syntax highlighter lacking in some way I decided to build one for myself. While I do use it for new posts, it has severely limited capabilities in its current form.

Snippet text template

One more itch I had to scratch was repeating of same phrases over and over again on multiple pages. So I built this plugin to help me with that. Unsuitable for anybody else because of hardcoding, but it does its job here.

SyntaxHighlighter Evolved

Syntax highlighter that I stopped using because of some annoying bugs and lack of development. However, lot of older posts use it so it will stay here a while. Ultimate goal is to change all those posts to use my own highlighter (once I finish it) but lack of time will probably ensure that never happens.

W3 Total Cache

Probably best caching program out there. If you are using shared hosting and there is any significant traffic, you need something like that. Lot of small options help optimize for your particular situation.

Widget Logic

If you want to limit widgets to some pages only, this is plugin for you.

WordPress HTTPS

This plugin ensures that your login always goes over HTTPS instead of HTTP. Must have if you occassionaly use unknown WiFi to access your blog. Of course, you do need SSL certificate too.

Force HTTPS for WordPress Login Page

If you do have SSL certificate on your domain it would be shame not to use it for WordPress login. Since WordPress sends passwords as plain-text, it would be great thing if we would be redirected to HTTPS version for every login (even if we forget to specify it).

Solution lies in adding few lines to your .htaccess file:

RewriteCond %{HTTPS} off
RewriteRule ^wordpress/wp-(login.php|admin/)(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]

This code checks whether we are going to login or admin pages. If we are and https is not protocol of our choice, it will simply rewrite request.

P.S. This example was taken from my pages where I have WordPress installed in sub-directory named wordpress) If you have it installed in some other directory, adjust accordingly.

P.P.S. Do not forget to set FORCE_SSL_LOGIN and FORCE_SSL_ADMIN also.

WordPress and Plain-text Password

If you ever did network capture of your WordPress login procedure you would have noticed that password is sent as plain text:

[plain] POST /wordpress/wp-login.php HTTP/1.1 log=admin&pwd=mysuperpass&wp-submit=Log+In&redirect_to=http%3A%2F%2Fwww.example.com%2Fwordpress%2Fwp-admin%2Fedit.php%3Fpost_status%3Ddraft%26post_type%3Dpost&testcookie=1 [/plain] Anyone that can watch traffic on your network can see it as clear as day. Worse still, if you traverse some proxy server (in most of companies), your password might get dumped into a log file. Whoever has access to those log files has access to your password. Do you really trust your network admin that much?

Proper way to sort this out would be to use SSL. That way connection is encrypted end-to-end and whole plain-text issue just goes away. Unfortunately, SSL certificates usually cost some money.

Cheaper solution would be to tunnel all your traffic through SSH on trusted network (e.g. your system at home). While this would alleviate thread when you are connecting from e.g. hotel, it still means that your plain-text is traveling to server unencrypted. However, in case of SSH tunnel from home, you can count (or can you?) on your provider not keeping such a detailed log.

Last thing that you might do is to force WordPress to use CHAP protocol. That way password is still visible but only in hashed form. Brute-force attacks will be possible but at least attacker has something to do. And, if password was selected carefully, it might require months and even years of computing. Only way I found to do this is by installing Chap Secure Login plugin.

Upon activating this plugin you should log-out and log-in again (that will fail). And then log-out and log-in again (in my case this was still sending plain-text). Only upon third logout/login plugin will start working properly. Snooping system again you will see something like:

POST /wordpress/wp-login.php HTTP/1.1
  log=admin&amp;pwd=0a1dbb73659c24dd237ec254022af7daef410404665cc7f4be22b69e1e2b1845&amp;wp-submit=Log+In&amp;redirect_to=http%3A%2F%2Fwww.example.com%2Fwordpress%2Fwp-admin%2Fedit.php%3Fpost_status%3Ddraft%26post_type%3Dpost&amp;testcookie=1

This looks much better.

[2014-10-29: I had this plugin fail with Suffusion. After its (manual) removal I was unable to log on anymore. Solution was to remove cookies.]