How to Regenerate Thumbnails in WordPress

Posted October 14th, 2013 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 »

Include Taxonomy Terms in WordPress get_posts() for Efficient Lookups

Posted October 9th, 2013 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_SIZE;
 
	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 »

Getting Bootstrap 3 NavBar Working with WordPress wp_nav_menu()

Posted October 7th, 2013 (Updated 9 Oct 2013) 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 »

A More Efficient (UTF-8) curl_multi()

Posted August 20th, 2013 in PHP

If you need to do multiple CURL requests with PHP, curl_multi is a great way to do it. The problem with curl_multi is that it waits for all requests to complete before it begins processing any of the responses – for example if you’re making 100 requests and just one is slow, the other 99 will be held off for processing until the one has come back. This is wasteful so I’ve whipped up a script originally written by Josh Fraser with my own modifications and improvements that will begin processing a response immediately before dispatching the next request.
 

Usage:

The $callback (second) argument of rolling_url() can take any callback supported by PHP.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
curl_multi_download(array(
	"http://translink.com.au/travel-information/services-and-timetables/trains/rss",
	"http://translink.com.au/travel-information/services-and-timetables/buses/rss",
	"http://arandombrokenurl.com/should/give/an/error",
	"http://translink.com.au/travel-information/services-and-timetables/ferries/rss",
), 'process_response');
 
function process_response( $info, $response ) 
{
	if ( $info['http_code'] != 200 )
	{
		echo "Error retrieving URL " . $info['url'] . "<br/>";
		return;
	};
	var_dump($info);
	var_dump($response);
}

 

Bonus Round

Here’s a curl_download() function, used to grab the response from a single URL (For extra points switch out curl_exec() with curl_exec_utf8() using the same method as above):

1
2
3
4
5
6
7
8
9
10
11
function curl_download( $url )
{
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $url);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
	$data = self::curl_exec($ch);
	curl_close($ch);
 
	return $data;
}

See inside for the full code.

Read More »

Secure WordPress – Limit Logins to Specified Countries with CloudFlare

Posted April 9th, 2013 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
    
    // Limit login countries - requires cloudflare
    add_action( 'wp_authenticate', 'login_failed', 1);
    function login_failed()
    {
    	if ( !isset($_SERVER["HTTP_CF_IPCOUNTRY"]) )
    		return;
     
    	if ( !in_array($_SERVER["HTTP_CF_IPCOUNTRY"], array('AU')) )	{
    		wp_redirect( home_url() );
    		exit;
    	}
    }

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 »

How to Drastically Speed Up WordPress with Redis

Posted March 16th, 2013 (Updated 25 Mar 2013) 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 »

Edit WordPress Posts from the Front End

Posted March 5th, 2013 (Updated 13 Mar 2013) 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 »

Adding Custom Post Types, Fields and Taxonomies in WordPress

Posted March 5th, 2013 (Updated 13 Mar 2013) 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
<?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;
 
  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 »

How to Load WordPress Multi-Site in a script

Posted February 12th, 2013 (Updated 14 Mar 2013) in PHP

This morning I was loading WPMU in a custom script and all get_option() lookups were returning the details for the main blog despite being in a switch_to_blog() block. After a bit of debugging it turns out this was due to get_option() caching its values from the main blog and never expiring its cache when switching. There’s a pretty easy action you can use to fix this:

1
2
3
4
5
6
7
8
function switch_to_blog_cache_clear( $blog_id, $prev_blog_id = 0 ) {
    if ( $blog_id === $prev_blog_id )
        return;
 
	wp_cache_delete( 'notoptions', 'options' );
	wp_cache_delete( 'alloptions', 'options' );
}
add_action( 'switch_blog', 'switch_to_blog_cache_clear', 10, 2 );

Below is a script in its entirety for loading WPMU, switching to blog 31 and grabbing its name:

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
<?php
 
$_SERVER['HTTP_HOST'] = 'mypolicedev.com';
 
ini_set('display_errors', true);
 
/**
 * BEGIN LOAD WORDPRESS
 */
function find_wordpress_base_path() {
	$dir = dirname(__FILE__);
	do {
		//it is possible to check for other files here
		if( file_exists($dir."/wp-config.php") ) {
			return $dir;
		}
	} while( $dir = realpath("$dir/..") );
	return null;
}
 
define( 'BASE_PATH', find_wordpress_base_path()."/" );
global $wp, $wp_query, $wp_the_query, $wp_rewrite, $wp_did_header;
require(BASE_PATH . 'wp-load.php');
 
function switch_to_blog_cache_clear( $blog_id, $prev_blog_id = 0 ) {
	if ( $blog_id === $prev_blog_id )
		return;
 
	wp_cache_delete( 'notoptions', 'options' );
	wp_cache_delete( 'alloptions', 'options' );
}
add_action( 'switch_blog', 'switch_to_blog_cache_clear', 10, 2 );
 
/**
 * END LOAD WORDPRESS
 */
 
switch_to_blog( 31 );
var_dump(get_bloginfo('name'));
restore_current_blog();

Read More »

How to move WPMU domains

Posted May 31st, 2012 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 »

Page 2 of 712345...Last »