Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Content filtering #170

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions class-srcset-callback.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

class WP_Ricg_Content_Filter {
private $attachments_loop;
private $attachments_array;

function __construct( $attachments_loop ) {
$this->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 ) ) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction:

if ( false === strpos( $atts, 'srcset="' ) ) {


// Get the url of the original image.
preg_match( '/src="(.+?)(\-([0-9]+)x([0-9]+))?(\.[a-zA-Z]{3,4})"/i', $atts, $url_matches );
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edited images get a suffix of "-e" followed by 13 digits. We should probably check for this too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex won't match JPEG 2000 (.JP2).
Probably better to use \.\w{3,4} instead of \.[a-zA-Z]{3,4} but have to make sure that it only matches if it's at the end to avoid problems with URLs that have an IP address as host.


$url = $url_matches[1] . $url_matches[5];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was getting the extension in either 4 or 5,

$url = $url_matches[1] . array_pop( $url_matches );


// 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(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although array pop is needed above, if there are sizes they're always in 3 & 4 so I haven't changed these.

(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 ) ) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this better to remove the regexp?

if ( $sizes_string && ( false === strpos( $atts, 'sizes="' ) ) ) {

$sizes = ' ' . $sizes_string;
}
}
}
}

return '<img ' . $atts . $sizes . $srcset . '>';
}
}
53 changes: 0 additions & 53 deletions js/wp-tevko-responsive-images.js

This file was deleted.

44 changes: 44 additions & 0 deletions tests/test-suite.php
Original file line number Diff line number Diff line change
Expand Up @@ -370,4 +370,48 @@ function test_tevkori_filter_attachment_image_attributes_thumbnails() {
$this->assertFalse( isset( $resp_attr['srcset'] ) );
$this->assertFalse( isset( $resp_attr['sizes'] ) );
}

/**
* @group 170
*/
function test_tevkori_filter_content_images() {
// Make image.
$id = $this->_test_img();

$srcset = tevkori_get_srcset_string( $id, 'medium' );
$sizes = tevkori_get_sizes_string( $id, 'medium' );

// Function used to build HTML for the editor.
$img = get_image_tag( $id, '', '', '', 'medium' );

// Manually add srcset and sizes to the markup from get_image_tag();
$respimg = preg_replace('|<img ([^>]+) />|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img);

$content = '<p>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.</p>
<p>First things first:</p>
<ul>
<li><a href="http://wordpress.org" title="Subscribe to the WordPress mailing list for Release Notifications">Subscribe to the WordPress mailing list for release notifications</a></li>
</ul>

%1$s

<p>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.<br />
When a new version is released, <a href="http://wordpress.org" title="If you are already logged in, this will take you directly to the Dashboard">log in to the Dashboard</a> and follow the instructions.<br />
Upgrading is a couple of clicks!</p>
<p>Then you can start enjoying the WordPress experience:</p>
<ul>
<li>Edit your personal information at <a href="http://wordpress.org" title="Edit settings like your password, your display name and your contact information">Users &#8250; Your Profile</a></li>
<li>Start publishing at <a href="http://wordpress.org" title="Create a new post">Posts &#8250; Add New</a> and at <a href="http://wordpress.org" title="Create a new page">Pages &#8250; Add New</a></li>
<li>Browse and install plugins at <a href="http://wordpress.org" title="Browse and install plugins at the official WordPress repository directly from your Dashboard">Plugins &#8250; Add New</a></li>
<li>Browse and install themes at <a href="http://wordpress.org" title="Browse and install themes at the official WordPress repository directly from your Dashboard">Appearance &#8250; Add New Themes</a></li>
<li>Modify and prettify your website&#8217;s links at <a href="http://wordpress.org" title="For example, select a link structure like: http://example.com/1999/12/post-name">Settings &#8250; Permalinks</a></li>
<li>Import content from another system or WordPress site at <a href="http://wordpress.org" title="WordPress comes with importers for the most common publishing systems">Tools &#8250; Import</a></li>
<li>Find answers to your questions at the <a href="http://wordpress.orgs" title="The official WordPress documentation, maintained by the WordPress community">WordPress Codex</a></li>
</ul>';

$content_unfiltered = sprintf( $content, $img );
$content_filtered = sprintf( $content, $respimg );

$this->assertSame( $content_filtered, tevkori_filter_content_images( $content_unfiltered ) );
}
}
133 changes: 60 additions & 73 deletions wp-tevko-responsive-images.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' );
Expand Down Expand Up @@ -301,31 +303,30 @@ function tevkori_get_src_sizes( $id, $size = 'thumbnail' ) {
}

/**
* Filter for extending image tag to include srcset attribute.
* Filter for the_content to add sizes and srcset attributes to images.
*
* @since 3.0
*
* @see images_send_to_editor
* @return string HTML for image.
* @param string $content The raw post content to be filtered.
*/
function tevkori_extend_image_tag( $html, $id, $caption, $title, $align, $url, $size, $alt ) {
add_filter( 'editor_max_image_size', 'tevkori_editor_image_size' );

// Get the srcset attribute.
$srcset = tevkori_get_srcset_string( $id, $size );

remove_filter( 'editor_max_image_size', 'tevkori_editor_image_size' );
function tevkori_filter_content_images( $content ) {
preg_match_all( '/src="([^"]*)"/', $content, $matches );

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to skip images with srcset in this line, regexp isn't my strong point sorry.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@peterwilsoncc

I am not a regex expert either, but I don't think they should be used to check if something is not there. If it's possible to write a regex like that it will probably not perform very well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should limit this to image elements because iframes also have a src attribute.


if ( $srcset ) {
$urls = array_pop( $matches );

// Build the data-sizes attribute if sizes were returned.
$sizes = tevkori_get_sizes( $id, $size );
$sizes = $sizes ? 'data-sizes="' . $sizes . '"' : '';
$attachments = tevkori_attachment_urls_to_loop( $urls );
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The URLs that we pass to the function contain the size suffix (eg. -300x200) if present. I think we have to strip that like we do in https://github.com/ResponsiveImagesCG/wp-tevko-responsive-images/pull/170/files#diff-517abe0a5b7ee2c55c6b5659d0c9350eR52 but would be nice if we don't have to do it at two places.


$html = preg_replace( '/(src\s*=\s*"(.+?)")/', '$1 ' . $sizes . ' ' . $srcset, $html );
}

return $html;
$content_filter = new WP_Ricg_Content_Filter( $attachments );

return preg_replace_callback(
'/<img\s([^>]+)>/i',

// Don't use an anonymous callback function because it isn't supported by PHP 5.2.
array( $content_filter, 'callback' ),
$content
);
}
add_filter( 'image_send_to_editor', 'tevkori_extend_image_tag', 0, 8 );
add_filter( 'the_content', 'tevkori_filter_content_images', 5, 1 );

/**
* Filter to add srcset and sizes attributes to post thumbnails and gallery images.
Expand Down Expand Up @@ -357,69 +358,55 @@ function tevkori_filter_attachment_image_attributes( $attr, $attachment, $size )
add_filter( 'wp_get_attachment_image_attributes', 'tevkori_filter_attachment_image_attributes', 0, 3 );

/**
* Disable the editor size constraint applied for images in TinyMCE.
* Convert multiple attachement URLs to thier ID.
*
* @return array A width & height array so large it shouldn't constrain reasonable images.
*/
function tevkori_editor_image_size() {
return array( 99999, 99999 );
}

/**
* Load admin scripts.
* @since 3.0
*
* @param string $hook Admin page file name.
*/
function tevkori_load_admin_scripts( $hook ) {
if ( $hook == 'post.php' || $hook == 'post-new.php' ) {
wp_enqueue_script( 'wp-tevko-responsive-images', plugin_dir_url( __FILE__ ) . 'js/wp-tevko-responsive-images.js', array( 'wp-backbone' ), '2.0.0', true );
}
}
add_action( 'admin_enqueue_scripts', 'tevkori_load_admin_scripts' );
* @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();
}

/**
* Filter for the_content to replace data-size attributes with size attributes.
*
* @since 2.2.0
*
* @param string $content The raw post content to be filtered.
*/
function tevkori_filter_content_sizes( $content ) {
$images = '/(<img\s.*?)data-sizes="([^"]+)"/i';
$sizes = '${2}';
$dir = wp_upload_dir();
$paths = $urls;

return preg_replace( $images, '${1}sizes="' . $sizes . '"', $content );
}
add_filter( 'the_content', 'tevkori_filter_content_sizes' );
$site_url = parse_url( $dir['url'] );

foreach ( $paths as $k => $path ) {
$image_path = parse_url( $path );
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we could probably add the check if the file path matches with the upload dir and skip if not, as suggested by @joemcgill in #144 (comment)


/**
* Ajax handler for updating the srcset when an image is changed in the editor.
*
* @since 2.3.0
*
* @return string A sourcest value.
*/
function tevkori_ajax_srcset() {
// 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 );
}

// Bail early if no post ID is passed.
if ( empty( $_POST['postID'] ) ) {
return;
}
if ( 0 === strpos( $path, $dir['baseurl'] . '/' ) ) {
$path = substr( $path, strlen( $dir['baseurl'] . '/' ) );
}

$postID = (int) $_POST['postID'];
if ( ! $postID ) {
return;
$paths[$k] = $path;
}

// Grab the image size being passed from the AJAX request.
$size = empty( $_POST['size'] ) ? $_POST['size'] : '';

// Get the srcset value for our image.
$srcset = tevkori_get_srcset( $postID, $size );

// For AJAX requests, we echo the result and then die.
wp_send_json( $srcset );
$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;
}
add_action( 'wp_ajax_tevkori_ajax_srcset', 'tevkori_ajax_srcset' );