WordPress 2.9: a lead to manage thumbnails for old posts

Before version 2.9, the bigger majority of us using custom fields to manage the thumbnails associated with posts. Based on a plugin, or a small development, this management was relatively simple. With version 2.9, WordPress can now attach a thumbnail to each post or page. So, users will have a choice: adopt the new management, or keep the current one. Both cases will be difficult to manage in the future. But is it possible to convert old posts and thumbnails to the new system?

  • Switching to the new method doesn’t involve major changes: in the theme, few lines to add to functions.php, and index.php and single.php, and some other files. The problem is that the functionality of WordPress will apply only to new posts. Manage older posts involve keeping the plugin or customization developed previously. This means that we will have to administer the required tools to both modes of management,
  • Keeping the current method doesn’t involve any change.

In both cases, with the future WordPress evolutions, management of old posts will become increasingly problematic. When we will run the version 3.x or 4, it will certainly be difficult or heavy to continue to manage both. Plugins that manage thumbnails today, will be gradually no longer supported.

The ideal would be to convert the posts in order to have only one system to manage. I propose to explore possible solutions to achieve this conversion.

Don’t try the following PHP code directly in your blog. Scripts must be tested several times in a development platform, before be used in production.

Required tools

What do we need to make this conversion?
First, we must read the existing data. We need to know

  • The name of custom field (meta_key) used,
  • The WordPress API to extract values of custom fields,
  • The format of the image path (absolute path from the root of the blog? Relative path? From where?

Then we must enter correct information into the database, for each image found in the previous step:

  • If the image is not yet an attachment, it must be declared as attachment,
  • and be associated to the relevant post.

Getting existing data

We assume that we managed thumbnails by storing their address in a custom field (named logo). The easiest way to list all the images associated with posts, is to use a SQL query:

global $wpdb; 
$meta_key = 'logo';
$sql = $wpdb->prepare('SELECT meta_id, post_id, meta_value FROM '.$wpdb->postmeta.', '.$wpdb->posts.' WHERE meta_key="%s" AND ID=post_id ORDER BY ID', $meta_key);
$thumbnails_values = $wpdb->get_results($sql, ARRAY_N);

This query gives us all images associated with posts.

Insert thumbnails in the WP 2.9 system

With WP 2.9, a thumbnails is an attachment. So if it is not already the case, the images found with the previous query, must be declared as attachments.
After this step, we just have to declare the thumbnails with the custom field _thumbnail_id.

The least risky way to add an attachment, is to use the wp_insert_attachment. We have also to generate the metadata of the thumbnail with the wp_update_attachment_metadata.

Adding custom field can be performed with the function with add_post_meta.

Difficulties

The conversion doesn’t seem too complicated. It will be difficult to get a plugin or function applicable in all cases. The two possible problems:

  • Detect how coded address of the thumbnail, and how to convert absolute path,
  • Generate, if necessary, an image of the right size.

The code required for the conversion could be:

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
global $wpdb;
 
// Getting current thumbnails path
$meta_key = 'logo';
$sql = $wpdb->prepare('SELECT post_title, meta_id, post_id, meta_value FROM '.$wpdb->postmeta.', '.$wpdb->posts.' WHERE meta_key="%s" AND ID=post_id ORDER BY ID', $meta_key);
$logo_values = $wpdb->get_results($sql, ARRAY_N);
 
// Getting the current setting for upload path
$upload_path = get_option('upload_path');
 
// Foreach current thumbnails
foreach ($logo_values as $values) {
	list($post_title, $meta_id, $post_id, $meta_value) = $values;
 
	// Building the attachment object
	$path_parts = pathinfo($meta_value);
	$attachment = array( 'post_mime_type' 	=> 'image/'.$path_parts['extension'],
				 'guid'		=> $SITE_URL.'/'.$meta_value,
				 'post_parent'      => $post_id,
				 'post_title'		=> $path_parts['filename'],
				 'post_content'	=> '');
	// Managing path format
	$new_path = str_replace($upload_path.'/','', $meta_value);
 
	// Adding the attachment
	$attachment_id = wp_insert_attachment($attachment, $new_path);
 
	// Adding metadata of the attachment
	wp_update_attachment_metadata($attachment_id, wp_generate_attachment_metadata( $attachment_id, path_join(ABSPATH, $meta_value)));
 
	// Add custom field to declare the attachment as a thumbnail
	add_post_meta($post_id, '_thumbnail_id', $attachment_id, TRUE);
 
	// Deleting the old entry
	delete_post_meta( $post_id, $meta_key );
}

In my case, I used a home made customization, using a custom field logo to store the path of the image taken as thumbnail. The path is relative to the root of the blog. Images were stored in the same tree as attachments, but are not attachments, they were not present in the database.

Remaining actions

The previous function works well in my case. It remains however some few problems:

  • We don’t test if image already exists in te database or not,
  • In some cases, the size of the image does not match the size of the thumbnail: we should therefore generate a thumbnail of the right size. This can be done separately, with a plugin like Regenerate Thumbnails.
  • In posts containing the shortcode Gallery, the images we just added will appear, which will not wanted, nor esthetic. In my case, for example, the thumbnail used are small (100×100).

With WordPress 2.9, the shortcode Gallery provides two new arguments: include and exclude. The idea could be

  • identify posts containing the shortcode,
  • for each post, add the argument exclude, specifying the ID of the thumbnail.

The function could take the form:

1
2
3
4
5
6
7
8
9
10
global $wpdb;
 
$posts_list = $wpdb->get_results('SELECT ID, post_content, meta_value FROM '.$wpdb->wp_posts.', '.$wpdb->wp_postmeta.' WHERE post_content LIKE "%[gallery%" AND meta_key = "_thumbnail_id" AND ID = post_id');
foreach ($posts_list as $values) {
	list($post_id, $post_title, $post_content, $meta_value) = $values;
	$search_pattern = "/(.*?)[gallery(.*?)](.*?)/";
	$replace_pattern = '$1[gallery$2 exclude='.$meta_value.']$3';
	$new_post_content = preg_replace ( $search_pattern , $replace_pattern , $post_content);
	$wpdb->query('UPDATE '.$wpdb->posts.' SET post_content="'.$new_post_content.'" WHERE ID='.$post_id);
}

Conclusion

Here are some ways to use the new system with the old posts. These codes are still in draft, but provide a good working basis.
The complexity comes in fact not from the conversion itself, but from the starting point: Which convention is used to specify thumbnails? How path are written? Are only two questions among many others.
I think I will study further the conversion, to find any other hidden consequences, before run this code in my own blog.
Hopefully soon plugins will appear allowing us to achieve the conversions automatically.