Posted in PHP

You may occasionally want to regenerate image thumbnails in WordPress (such as after changing image quality settings). Here is a handy function that regenerates all thumbnails for a given attachment post

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function regenerate_attachment_images( $attachment_id )
{
	// We only want to look at image attachments
	if ( !wp_attachment_is_image($attachment_id) )
		return;
 
	$filepath = get_attached_file( $attachment_id, true );
	$metadata = wp_generate_attachment_metadata( $attachment_id, $filepath );
 
	// Was there an error?
	if ( is_wp_error( $metadata ) )
		$this->die_json_error_msg( $attachment_id, $metadata->get_error_message() );
	if ( empty( $metadata ) )
		$this->die_json_error_msg( $attachment_id, 'Unknown failure reason.' );
 
	// If this fails, then it just means that nothing was changed (old value == new value)
	wp_update_attachment_metadata( $attachment_id, $metadata );
}

The above was taken mostly from the Regenerate Thumbnails plugin with a few modifications of my own.

Example Usage

To regenerate all thumbnails for all images use the following (Warning: This could take a while if you have alot of images and/or image sizes):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$images = new WP_Query(array(
	'post_type' => 'attachment',
	'posts_per_page' => -1,
	'post_mime_type' => 'image',
	'suppress_filters' => false,
 
	'offset' => 0,
	'post_status' => 'inherit',
	'ignore_sticky_posts' => true,
	'no_found_rows' => true,
));
 
while ( $images->have_posts() ): 
	$images->next_post();
	regenerate_attachment_images( $images->post->ID );
endwhile;

Read More »

Posted (Updated ) in Database, PHP

Sometimes in WordPress you want to include associated taxonomy terms with your get_posts() or WP_Query lookups. Doing so can have a noticeable impact on performance. Not to mention it’s much cleaner code-wise.

Here’s an example. I wanted to group image attachments into genre – action, adventure etc. and display that information on my sites frontend. Firstly I added my genre taxonomy to the attachment post type:

1
2
3
4
5
6
7
8
9
register_taxonomy('genre', 'attachment', array(
	'label' => 'Genres',
	'rewrite' => array( 'slug' => 'genre' ),
	'hierarchical' => true,
	'capabilities' => array(
		'assign_terms' => 'edit_posts',
		'edit_terms' => 'publish_posts'
	)
));

I now needed to display that information on the images associated post page (single.php) on the frontend.

 

The dumb way

On my first attempt I looped through the images, grabbing the associated genres and displaying them:

1
2
3
4
5
6
7
8
9
10
$images = get_posts(array(
	'post_parent' => get_the_ID(),
	'post_type' => 'attachment',
	'numberposts' => -1,
	'orderby'        => 'title',
	'order'           => 'ASC',
	'post_mime_type' => 'image',
));
foreach ( $images as $image )
	echo $image->post_title . ': ' . strip_tags(get_the_term_list($image->ID, 'genre', '', ', ', ''));

My image: Action, Adventure

This resulted in one unnecessary database call per image which could add up quickly. I needed a better way.

 

A smarter approach

WP_Query (which get_posts() uses to retrieve its results) supports a filter posts_clauses that lets you modify various parts of the SQL query it is about to perform. I used this to JOIN the taxonomy tables on and include the genre name(s) in the result array.

Firstly the filter (only works if you drop it in functions.php):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * Include 'size' name in image attachment lookups. This only applies if
 * INCLUDE_GENRES global variable flag is set - otherwise it will affect
 * the_loop
 *
 * @param array $pieces Includes where, groupby, join, orderby, distinct, fields, limits
 *
 * @return array $pieces
 */
add_filter( 'posts_clauses', function( $pieces )
{
	global $wpdb, $INCLUDE_GENRES;
 
	if ( empty($INCLUDE_GENRES) )
		return $pieces;
 
	$pieces['join'] .= " LEFT JOIN $wpdb->term_relationships iqctr ON iqctr.object_id=$wpdb->posts.ID
						 LEFT JOIN $wpdb->term_taxonomy iqctt ON iqctt.term_taxonomy_id=iqctr.term_taxonomy_id AND iqctt.taxonomy='genre'
						 LEFT JOIN $wpdb->terms iqct ON iqct.term_id=iqctt.term_id";
	$pieces['fields'] .= ",GROUP_CONCAT(iqct.name SEPARATOR ', ') AS genres";
 
	return $pieces;
}, 10, 1 );

You’ll notice the $INCLUDE_GENRES variable. This is required because without it the filter will apply to all the_loop and other queries. We only want it to apply for one specific query. Now how to use it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$INCLUDE_GENRES = true;
$images = get_posts(array(
	'post_parent' => get_the_ID(),
	'post_type' => 'attachment',
	'numberposts' => -1,
	'orderby'        => 'title',
	'order'           => 'ASC',
	'post_mime_type' => 'image',
	'suppress_filters' => false,
));
$INCLUDE_GENRES = false;
 
foreach ( $images as $image )
	echo $image->post_title . ': ' . $image->genres;

My image: Action, Adventure

Perfect!

Read More »

Posted (Updated ) in PHP

Bootstrap 3’s NavBar component has a convoluted markup that makes it difficult to integrate into WordPress’s wp_nav_menu() function but with the help of a custom Walker and a filter it’s quite possible to get happening.

 

Target Markup

We’re aiming to replicate the NavBar markup from the BS3 documentation. Here it is in full. Specifically wp_nav_menu() will cover the highlighted section.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<nav class="navbar navbar-default" role="navigation">
	<!-- Brand and toggle get grouped for better mobile display -->
	<div class="navbar-header">
		<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
			<span class="sr-only">Toggle navigation</span>
			<span class="icon-bar"></span>
			<span class="icon-bar"></span>
			<span class="icon-bar"></span>
		</button>
		<a class="navbar-brand" href="#">Brand</a>
	</div>
 
	<!-- Collect the nav links, forms, and other content for toggling -->
	<div class="collapse navbar-collapse navbar-ex1-collapse">
		<ul class="nav navbar-nav">			<li class="active"><a href="#">Link</a></li>			<li><a href="#">Link</a></li>			<li class="dropdown">				<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a>				<ul class="dropdown-menu">					<li><a href="#">Action</a></li>					<li><a href="#">Another action</a></li>					<li><a href="#">Something else here</a></li>					<li><a href="#">Separated link</a></li>					<li><a href="#">One more separated link</a></li>				</ul>			</li>		</ul>	</div><!-- /.navbar-collapse -->
</nav>

Read on for full details!

Read More »

Posted (Updated ) in PHP

As I’m sure is the case with just about every WordPress user, my site is constantly hit with failed login attempts from bots – usually originating from other countries. I had the idea last night to implement country-based login restrictions using CloudFlare’s IP Geolocation server variable.

My goal was to redirect redirect all users visiting the login page from countries other than Australia to the home page. This occurs before a login attempt can even be made.

Here’s how it’s done:

  • Go into CloudFlare settings and make sure the IP Geolocation option is turned onMake sure CloudFlare IP Geolocation is turned on
  • Add the following to your active themes functions.php file
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    // Limit login countries - requires cloudflare
    add_filter( 'authenticate', 'login_failed', 1, 3);
    function login_failed( $user, $username, $password )
    {
        if ( !isset( $_SERVER['HTTP_CF_IPCOUNTRY'] ) )
            return $user;
     
        if ( !in_array( $_SERVER['HTTP_CF_IPCOUNTRY'], array('AU') ) )    {
            wp_redirect( home_url() );
            exit;
        }
     
        return $user;
    }

To test simply use a country other than your own. Once it’s confirmed working switch to your country and you’re good to go!

Read More »

Posted (Updated ) in Database, PHP

I recently came across a tutorial on sitting Redis infront of WordPress allowing for insanely fast page generation. I gave it a try and it really works, in fact I’m now using it on this very site! The best part however is the fact that the script requires absolutely no modification to your existing WordPress site save for 1 line of htaccess. Truly amazing.

Below I’ll detail my slightly modified version of Jim’s script along with some metrics.

 

Firstly, What is Redis and what will it do for me?

The Redis website describes Redis as

… an open source, BSD licensed, advanced key-value store. It is often referred to as a data structure server since keys can contain stringshasheslistssets and sorted sets.

What does this mean? Essentially it’s Memcached but more useful. Redis stores key-value pairs in memory and spits them out when requested. Unlike Memcached it has built in persistence but what’s most important to us is that it’s fast – very fast.

We’ll be using Redis to speed up our site by loading cached pages from it directly without even booting up WordPress. This will save a large amount of page generation time and get out site infront of our users’ eyeballs faster.

 

Exactly how much faster are we talking?

In my very unscientific tests, loading www.flynsarmy.com a bunch of times resulted in the following:

Before (Secs) After (Secs)
1.556
0.468
0.494
0.498
0.492
0.514
0.499
0.511
0.499
0.02001
0.00896
0.00883
0.00959
0.01472
0.00916
0.00915
0.00756
0.01989

As you can see from the table above this equates to a 20x to 50x speed increase and that was WITH W3 Total Cache installed! Results of course may vary but I think you get the picture.

Read More »

Posted (Updated ) in PHP

I have a project coming up involving editing WordPress posts from the front end of the site. There are a bunch of plugins that let you do this the coolest of which seems to be Front-end Editor but I wanted to come up with my own solution. Luckily it turned out to be surprisingly quick and painless!

For this tutorial I’ll be editing the cafe custom post type from my last post.

Read More »

Posted (Updated ) in PHP

This information is pretty readily available on the internet but I figured I’d make my own post for safe keeping. I’ll be adding a custom post type (Cafes) with 3 custom fields (Website, Address and Phone) and a custom taxonomy (Countries).

The following code all goes in your functions.php file.

 

Custom Post Types

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
 * Add Cafe post type
 */
function create_cafe_post_type() {
	register_post_type( 'cafes',
		array(
			'labels' => array(
				'name' => 'Cafes',
				'singular_name' => 'Cafe',
				'add_new' => 'Add New',
				'add_new_item' => 'Add New Cafe',
				'edit_item' => 'Edit Cafe',
				'new_item' => 'New Cafe',
				'view_item' => 'View Cafe',
				'search_items' => 'Search Cafes',
				'not_found' =>  'Nothing Found',
				'not_found_in_trash' => 'Nothing found in the Trash',
				'parent_item_colon' => ''
			),
			'public' => true,
			'publicly_queryable' => true,
			'show_ui' => true,
			'query_var' => true,
			//'menu_icon' => get_stylesheet_directory_uri() . '/yourimage.png',
			'rewrite' => true,
			'capability_type' => 'post',
			'hierarchical' => false,
			'menu_position' => null,
			'supports' => array('title','editor','thumbnail')
		)
	);
}
add_action( 'init', 'create_cafe_post_type' );

 

Custom Taxonomies

Taxonomies are basically tags for your post types. Add the following below the register_post_type call above:

1
2
3
4
5
6
7
//Cafe countries taxonomy
register_taxonomy("countries", array("cafes"), array(
	"hierarchical" => false,
	"label" => "Countries",
	"singular_label" => "Country",
	"rewrite" => true
));

 

Custom Fields

This is the hardest of the lot. It’s not really hard though, just alot of HTML to make the meta boxes. You can put any HTML you like in meta boxes, I just like to keep mine looking like standard WP fields:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php
/**
 * Add cafe custom fields
 */
function add_cafe_meta_boxes() {
	add_meta_box("cafe_contact_meta", "Contact Details", "add_contact_details_cafe_meta_box", "cafes", "normal", "low");
}
function add_contact_details_cafe_meta_box()
{
	global $post;
	$custom = get_post_custom( $post->ID );
 
	?>
	<style>.width99 {width:99%;}</style>
	<p>
		<label>Address:</label><br />
		<textarea rows="5" name="address" class="width99"><?= @$custom["address"][0] ?></textarea>
	</p>
	<p>
		<label>Website:</label><br />
		<input type="text" name="website" value="<?= @$custom["website"][0] ?>" class="width99" />
	</p>
	<p>
		<label>Phone:</label><br />
		<input type="text" name="phone" value="<?= @$custom["phone"][0] ?>" class="width99" />
	</p>
	<?php
}
/**
 * Save custom field data when creating/updating posts
 */
function save_cafe_custom_fields(){
  global $post;
 
  if ( $post )
  {
    update_post_meta($post->ID, "address", @$_POST["address"]);
    update_post_meta($post->ID, "website", @$_POST["website"]);
    update_post_meta($post->ID, "phone", @$_POST["phone"]);
  }
}
add_action( 'admin_init', 'add_cafe_meta_boxes' );
add_action( 'save_post', 'save_cafe_custom_fields' );

 

Custom Templates

You may want your new post type to look different from standard posts. This can be done using custom templates. The WordPress codex cover custom post type templates nicely but I’ll quickly go over what I did for completeness. I’m using twentytwelve theme in my demo but this should apply for most themes.

  1. Duplicate single.php into single-<post type>.php
  2. Replace
    <?php get_template_part( 'content', get_post_format() ); ?>

    with

    <?php get_template_part( 'content', '<post type>' ); ?>
  3. Duplicate content.php into content-<post type>.php and modify however you like

 

Conclusion

And we’re done! Always remember to check the WordPress documentation for the various functions involved if you want to customize. Lots of useful information in there.

Read More »

Posted in Database, PHP

Here’s a very quick set of SQL snippets for updating a WPMU domain. This might be useful when building a site on a development domain before moving to a production one later on.

Firstly update the wp_blogs table:

1
UPDATE wp_blogs SET domain='newdomain.com';

There will be a bunch of wp_options and wp_posts tables – one per site. Find all the tables:

1
2
SHOW TABLES LIKE "%_options";
SHOW TABLES LIKE "%_posts";

and for each table, perform the following query:

1
2
3
4
#_options tables
UPDATE <tablename> SET option_value=REPLACE(option_value, 'http://olddomain.com', 'http://newdomain.com');
#_posts tables
UPDATE <tablename> SET post_content=REPLACE(post_content, 'http://olddomain.com', 'http://newdomain.com');

This was enough to get the sites working for me. Additional tweaks are probably required afterwards – if you find anything let me know in the comments below.

Read More »

Posted (Updated ) in Database, Linux, PHP

Tonight I had to move my WPMU install from my local development machine to the live server – this meant a change in installation path which is always a hassle with WordPress. Below I’ll detail my issues and the corresponding fixes in the hopes it will make life easier for others experiencing the same problems.

Redirection to wp-signup.php

First thing I noticed was that when loading the site on a live domain, I’d get instantly redirected to mydomain.com/wp-signup.php?new=mydomain.com. With a bit of Googling I came across this forum thread which recommended adding the following to my wp-config.php:

1
2
define( 'NOBLOGREDIRECT', '' );
define( 'SUBDOMAIN_INSTALL', false );

Don’t do this! It will get the site closer to working order but it’s not going to help you in the long run – especially when you need the other domains working too. Instead here’s what you should be doing:

1
2
3
4
5
6
7
#Dump your DB
mysqldump - u<username> -p<password> -h<host> <dbname> > test.sql
 
#Update folder path to that of the new servers
sed -i "s/path\/to\/install/new\/path\/to\/install/g" test.sql 
#If you installed into localhost/foo/bar/mysite, change that to your live servers domain
sed -i "s/url\/path\/to\/site/www\.domain\.com/g" test.sql

This is pretty standard behavior for moving WordPress sites, however if you load the above dump up on your live domain you’ll probably be greeted with the dreaded Error establishing a database connection.

Error establishing a database connection

Heading to www.domain.com/wp-admin will shed a little more light on the situation – you need to update your wp_blogs table for the main site. WPMU is currently using your development servers URL from this table and ignoring what’s in wp-config.php. To play things safe we’ll update any occurrances of our test servers domain in test.sql:

1
2
#Update your live servers subdomain in wp_blogs to your live servers domain
sed -i  "s/yoursite\.localhost\.com/www\.yourdomain\.com/g" test.sql

This should do the trick. Load that bad boy into your live server and you should be good to go!

Read More »

Posted (Updated ) in PHP

Update 06 June 2011: I’ve contacted the current developer and he’s accepted the patch. It should be appearing in a WP-Syntax near you shortly!
Update 16 July 2011: I’ve merged Chimo’s excellent patch which adds page range support. See the updated tutorial below for usage instructions. Thanks Chimo!
Update 27 July 2011: We have liftoff! This patch is finally part of the official plugin!

I’ve used a few different WordPress syntax highlighters in my day but WP-Syntax is easily the best of them. One issue that’s bothered me with WP-Syntax for a while now, however, is lack of line highlighting support so I decided to look into adding it myself. At the time of writing I’m using WP-Syntax 0.9.9.

To add highlighting support, open up /wp-content/plugins/wp-syntax/wp-syntax.php. You’ll need to modify two functions – wp_syntax_highlight and wp_syntax_before_filter. Here’s wp_syntax_highlight:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
function wp_syntax_highlight($match)
{
    global $wp_syntax_matches;
 
    $i = intval($match[1]);
    $match = $wp_syntax_matches[$i];
 
    $language = strtolower(trim($match[1]));
    $line = trim($match[2]);
    $escaped = trim($match[3]);
 
    $code = wp_syntax_code_trim($match[5]);    if ($escaped == "true") $code = htmlspecialchars_decode($code);
 
    $geshi = new GeSHi($code, $language);
    $geshi->enable_keyword_links(false);
    do_action_ref_array('wp_syntax_init_geshi', array(&$geshi));
 
    //START LINE HIGHLIGHT SUPPORT    $highlight = array();    if ( !empty($match[4]) )    {        $highlight = strpos($match[4],',') == false ? array($match[4]) : explode(',', $match[4]); 	$h_lines = array();	for( $i=0; $i<sizeof($highlight); $i++ )	{		$h_range = explode('-', $highlight[$i]); 		if( sizeof($h_range) == 2 )			$h_lines = array_merge( $h_lines, range($h_range[0], $h_range[1]) );		else			array_push($h_lines, $highlight[$i]);	}         $geshi->highlight_lines_extra( $h_lines );    }    //END LINE HIGHLIGHT SUPPORT 
    $output = "\n<div class=\"wp_syntax\">";
 
    if ($line)
    {
        $output .= "<table><tr><td class=\"line_numbers\">";
        $output .= wp_syntax_line_numbers($code, $line);
        $output .= "</td><td class=\"code\">";
        $output .= $geshi->parse_code();
        $output .= "</td></tr></table>";
    }
    else
    {
        $output .= "<div class=\"code\">";
        $output .= $geshi->parse_code();
        $output .= "</div>";
    }
    return
 
    $output .= "</div>\n";
 
    return $output;
}

and here’s wp_syntax_before_filter:

1
2
3
4
5
6
7
8
function wp_syntax_before_filter($content)
{
    return preg_replace_callback(
        "/\s*<pre(?:lang=[\"']([\w-]+)[\"']|line=[\"'](\d*)[\"']|escaped=[\"'](true|false)?[\"']|highlight=[\"']((?:\d+[,-])*\d+)[\"']|\s)+>(.*)<\/pre>\s*/siU",        "wp_syntax_substitute",
        $content
    );
}

Updated/added lines are, of course, highlighted 🙂

Below is an example of the new functionality:

<pre lang=”php” highlight=”2″>
$foo = ‘foo’;
$bar = ‘bar’;
$foobar = ‘foobar’;
</pre>

$foo = 'foo';
    $bar = 'bar';    $foobar = 'foobar';

<pre lang=”php” highlight=”1,2,5-7″>
$foo = ‘foo’;
$bar = ‘bar’;
$foobar = ‘foobar’;

$foo = ‘foo’;
$bar = ‘bar’;
$foobar = ‘foobar’;

$foo = ‘foo’;
$bar = ‘bar’;
$foobar = ‘foobar’;
</pre>

$foo = 'foo';    $bar = 'bar';    $foobar = 'foobar';
 
    $foo = 'foo';    $bar = 'bar';    $foobar = 'foobar'; 
    $foo = 'foo';
    $bar = 'bar';
    $foobar = 'foobar';

Remember to add highlight to the list of allowed <PRE> tag arguments in your themes functions.php file. Details on doing so can be found here.

Read More »