From 567a2c337c20400da3541bc3cd404ac1d876ca5f Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Sun, 13 Sep 2015 18:32:57 +1000 Subject: [PATCH 01/14] Get all attachments at once --- class-srcset-callback.php | 90 +++++++++++++++++++++++++ wp-tevko-responsive-images.php | 120 ++++++++++++++++++--------------- 2 files changed, 155 insertions(+), 55 deletions(-) create mode 100644 class-srcset-callback.php diff --git a/class-srcset-callback.php b/class-srcset-callback.php new file mode 100644 index 0000000..016283f --- /dev/null +++ b/class-srcset-callback.php @@ -0,0 +1,90 @@ +attachments_loop = $attachments_loop; + $upload_dir = wp_upload_dir(); + $upload_dir = $upload_dir['baseurl'] . '/'; + + while( $attachments_loop->have_posts() ) : + $attachments_loop->the_post(); + + // Get the image meta data. + if ( is_array( $img_meta = wp_get_attachment_metadata( get_the_ID() ) ) ) { + + // Get base URL. + $base_url = $upload_dir . pathinfo( $img_meta['file'], PATHINFO_DIRNAME ); + $base_url = untrailingslashit( $base_url ) . '/'; + + // Add default/full size image. + $this->attachments_array[ $upload_dir . $img_meta['file'] ] = get_the_ID(); + + // Build from sizes array. + $img_sizes = $img_meta['sizes']; + + foreach( $img_sizes as $img_size ) { + $this->attachments_array[ $base_url . $img_size['file'] ] = get_the_ID(); + } + } + endwhile; + + wp_reset_postdata(); + } + + /** + * Callback function for tevkori_filter_content_images. + * + * @since 3.0 + * + * @see tevkori_filter_content_images + * @param array $matches Array containing the regular expression matches. + */ + public function callback( $matches ) { + $atts = $matches[1]; + $sizes = $srcset = ''; + + // Check if srcset attribute is not already present. + if ( false !== strpos( 'srcset="', $atts ) ) { + + // Get the url of the original image. + preg_match( '/src="(.+?)(\-([0-9]+)x([0-9]+))?(\.[a-zA-Z]{3,4})"/i', $atts, $url_matches ); + + $url = $url_matches[1] . $url_matches[5]; + + // Get the image ID. + $id = $this->attachments_array[$url]; + + if ( $id ) { + + // Use the width and height from the image url. + if ( $url_matches[3] && $url_matches[4] ) { + $size = array( + (int) $url_matches[3], + (int) $url_matches[4] + ); + } else { + $size = 'full'; + } + + // Get the srcset string. + $srcset_string = tevkori_get_srcset_string( $id, $size ); + + if ( $srcset_string ) { + $srcset = ' ' . $srcset_string; + + // Get the sizes string. + $sizes_string = tevkori_get_sizes_string( $id, $size ); + + if ( $sizes_string && ! preg_match( '/sizes="([^"]+)"/i', $atts ) ) { + $sizes = ' ' . $sizes_string; + } + } + } + } + + return ''; + } +} \ No newline at end of file diff --git a/wp-tevko-responsive-images.php b/wp-tevko-responsive-images.php index 9895d01..8e00711 100644 --- a/wp-tevko-responsive-images.php +++ b/wp-tevko-responsive-images.php @@ -19,6 +19,8 @@ defined( 'ABSPATH' ) or die( "No script kiddies please!" ); // List includes +require_once( plugin_dir_path( __FILE__ ) . 'class-srcset-callback.php' ); + if ( class_exists( 'Imagick' ) ) { require_once( plugin_dir_path( __FILE__ ) . 'class-respimg.php' ); require_once( plugin_dir_path( __FILE__ ) . 'class-wp-image-editor-respimg.php' ); @@ -308,70 +310,24 @@ function tevkori_get_src_sizes( $id, $size = 'thumbnail' ) { * @param string $content The raw post content to be filtered. */ function tevkori_filter_content_images( $content ) { + preg_match_all( '/src="([^"]*)"/', $content, $matches ); + + $urls = array_pop( $matches ); + + $attachments = tevkori_attachment_urls_to_loop( $urls ); + + $content_filter = new WP_Ricg_Content_Filter( $attachments ); + return preg_replace_callback( '/]+)>/i', // Don't use an anonymous callback function because it isn't supported by PHP 5.2. - 'tevkori_filter_content_images_callback', + array( $content_filter, 'callback' ), $content ); } add_filter( 'the_content', 'tevkori_filter_content_images', 5, 1 ); -/** - * Callback function for tevkori_filter_content_images. - * - * @since 3.0 - * - * @see tevkori_filter_content_images - * @param array $matches Array containing the regular expression matches. - */ -function tevkori_filter_content_images_callback( $matches ) { - $atts = $matches[1]; - $sizes = $srcset = ''; - - // Check if srcset attribute is not already present. - if ( false !== strpos( 'srcset="', $atts ) ) { - - // Get the url of the original image. - preg_match( '/src="(.+?)(\-([0-9]+)x([0-9]+))?(\.[a-zA-Z]{3,4})"/i', $atts, $url_matches ); - - $url = $url_matches[1] . $url_matches[5]; - - // Get the image ID. - $id = attachment_url_to_postid( $url ); - - if ( $id ) { - - // Use the width and height from the image url. - if ( $url_matches[3] && $url_matches[4] ) { - $size = array( - (int) $url_matches[3], - (int) $url_matches[4] - ); - } else { - $size = 'full'; - } - - // Get the srcset string. - $srcset_string = tevkori_get_srcset_string( $id, $size ); - - if ( $srcset_string ) { - $srcset = ' ' . $srcset_string; - - // Get the sizes string. - $sizes_string = tevkori_get_sizes_string( $id, $size ); - - if ( $sizes_string && ! preg_match( '/sizes="([^"]+)"/i', $atts ) ) { - $sizes = ' ' . $sizes_string; - } - } - } - } - - return ''; -} - /** * Filter to add srcset and sizes attributes to post thumbnails and gallery images. * @@ -400,3 +356,57 @@ function tevkori_filter_attachment_image_attributes( $attr, $attachment, $size ) return $attr; } add_filter( 'wp_get_attachment_image_attributes', 'tevkori_filter_attachment_image_attributes', 0, 3 ); + +/** + * Convert multiple attachement URLs to thier ID. + * + * @since 3.0 + * + * @return array IDs. + **/ +function tevkori_attachment_urls_to_loop( $urls ) { + global $wpdb; + + if ( is_string( $urls ) ) { + return tevkori_attachment_urls_to_loop( array( $urls ) ); + } + + if ( ! is_array( $urls ) ) { + $urls = array(); + } + + $dir = wp_upload_dir(); + $paths = $urls; + + $site_url = parse_url( $dir['url'] ); + + foreach ( $paths as $k => $path ) { + $image_path = parse_url( $path ); + + // Force the protocols to match if needed. + if ( isset( $image_path['scheme'] ) && ( $image_path['scheme'] !== $site_url['scheme'] ) ) { + $path = str_replace( $image_path['scheme'], $site_url['scheme'], $path ); + } + + if ( 0 === strpos( $path, $dir['baseurl'] . '/' ) ) { + $path = substr( $path, strlen( $dir['baseurl'] . '/' ) ); + } + + $paths[$k] = $path; + } + + $attachments = new WP_Query(array( + 'post_type' => 'attachment', + 'meta_query' => array( + array( + 'key' => '_wp_attached_file', + 'value' => $paths, + 'compare' => 'in' + ), + ), + 'posts_per_page' => '-1', + 'post_status' => 'inherit', + )); + + return $attachments; +} From b7286db6ec31234b9e8516ff99254624f9674435 Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Fri, 18 Sep 2015 09:46:59 -0500 Subject: [PATCH 02/14] Content filter: try parsing the HTML before a DB query --- class-srcset-callback.php | 90 ----------------------- wp-tevko-responsive-images.php | 126 +++++++++++++++------------------ 2 files changed, 59 insertions(+), 157 deletions(-) delete mode 100644 class-srcset-callback.php diff --git a/class-srcset-callback.php b/class-srcset-callback.php deleted file mode 100644 index 016283f..0000000 --- a/class-srcset-callback.php +++ /dev/null @@ -1,90 +0,0 @@ -attachments_loop = $attachments_loop; - $upload_dir = wp_upload_dir(); - $upload_dir = $upload_dir['baseurl'] . '/'; - - while( $attachments_loop->have_posts() ) : - $attachments_loop->the_post(); - - // Get the image meta data. - if ( is_array( $img_meta = wp_get_attachment_metadata( get_the_ID() ) ) ) { - - // Get base URL. - $base_url = $upload_dir . pathinfo( $img_meta['file'], PATHINFO_DIRNAME ); - $base_url = untrailingslashit( $base_url ) . '/'; - - // Add default/full size image. - $this->attachments_array[ $upload_dir . $img_meta['file'] ] = get_the_ID(); - - // Build from sizes array. - $img_sizes = $img_meta['sizes']; - - foreach( $img_sizes as $img_size ) { - $this->attachments_array[ $base_url . $img_size['file'] ] = get_the_ID(); - } - } - endwhile; - - wp_reset_postdata(); - } - - /** - * Callback function for tevkori_filter_content_images. - * - * @since 3.0 - * - * @see tevkori_filter_content_images - * @param array $matches Array containing the regular expression matches. - */ - public function callback( $matches ) { - $atts = $matches[1]; - $sizes = $srcset = ''; - - // Check if srcset attribute is not already present. - if ( false !== strpos( 'srcset="', $atts ) ) { - - // Get the url of the original image. - preg_match( '/src="(.+?)(\-([0-9]+)x([0-9]+))?(\.[a-zA-Z]{3,4})"/i', $atts, $url_matches ); - - $url = $url_matches[1] . $url_matches[5]; - - // Get the image ID. - $id = $this->attachments_array[$url]; - - if ( $id ) { - - // Use the width and height from the image url. - if ( $url_matches[3] && $url_matches[4] ) { - $size = array( - (int) $url_matches[3], - (int) $url_matches[4] - ); - } else { - $size = 'full'; - } - - // Get the srcset string. - $srcset_string = tevkori_get_srcset_string( $id, $size ); - - if ( $srcset_string ) { - $srcset = ' ' . $srcset_string; - - // Get the sizes string. - $sizes_string = tevkori_get_sizes_string( $id, $size ); - - if ( $sizes_string && ! preg_match( '/sizes="([^"]+)"/i', $atts ) ) { - $sizes = ' ' . $sizes_string; - } - } - } - } - - return ''; - } -} \ No newline at end of file diff --git a/wp-tevko-responsive-images.php b/wp-tevko-responsive-images.php index 8e00711..8342a67 100644 --- a/wp-tevko-responsive-images.php +++ b/wp-tevko-responsive-images.php @@ -310,24 +310,70 @@ function tevkori_get_src_sizes( $id, $size = 'thumbnail' ) { * @param string $content The raw post content to be filtered. */ function tevkori_filter_content_images( $content ) { - preg_match_all( '/src="([^"]*)"/', $content, $matches ); + // Only match images in our uploads directory + $uploads_dir = wp_upload_dir(); + $path_to_upload_dir = $uploads_dir['baseurl']; - $urls = array_pop( $matches ); + $content = preg_replace_callback( '|]+' . $path_to_upload_dir . '[^>]+>|', '_tevkori_filter_content_images_callback', $content ); - $attachments = tevkori_attachment_urls_to_loop( $urls ); - - $content_filter = new WP_Ricg_Content_Filter( $attachments ); - - return preg_replace_callback( - '/]+)>/i', - - // Don't use an anonymous callback function because it isn't supported by PHP 5.2. - array( $content_filter, 'callback' ), - $content - ); + return $content; } add_filter( 'the_content', 'tevkori_filter_content_images', 5, 1 ); +function _tevkori_filter_content_images_callback( $image ) { + $image_html = $image[0]; + $id = $size = false; + + // Attempt to get the id and size from the class attribute first. + if ( preg_match( '/]+wp-image-([\d]+) size-([^\s|"]+)?[^>]+)>/', $image_html, $matches ) ) { + list( $image, $atts, $id, $size ) = $matches; + // If not, try getting an ID and size by parsing the `src` value. + } elseif ( preg_match( '/]+src="([^"]+)"[^>]+)>/i', $image_html, $url_matches ) ) { + + list( $image, $atts, $url ) = $url_matches; + + $image_filename = basename( $url ); + + // Query the DB to get the post id and meta values for any attachment + // containing the file name of our url. + global $wpdb; + $sql = $wpdb->prepare( + "SELECT `post_id`, `meta_value` FROM `wp_postmeta` WHERE `meta_key` = '_wp_attachment_metadata' AND `meta_value` LIKE %s", + '%' . $image_filename . '%' + ); + $meta_object = $wpdb->get_results( $sql ); + + // If the query is successful, we can determine the ID and size. + if ( $meta_object ) { + $id = $meta_object[0]->post_id; + // Unserialize the meta_value returned in our query. + $meta = maybe_unserialize( $meta_object[0]->meta_value ); + + // If the file name is the full size image, just use that. + if ( $image_filename === basename( $meta['file'] ) ) { + $size = 'full'; + } else { + // Otherwise, we loop through the sizes until we find the one whose + // file name matches the file name of our image. + foreach( $meta['sizes'] as $image_size => $image_size_data ) { + if ( $image_filename === $image_size_data['file'] ) { + $size = $image_size; + break; + } + } + } + } + } + + // If we have an ID and size, try for srcset and sizes and update the markup. + if ( $id && $size && $srcset = tevkori_get_srcset_string( $id, $size ) ) { + $sizes = tevkori_get_sizes_string( $id, $size ); + $image_html = ""; + }; + + return $image_html; +} + /** * Filter to add srcset and sizes attributes to post thumbnails and gallery images. * @@ -356,57 +402,3 @@ function tevkori_filter_attachment_image_attributes( $attr, $attachment, $size ) return $attr; } add_filter( 'wp_get_attachment_image_attributes', 'tevkori_filter_attachment_image_attributes', 0, 3 ); - -/** - * Convert multiple attachement URLs to thier ID. - * - * @since 3.0 - * - * @return array IDs. - **/ -function tevkori_attachment_urls_to_loop( $urls ) { - global $wpdb; - - if ( is_string( $urls ) ) { - return tevkori_attachment_urls_to_loop( array( $urls ) ); - } - - if ( ! is_array( $urls ) ) { - $urls = array(); - } - - $dir = wp_upload_dir(); - $paths = $urls; - - $site_url = parse_url( $dir['url'] ); - - foreach ( $paths as $k => $path ) { - $image_path = parse_url( $path ); - - // Force the protocols to match if needed. - if ( isset( $image_path['scheme'] ) && ( $image_path['scheme'] !== $site_url['scheme'] ) ) { - $path = str_replace( $image_path['scheme'], $site_url['scheme'], $path ); - } - - if ( 0 === strpos( $path, $dir['baseurl'] . '/' ) ) { - $path = substr( $path, strlen( $dir['baseurl'] . '/' ) ); - } - - $paths[$k] = $path; - } - - $attachments = new WP_Query(array( - 'post_type' => 'attachment', - 'meta_query' => array( - array( - 'key' => '_wp_attached_file', - 'value' => $paths, - 'compare' => 'in' - ), - ), - 'posts_per_page' => '-1', - 'post_status' => 'inherit', - )); - - return $attachments; -} From 7fcb70d698a8b956b0f1f101ddc6dac854c50c8e Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Fri, 18 Sep 2015 09:57:54 -0500 Subject: [PATCH 03/14] Remove requirement for removed class-srcset-callback.php --- wp-tevko-responsive-images.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/wp-tevko-responsive-images.php b/wp-tevko-responsive-images.php index 8342a67..a5d8862 100644 --- a/wp-tevko-responsive-images.php +++ b/wp-tevko-responsive-images.php @@ -18,9 +18,6 @@ // Don't load the plugin directly defined( 'ABSPATH' ) or die( "No script kiddies please!" ); -// List includes -require_once( plugin_dir_path( __FILE__ ) . 'class-srcset-callback.php' ); - if ( class_exists( 'Imagick' ) ) { require_once( plugin_dir_path( __FILE__ ) . 'class-respimg.php' ); require_once( plugin_dir_path( __FILE__ ) . 'class-wp-image-editor-respimg.php' ); From 915d8c3770f018b73530b327fa035901040c415b Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Fri, 18 Sep 2015 17:17:57 -0500 Subject: [PATCH 04/14] Updated the filter logic and tests --- tests/test-suite.php | 3 +- wp-tevko-responsive-images.php | 52 +++++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/tests/test-suite.php b/tests/test-suite.php index 16b68ed..df650d9 100644 --- a/tests/test-suite.php +++ b/tests/test-suite.php @@ -384,7 +384,8 @@ function test_tevkori_filter_content_images() { // Function used to build HTML for the editor. $img = get_image_tag( $id, '', '', '', 'medium' ); - $respimg = preg_replace('/src="([^"]*)/', 'src="$1" ' . $srcset . ' ' . $sizes, $img); + // Manually add srcset and sizes to the markup from get_image_tag(); + $respimg = preg_replace('|]+) />|', '', $img); $content = '

Welcome to WordPress! This post contains important information. After you read it, you can make it private to hide it from visitors but still have the information handy for future reference.

First things first:

diff --git a/wp-tevko-responsive-images.php b/wp-tevko-responsive-images.php index a5d8862..f95ed19 100644 --- a/wp-tevko-responsive-images.php +++ b/wp-tevko-responsive-images.php @@ -311,40 +311,58 @@ function tevkori_filter_content_images( $content ) { $uploads_dir = wp_upload_dir(); $path_to_upload_dir = $uploads_dir['baseurl']; - $content = preg_replace_callback( '|]+' . $path_to_upload_dir . '[^>]+>|', '_tevkori_filter_content_images_callback', $content ); + $content = preg_replace_callback( '|]+' . $path_to_upload_dir . '[^>]+) />|', '_tevkori_filter_content_images_callback', $content ); return $content; } add_filter( 'the_content', 'tevkori_filter_content_images', 5, 1 ); function _tevkori_filter_content_images_callback( $image ) { - $image_html = $image[0]; + list( $image_html, $atts ) = $image; $id = $size = false; - // Attempt to get the id and size from the class attribute first. - if ( preg_match( '/]+wp-image-([\d]+) size-([^\s|"]+)?[^>]+)>/', $image_html, $matches ) ) { - list( $image, $atts, $id, $size ) = $matches; - // If not, try getting an ID and size by parsing the `src` value. - } elseif ( preg_match( '/]+src="([^"]+)"[^>]+)>/i', $image_html, $url_matches ) ) { + // Grab ID and size info from core classes. + if ( preg_match( '/wp-image-([0-9]+)/i', $atts, $class_id ) ) { + $id = $class_id[1]; + } + if ( preg_match( '/size-([^\s|"]+)/i', $atts, $class_size ) ) { + $size = $class_size[1]; + } + + if ( $id && false === $size ) { + preg_match( '/width="([0-9]+)"/', $atts, $width ); + preg_match( '/height="([0-9]+)"/', $atts, $height ); - list( $image, $atts, $url ) = $url_matches; + $size = array( + (int) $width[1], + (int) $height[1] + ); + } - $image_filename = basename( $url ); + // If attempts to get values for `id` and `size` failed, use the + // src to query for matching values in `_wp_attachment_metadata` + if ( false === $id || false === $size ) { + preg_match( '/src="([^"]+)"/', $atts, $url ); + + if ( ! $url[1] ) { + return $image_html; + } + + $image_filename = basename( $url[1] ); // Query the DB to get the post id and meta values for any attachment // containing the file name of our url. global $wpdb; - $sql = $wpdb->prepare( - "SELECT `post_id`, `meta_value` FROM `wp_postmeta` WHERE `meta_key` = '_wp_attachment_metadata' AND `meta_value` LIKE %s", + $meta_object = $wpdb->get_row( $wpdb->prepare( + "SELECT `post_id`, `meta_value` FROM $wpdb->postmeta WHERE `meta_key` = '_wp_attachment_metadata' AND `meta_value` LIKE %s", '%' . $image_filename . '%' - ); - $meta_object = $wpdb->get_results( $sql ); + ) ); // If the query is successful, we can determine the ID and size. - if ( $meta_object ) { - $id = $meta_object[0]->post_id; + if ( is_object( $meta_object ) ) { + $id = $meta_object->post_id; // Unserialize the meta_value returned in our query. - $meta = maybe_unserialize( $meta_object[0]->meta_value ); + $meta = maybe_unserialize( $meta_object->meta_value ); // If the file name is the full size image, just use that. if ( $image_filename === basename( $meta['file'] ) ) { @@ -365,7 +383,7 @@ function _tevkori_filter_content_images_callback( $image ) { // If we have an ID and size, try for srcset and sizes and update the markup. if ( $id && $size && $srcset = tevkori_get_srcset_string( $id, $size ) ) { $sizes = tevkori_get_sizes_string( $id, $size ); - $image_html = ""; + $image_html = ""; }; return $image_html; From b29c09b00fd3f79fedef7b9daa87b6f969a1bff6 Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Fri, 18 Sep 2015 20:10:34 -0500 Subject: [PATCH 05/14] Sanity check value of incoming $image variable --- wp-tevko-responsive-images.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wp-tevko-responsive-images.php b/wp-tevko-responsive-images.php index f95ed19..9eef3c3 100644 --- a/wp-tevko-responsive-images.php +++ b/wp-tevko-responsive-images.php @@ -318,6 +318,10 @@ function tevkori_filter_content_images( $content ) { add_filter( 'the_content', 'tevkori_filter_content_images', 5, 1 ); function _tevkori_filter_content_images_callback( $image ) { + if ( empty( $image ) ) { + return false; + } + list( $image_html, $atts ) = $image; $id = $size = false; From 07ffe147a5ca20778a0ba4883367201fc32ea234 Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Fri, 18 Sep 2015 20:22:37 -0500 Subject: [PATCH 06/14] Update docs for tevkori_filter_content_images and callback --- wp-tevko-responsive-images.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/wp-tevko-responsive-images.php b/wp-tevko-responsive-images.php index 9eef3c3..0c9198f 100644 --- a/wp-tevko-responsive-images.php +++ b/wp-tevko-responsive-images.php @@ -300,11 +300,12 @@ function tevkori_get_src_sizes( $id, $size = 'thumbnail' ) { } /** - * Filter for the_content to add sizes and srcset attributes to images. + * Filters images in post content to add `srcset` and `sizes`. * * @since 3.0 * * @param string $content The raw post content to be filtered. + * @return string Converted content with `srcset` and `sizes` added to images. */ function tevkori_filter_content_images( $content ) { // Only match images in our uploads directory @@ -317,6 +318,12 @@ function tevkori_filter_content_images( $content ) { } add_filter( 'the_content', 'tevkori_filter_content_images', 5, 1 ); +/** + * Private preg_replace callback used in tevkori_filter_content_images() + * + * @access private + * @since 3.0.0 + */ function _tevkori_filter_content_images_callback( $image ) { if ( empty( $image ) ) { return false; From bbcb69b3bd416fc066d09cb6509dc2c047ac08de Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Sat, 19 Sep 2015 09:56:39 -0500 Subject: [PATCH 07/14] Filter: Make closing space and slash optional in regex pattern. --- wp-tevko-responsive-images.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wp-tevko-responsive-images.php b/wp-tevko-responsive-images.php index 0c9198f..0f59127 100644 --- a/wp-tevko-responsive-images.php +++ b/wp-tevko-responsive-images.php @@ -312,7 +312,7 @@ function tevkori_filter_content_images( $content ) { $uploads_dir = wp_upload_dir(); $path_to_upload_dir = $uploads_dir['baseurl']; - $content = preg_replace_callback( '|]+' . $path_to_upload_dir . '[^>]+) />|', '_tevkori_filter_content_images_callback', $content ); + $content = preg_replace_callback( '|]+' . $path_to_upload_dir . '[^>]+)[\s?][\/?]>|i', '_tevkori_filter_content_images_callback', $content ); return $content; } From 758f6e9f6ee196d722062de4a413d74f74316715 Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Sat, 19 Sep 2015 10:05:10 -0500 Subject: [PATCH 08/14] Follow WP standards for inline docs --- wp-tevko-responsive-images.php | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/wp-tevko-responsive-images.php b/wp-tevko-responsive-images.php index 0f59127..7e3eb03 100644 --- a/wp-tevko-responsive-images.php +++ b/wp-tevko-responsive-images.php @@ -308,7 +308,7 @@ function tevkori_get_src_sizes( $id, $size = 'thumbnail' ) { * @return string Converted content with `srcset` and `sizes` added to images. */ function tevkori_filter_content_images( $content ) { - // Only match images in our uploads directory + // Only match images in our uploads directory. $uploads_dir = wp_upload_dir(); $path_to_upload_dir = $uploads_dir['baseurl']; @@ -350,8 +350,10 @@ function _tevkori_filter_content_images_callback( $image ) { ); } - // If attempts to get values for `id` and `size` failed, use the - // src to query for matching values in `_wp_attachment_metadata` + /* + * If attempts to get values for `id` and `size` failed, use the + * src to query for matching values in `_wp_attachment_metadata` + */ if ( false === $id || false === $size ) { preg_match( '/src="([^"]+)"/', $atts, $url ); @@ -361,8 +363,10 @@ function _tevkori_filter_content_images_callback( $image ) { $image_filename = basename( $url[1] ); - // Query the DB to get the post id and meta values for any attachment - // containing the file name of our url. + /* + * Query the DB to get the post id and meta values for any attachment + * containing the file name of our url. + */ global $wpdb; $meta_object = $wpdb->get_row( $wpdb->prepare( "SELECT `post_id`, `meta_value` FROM $wpdb->postmeta WHERE `meta_key` = '_wp_attachment_metadata' AND `meta_value` LIKE %s", @@ -379,8 +383,10 @@ function _tevkori_filter_content_images_callback( $image ) { if ( $image_filename === basename( $meta['file'] ) ) { $size = 'full'; } else { - // Otherwise, we loop through the sizes until we find the one whose - // file name matches the file name of our image. + /* + * Otherwise, we loop through the sizes until we find the one whose + * file name matches the file name of our image. + */ foreach( $meta['sizes'] as $image_size => $image_size_data ) { if ( $image_filename === $image_size_data['file'] ) { $size = $image_size; From 75f99ad621879917bbb6acb5484df0b418539cc8 Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Sat, 19 Sep 2015 17:18:48 -0500 Subject: [PATCH 09/14] Content filter: Try getting medata using an ID. --- wp-tevko-responsive-images.php | 52 +++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/wp-tevko-responsive-images.php b/wp-tevko-responsive-images.php index 7e3eb03..c7a24c5 100644 --- a/wp-tevko-responsive-images.php +++ b/wp-tevko-responsive-images.php @@ -364,29 +364,43 @@ function _tevkori_filter_content_images_callback( $image ) { $image_filename = basename( $url[1] ); /* - * Query the DB to get the post id and meta values for any attachment - * containing the file name of our url. + * If we already have an ID, we use it to get the attachment metadata + * using `wp_get_attachment_metadata()`. Otherwise, we'll use the image + * `src` url to query the postmeta table for both the attachement ID and + * metadata, which we'll use later to get the size. */ - global $wpdb; - $meta_object = $wpdb->get_row( $wpdb->prepare( - "SELECT `post_id`, `meta_value` FROM $wpdb->postmeta WHERE `meta_key` = '_wp_attachment_metadata' AND `meta_value` LIKE %s", - '%' . $image_filename . '%' - ) ); - - // If the query is successful, we can determine the ID and size. - if ( is_object( $meta_object ) ) { - $id = $meta_object->post_id; - // Unserialize the meta_value returned in our query. - $meta = maybe_unserialize( $meta_object->meta_value ); - - // If the file name is the full size image, just use that. + if ( ! empty( $id ) ) { + $meta = wp_get_attachment_metadata( $id ); + } else { + global $wpdb; + $meta_object = $wpdb->get_row( $wpdb->prepare( + "SELECT `post_id`, `meta_value` FROM $wpdb->postmeta WHERE `meta_key` = '_wp_attachment_metadata' AND `meta_value` LIKE %s", + '%' . $image_filename . '%' + ) ); + + // If the query is successful, we can determine the ID and size. + if ( is_object( $meta_object ) ) { + $id = $meta_object->post_id; + // Unserialize the meta_value returned in our query. + $meta = maybe_unserialize( $meta_object->meta_value ); + } else { + $meta = false; + } + } + + /* + * Now that we have the attachment ID and metadata, we can retrieve the + * size by matching the original image's `src` filename with the sizes + * included in the attachment metadata. + */ + if ( $id && $meta ) { + /* + * First, see if the file is the full size image. If not, we loop through + * the itermediate sizes until we find a match. + */ if ( $image_filename === basename( $meta['file'] ) ) { $size = 'full'; } else { - /* - * Otherwise, we loop through the sizes until we find the one whose - * file name matches the file name of our image. - */ foreach( $meta['sizes'] as $image_size => $image_size_data ) { if ( $image_filename === $image_size_data['file'] ) { $size = $image_size; From 8bb2f333325ea56cc47774459840ccee207dd71b Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Sat, 19 Sep 2015 17:24:07 -0500 Subject: [PATCH 10/14] Cast as integer when retrieving from class name --- wp-tevko-responsive-images.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wp-tevko-responsive-images.php b/wp-tevko-responsive-images.php index c7a24c5..c761316 100644 --- a/wp-tevko-responsive-images.php +++ b/wp-tevko-responsive-images.php @@ -334,7 +334,7 @@ function _tevkori_filter_content_images_callback( $image ) { // Grab ID and size info from core classes. if ( preg_match( '/wp-image-([0-9]+)/i', $atts, $class_id ) ) { - $id = $class_id[1]; + (int) $id = $class_id[1]; } if ( preg_match( '/size-([^\s|"]+)/i', $atts, $class_size ) ) { $size = $class_size[1]; From b5b0484b00b19bf83444ecd23d086e1d1dee92e2 Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Sat, 19 Sep 2015 17:26:03 -0500 Subject: [PATCH 11/14] Replace '// List includes' that was removed --- wp-tevko-responsive-images.php | 1 + 1 file changed, 1 insertion(+) diff --git a/wp-tevko-responsive-images.php b/wp-tevko-responsive-images.php index c761316..c50bcfb 100644 --- a/wp-tevko-responsive-images.php +++ b/wp-tevko-responsive-images.php @@ -18,6 +18,7 @@ // Don't load the plugin directly defined( 'ABSPATH' ) or die( "No script kiddies please!" ); +// List includes if ( class_exists( 'Imagick' ) ) { require_once( plugin_dir_path( __FILE__ ) . 'class-respimg.php' ); require_once( plugin_dir_path( __FILE__ ) . 'class-wp-image-editor-respimg.php' ); From 610974c69cb48b18e5238f0969470c687be4ca64 Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Sun, 20 Sep 2015 17:10:34 -0500 Subject: [PATCH 12/14] Content filter: Bail early if `srcset` present. --- wp-tevko-responsive-images.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/wp-tevko-responsive-images.php b/wp-tevko-responsive-images.php index c50bcfb..9777a75 100644 --- a/wp-tevko-responsive-images.php +++ b/wp-tevko-responsive-images.php @@ -333,6 +333,11 @@ function _tevkori_filter_content_images_callback( $image ) { list( $image_html, $atts ) = $image; $id = $size = false; + // Bail early if a `srcset` attribute already exists. + if ( false !== strpos( $atts, 'srcset=' ) ) { + return $image_html; + } + // Grab ID and size info from core classes. if ( preg_match( '/wp-image-([0-9]+)/i', $atts, $class_id ) ) { (int) $id = $class_id[1]; From b774f4065f094665851a40bcfffc37c456a2981e Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Mon, 21 Sep 2015 20:51:50 -0500 Subject: [PATCH 13/14] Tests: Account for all content filter patterns --- tests/test-suite.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/test-suite.php b/tests/test-suite.php index df650d9..48577a1 100644 --- a/tests/test-suite.php +++ b/tests/test-suite.php @@ -383,21 +383,31 @@ function test_tevkori_filter_content_images() { // Function used to build HTML for the editor. $img = get_image_tag( $id, '', '', '', 'medium' ); + $img_no_size = str_replace( 'size-', '', $img ); + $img_no_size_id = str_replace( 'wp-attachment-', '', $img_no_size ); // Manually add srcset and sizes to the markup from get_image_tag(); $respimg = preg_replace('|]+) />|', '', $img); + $respimg_no_size = preg_replace('|]+) />|', '', $img_no_size); + $respimg_no_size_id = preg_replace('|]+) />|', '', $img_no_size_id); $content = '

Welcome to WordPress! This post contains important information. After you read it, you can make it private to hide it from visitors but still have the information handy for future reference.

First things first:

+ + %1$s + - %1$s + %2$s

As a subscriber, you will receive an email every time an update is available (and only then). This will make it easier to keep your site up to date, and secure from evildoers.
When a new version is released, log in to the Dashboard and follow the instructions.
Upgrading is a couple of clicks!

+ + %3$s +

Then you can start enjoying the WordPress experience:

'; - $content_unfiltered = sprintf( $content, $img ); - $content_filtered = sprintf( $content, $respimg ); + $content_unfiltered = sprintf( $content, $img, $img_no_size, $img_no_size_id ); + $content_filtered = sprintf( $content, $respimg, $respimg_no_size, $respimg_no_size_id ); $this->assertSame( $content_filtered, tevkori_filter_content_images( $content_unfiltered ) ); } From 334141ba1d20595f2a46e690b0e15528acbb02b1 Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Mon, 21 Sep 2015 21:01:16 -0500 Subject: [PATCH 14/14] Tests: Add test for preexisting srcset attribute --- tests/test-suite.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test-suite.php b/tests/test-suite.php index 48577a1..8d1cdc4 100644 --- a/tests/test-suite.php +++ b/tests/test-suite.php @@ -424,4 +424,20 @@ function test_tevkori_filter_content_images() { $this->assertSame( $content_filtered, tevkori_filter_content_images( $content_unfiltered ) ); } + + /** + * @group 170 + */ + function test_tevkori_filter_content_images_with_preexisting_srcset() { + // Make image. + $id = $this->_test_img(); + + // Generate HTML and add a dummy srcset attribute. + $image_html = get_image_tag( $id, '', '', '', 'medium' ); + $image_html = preg_replace('|]+) />|', '', $image_html ); + + // The content filter should return the image unchanged. + $this->assertSame( $image_html, tevkori_filter_content_images( $image_html ) ); + } + }