My talk from WordCamp Kathmandu entitled “Coding With Jetpack” is now available on WordPress.TV. I hope you enjoy 🙂
[videopress xQwbucAg]
My talk from WordCamp Kathmandu entitled “Coding With Jetpack” is now available on WordPress.TV. I hope you enjoy 🙂
[videopress xQwbucAg]
Today, I open sourced my Twenty Sixteen child theme. It includes an empty resume page template and a clean travel shortcode that uses Google Maps. You’ll need to check out the source code for usage, but it’s very simple 🙂
This child theme is meant to be only very small improvements to the WordPress Twenty Sixteen default theme, but they were improvements that I felt were very valuable.
View the code:Â https://github.com/rcoll/twentysixteen-rcollier
Pull requests are welcome.
I regularly work with huge MySQL database files (10G-100G) so making copies and importing them locally for use in VVV is extremely cumbersome (at best). Here are some MySQL statements that allow you to truncate all but the most recent 1000 posts in your WordPress database. These are useful because they allow you to truncate the database to a manageable size before copying to your local environment or development server. Unless you have something unusual, these statements should retain all post-to-postmeta associations, comment-to-post associations, and term-to-post associations.
WARNING: DO NOT run these on your production database. You must make a copy of the whole database first and then run these statements on the copy. You have been warned!
Delete all but the most recent 1000 posts:
DELETE FROM wp_posts WHERE ID NOT IN ( SELECT ID FROM ( SELECT ID FROM wp_posts WHERE post_type="post" AND post_status="publish" ORDER BY ID DESC LIMIT 1000 ) foo );
Delete all post meta that is not associated with the remaining posts:
DELETE FROM wp_postmeta WHERE post_id NOT IN ( SELECT ID FROM ( SELECT ID FROM wp_posts ) foo );
Delete all comments that are not associated with a remaining post:
DELETE FROM wp_comments WHERE comment_post_ID NOT IN ( SELECT ID FROM ( SELECT ID FROM wp_posts ) foo );
Delete all comment meta that is not associated with a remaining post:
DELETE FROM wp_commentmeta WHERE comment_ID NOT IN ( SELECT comment_ID FROM ( SELECT comment_ID FROM wp_comments ) foo );
Delete term relationships that don’t associate to a remaining post:
DELETE FROM wp_term_relationships WHERE object_id NOT IN ( SELECT ID FROM ( SELECT ID FROM wp_posts ) foo );
Delete terms that are no longer related to an existing post:
DELETE FROM wp_terms WHERE term_id NOT IN ( SELECT term_taxonomy_id FROM ( SELECT term_taxonomy_id FROM wp_term_relationships ) foo );
As an added bonus, delete transients from the options table:
DELETE FROM wp_options WHERE option_name like '%_transient_%';
And here’s the whole thing in one shot – script it in if you wish:
DELETE FROM wp_posts WHERE ID NOT IN ( SELECT ID FROM ( SELECT ID FROM wp_posts WHERE post_type="post" AND post_status="publish" ORDER BY ID DESC LIMIT 1000 ) foo ); DELETE FROM wp_postmeta WHERE post_id NOT IN ( SELECT ID FROM ( SELECT ID FROM wp_posts ) foo ); DELETE FROM wp_comments WHERE comment_post_ID NOT IN ( SELECT ID FROM ( SELECT ID FROM wp_posts ) foo ); DELETE FROM wp_commentmeta WHERE comment_ID NOT IN ( SELECT comment_ID FROM ( SELECT comment_ID FROM wp_comments ) foo ); DELETE FROM wp_term_relationships WHERE object_id NOT IN ( SELECT ID FROM ( SELECT ID FROM wp_posts ) foo ); DELETE FROM wp_terms WHERE term_id NOT IN ( SELECT term_taxonomy_id FROM ( SELECT term_taxonomy_id FROM wp_term_relationships ) foo ); DELETE FROM wp_options WHERE option_name like '%_transient_%';
A few people have asked how I get the WordPress featured image for my YouTube posts. It’s actually quite simple – YouTube provides a thumbnail for each video you publish (in the form of http://img.youtube.com/vi/{video_id}/0.jpg) that is free for the taking.
Here is a little script that detects a YouTube embed and automatically downloads, imports, and assigns your YouTube thumbnail as the posts’ featured image. Remove lines 3 and 4 if you’re not using post formats.
function rdc_youtube_video_post( $post_id ) { // No need to continue if it's not a video post if ( 'video' != get_post_format( $post_id ) ) return false; // No need to continue if we already have a post thumbnail if ( has_post_thumbnail( $post_id ) ) return false; // Get the post content $the_post = get_post( $post_id ); $the_content = $the_post->post_content; // No need to continue if we haven't inserted a youtube embed if ( false === strpos( $the_content, 'www.youtube.com/embed/' ) ) return false; // Extract the youtube ID $matches = array(); preg_match_all( '/www.youtube.com/embed/(.*?)(?|")/i', $the_content, $matches ); $yt_id = $matches[1][0]; // Bail if we don't have a valid Youtube ID if ( ! $yt_id || empty( $yt_id ) ) return false; // Get the URL of the YouTube thumbnail $yt_image_url = 'http://img.youtube.com/vi/' . $yt_id . '/0.jpg'; // Set up for importing and grab the remote image $upload_dir = wp_upload_dir(); $image_contents = file_get_contents( $yt_image_url ); $filename = sanitize_file_name( basename( $yt_id . '.jpg' ) ); // Create the upload directory if we must if ( wp_mkdir_p( $upload_dir['path'] ) ) $file = trailingslashit( $upload_dir['path'] ) . $filename; else $file = trailingslashit( $upload_dir['basedir'] ) . $filename; // Store the image file on the server file_put_contents( $file, $image_contents ); // Check the mime type of the file $mimetype = wp_check_filetype( $filename, null ); // Consolidate some details about the attachment $attachment = array( 'post_mime_type' => $mimetype['type'], 'post_title' => $filename, 'post_content' => '', 'post_status' => 'inherit', ); // Insert the attachment into WordPress $attach_id = wp_insert_attachment( $attachment, $file, $post_id ); // We need some additional functions require_once( ABSPATH . 'wp-admin/includes/image.php' ); // Generate and save the attachment metadata $attach_data = wp_generate_attachment_metadata( $attach_id, $file ); wp_update_attachment_metadata( $attach_id, $attach_data ); // Finally, set the post thumbnail to use our youtube image set_post_thumbnail( $post_id, $attach_id ); } add_action( 'save_post', 'rdc_youtube_video_post' );
I decided to get with the times and give Jetpack a try. If you’re unfamiliar with Jetpack, it’s essentially a WordPress plugin which ties your self-hosted WordPress blog to WordPress.com’s servers and allows nifty features like an image CDN, Gravatar hovercards, centralized comments, and a multitude of other features. Personally, I love the WordPress.com stats the most.
I missed my views column on my posts page in admin though (which I used to source from my Better Postviews plugin). So I wrote up this little function to get the data from Jetpack servers and happily place it where it belongs. Here is the code to do so:
<?php // Add a Views columns to insert jetpack postviews data into function rdc_add_views_column( $cols ) { $cols['pageviews'] = 'Views'; return $cols; } add_filter( 'manage_edit-post_columns', 'rdc_add_views_column' ); // Grab and display the postviews data from Jetpack for each post function rdc_add_views_col_data( $colname ) { global $post; // Make sure we're inserting into the correct column if ( 'pageviews' !== $colname ) return false; // Make sure jetpack and stats are available if ( ! ( class_exists( 'Jetpack' ) && Jetpack::is_module_active( 'stats' ) ) ) { echo 'Error'; return false; } // Make sure stats_get_csv is available if ( ! function_exists( 'stats_get_csv' ) ) { echo 'Error'; return false; } // Try to get view count from post meta "cache" $view_count = get_post_meta( $post->ID, '_jetpack_post_views_count', true ); $view_count_created = absint( get_post_meta( $post->ID, '_jetpack_post_views_count_created', true ) ); // No "cache" value, hit the API for the value if ( ! $view_count || time() > $view_count_created + 3600 ) { // Get the post data from Jetpack $postviews = stats_get_csv( 'postviews', "post_id={$post->ID}" ); // We have a problem if there was no data returned if ( ! $postviews ) { echo 'Error'; return false; } // Get view count from returned results $view_count = absint( $postviews[0]['views'] ); // Store the value and time as post meta update_post_meta( $post->ID, '_jetpack_post_views_count', absint( $view_count ) ); update_post_meta( $post->ID, '_jetpack_post_views_count_created', absint( time() ) ); } // Print Jetpack post views echo '<strong>' . number_format( absint( $view_count ) ) . '</strong>'; } add_action( 'manage_posts_custom_column', 'rdc_add_views_col_data' );
Drop that code into your functions.php or another theme file. Also, if you’re REALLY scaling up, you’re probably using something like Varnish cache and the Better Postviews Plugin may not register an accurate view count. If that’s the case, Jetpack may be for you since WordPress.com stats are javascript based.
There are a ton of other metrics which you can pull from Jetpack also – check out George Stephanis’ slides from WordCamp Boston 2013 for some more info.
Enjoy!
It’s no secret that I love Graphite. My co-workers think it’s a bit ridiculous how much data I feed into our graphite server. I may be a bit excessive, but I have a monitor in my office which allows me to see at a glance every little detail about our network in real time – server health, pageviews, Google Pagespeed Insight scores, etc. I can’t even describe how wonderful a tool it is.
I wanted an easy way to display graphite graphs on the WordPress dashboard. So I rolled out a plugin over the weekend. I give you WordPress Graphite Graphs – I admit I [somewhat] got the idea from WPVIP’s “page generation time” graph in the VIP dashboard, but I’m sure they won’t mind. This plugin allows me (or you) to rapidly deploy monitoring graphs across many different sites without a lot of hassle and NO custom code for each site. Just plug in the URL of your graphite server, the metrics you want to display, and viola – Graphy delight! While I was at it, I went ahead and added support for graphs on the front-end in the sidebar that can be hidden from non-users if needed. Why not, right?
Shoot me a tweet if you’re interested in additional features or get in on the action and fork it on github.
This is a quick script which reads a standard RSS feed and then grabs the response header for each feed item and makes a log file of 404 errors. Useful in debugging sticky situations.
<?php $link_queue = array(); $exception_queue = array(); $timestamp = date( 'Y-m-d H:i:s' ); $rss = simplexml_load_file( $argv[1] ); foreach ( $rss->channel->item as $item ) array_push( $link_queue, $item->link ); foreach ( $link_queue as $url ) { $handle = curl_init( $url ); curl_setopt( $handle, CURLOPT_RETURNTRANSFER, TRUE ); $response = curl_exec( $handle ); $response_code = curl_getinfo( $handle, CURLINFO_HTTP_CODE ); if ( $response_code !== 200 ) array_push( $exception_queue, "{$url} returned status code {$response_code}" ); curl_close( $handle ); } if ( count( $exception_queue ) ) { foreach ( $exception_queue as $e ) echo $timestamp . ' > ' . $e . "n"; } else { echo $timestamp . " > All URLs returned status code 200n"; } die(); // omit
Throw it in cron like this:
*/30 * * * * php /path/to/script.php http://somesite.com/feed/ >> /path/to/log 2>&1
Or run it on the command line like this:
php /path/to/the/script.php http://somesite.com/feed/
Could be used for setting up dummy content for a staging environment, or something similar. Be my guest and let me know how you decide to use it in the comments! Also, check my github for this function built into a WP-CLI command.
$posts = new WP_Query( array( 'posts_per_page' => -1, 'post_type' => 'post', )); $post_ids = array(); while ( $posts->have_posts() ) { $posts->the_post(); array_push( $post_ids, get_the_ID() ); } wp_reset_query(); $attachments = new WP_Query( array( 'posts_per_page' => -1, 'post_type' => 'attachment', 'post_status' => 'inherit', )); $attachment_ids = array(); while ( $attachments->have_posts() ) { $attachments->the_post(); array_push( $attachment_ids, get_the_ID() ); } wp_reset_query(); foreach ( $post_ids as $post_id ) { $attachment_id = rand( 0, count( $attachment_ids ) - 1 ); echo 'post:' . $post_id . ' attachment:' . $attachment_ids[$attachment_id]; if ( update_post_meta( $post_id, '_thumbnail_id', $attachment_ids[$attachment_id] ) ) echo ' SUCCESS'; else echo ' FAILED'; } die( 'script complete' );
Sometimes you just need to get rid of all that trackback data from your WordPress database. Here’s how to do it manually from your MySQL command line:
DELETE FROM wp_comments WHERE comment_type='trackback'; DELETE FROM wp_comments WHERE comment_type='pingback';
Great, but now your comments counts are all out of whack, since that integer is stored in the wp_posts table. Here’s how to check that:
SELECT wpp.id, wpp.post_title, wpp.comment_count, wpc.cnt FROM wp_posts wpp LEFT JOIN ( SELECT comment_post_id AS c_post_id, count(*) AS cnt FROM wp_comments WHERE comment_approved = 1 GROUP BY comment_post_id) wpc ON wpp.id=wpc.c_post_id WHERE wpp.post_type IN ('post', 'page') AND (wpp.comment_count!=wpc.cnt OR (wpp.comment_count != 0 AND wpc.cnt IS NULL) );
And here’s how to fix it (make a backup of your database first, just in case):
UPDATE wp_posts wpp LEFT JOIN ( SELECT comment_post_id AS c_post_id, count(*) AS cnt FROM wp_comments WHERE comment_approved = 1 GROUP BY comment_post_id) wpc ON wpp.id=wpc.c_post_id SET wpp.comment_count=wpc.cnt WHERE wpp.post_type IN ('post', 'page') AND (wpp.comment_count!=wpc.cnt OR (wpp.comment_count != 0 AND wpc.cnt IS NULL) );
Cheers!
Here’s another object-oriented mu-plugin that you can expand upon if needed. This adds the option to perform a search with “&search__only_titles” or “&search__only_content” in the url to search only titles or content of posts.
<?php class rdc_search_object { function __construct() { add_filter( 'posts_search', 'filter__posts_search' ); } function filter__posts_search( $query ) { $query_parts = explode( ' OR ', $query ); if ( isset( $_GET['search__only_titles'] ) ) return $query_parts[0] . '))'; if ( isset( $_GET['search__only_content'] ) ) return ' AND ((' . $query_parts[1]; return $query; } } $rdc_search_object = new rdc_search_object(); // omit
Go ahead and use this juicy tidbit and let me know in the comments what creative use you found for it.