Skip to content
Merged

3.2.14 #1100

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
2 changes: 1 addition & 1 deletion .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.2.13
3.2.14
10 changes: 5 additions & 5 deletions languages/cloudinary.pot
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Cloudinary STABLETAG\n"
"Report-Msgid-Bugs-To: https://github.com/cloudinary/cloudinary_wordpress\n"
"POT-Creation-Date: 2025-09-12 09:34:43+00:00\n"
"POT-Creation-Date: 2025-09-29 13:51:03+00:00\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
Expand All @@ -22,7 +22,7 @@ msgstr ""
"X-Poedit-SearchPath-0: .\n"
"X-Poedit-Bookmarks: \n"
"X-Textdomain-Support: yes\n"
"X-Generator: grunt-wp-i18n 1.0.3\n"
"X-Generator: grunt-wp-i18n 1.0.4\n"

#: cloudinary.php:63
msgid ""
Expand Down Expand Up @@ -808,17 +808,17 @@ msgstr ""
msgid "Clean up sync metadata for %d"
msgstr ""

#: php/class-utils.php:1279
#: php/class-utils.php:1293
msgid "Cloudinary global transformations"
msgstr ""

#: php/class-utils.php:1286
#: php/class-utils.php:1300
#. translators: %1$s is the taxonomy label and the %2$s is the context of the
#. use.
msgid "%1$s %2$s transformations"
msgstr ""

#: php/class-utils.php:1296
#: php/class-utils.php:1310
#. translators: %s is the term name.
msgid "%s transformations"
msgstr ""
Expand Down
84,033 changes: 45,334 additions & 38,699 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@
"chart.js": "^3.5.0",
"classnames": "^2.3.1",
"codemirror": "^5.65.0",
"coveralls": "^3.1.0",
"cross-env": "^7.0.2",
"css-loader": "^5.0.1",
"css-minimizer-webpack-plugin": "^4.2.2",
Expand Down Expand Up @@ -144,5 +143,5 @@
"webpack-cli": "^4.2.0",
"webpackbar": "^5.0.2"
},
"version": "3.2.13"
"version": "3.2.14"
}
1 change: 1 addition & 0 deletions php/class-assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ class Assets extends Settings_Component {
*/
public function __construct( Plugin $plugin ) {
parent::__construct( $plugin );
$this->should_sanitize_slugs = true;

$this->media = $plugin->get_component( 'media' );
$this->delivery = $plugin->get_component( 'delivery' );
Expand Down
96 changes: 66 additions & 30 deletions php/class-utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,9 @@ public static function is_admin() {
* @return array
*/
public static function extract_urls( $content ) {
// Remove inline SVG data URIs, as they can cause parsing issues when extracting URLs.
$content = self::strip_inline_svg_data_uris( $content );

preg_match_all(
"#([\"']?)("
. '(?:[\w-]+:)?//?'
Expand All @@ -642,6 +645,25 @@ public static function extract_urls( $content ) {
return array_values( $post_links );
}

/**
* Strip inline SVG data URIs from content.
*
* @param string $content The content to process.
*
* @return string The content with SVG data URIs removed.
*/
public static function strip_inline_svg_data_uris( $content ) {
// Pattern to match the data URI structure: data:image/svg+xml;base64,<base64-encoded-data>.
$svg_data_uri_pattern = '/data:image\/svg\+xml;base64,[a-zA-Z0-9\/\+\=]+/i';

// Remove all occurrences of SVG data URIs from the content.
$cleaned_content = preg_replace( $svg_data_uri_pattern, '', $content );

// In case an error occurred, we return the original content to avoid data loss.
return is_null( $cleaned_content ) ? $content : $cleaned_content;
}


/**
* Is saving metadata.
*
Expand Down Expand Up @@ -1009,19 +1031,16 @@ public static function attachment_url_to_postid( $url ) {
* @return array
*/
public static function query_relations( $public_ids, $urls = array() ) {
global $wpdb;

$wheres = array();
$searched_things = array();
$chunk_size = 25;
$results = array();
$tablename = self::get_relationship_table();

/**
* Filter the media context query.
*
* @hook cloudinary_media_context_query
* @since 3.2.0
*
* @param $media_context_query {string} The default media context query.
*
* @return {string}
*/
$media_context_query = apply_filters( 'cloudinary_media_context_query', 'media_context = %s' );
Expand All @@ -1031,43 +1050,60 @@ public static function query_relations( $public_ids, $urls = array() ) {
*
* @hook cloudinary_media_context_things
* @since 3.2.0
*
* @param $media_context_things {array} The default media context things.
*
* @return {array}
*/
$media_context_things = apply_filters( 'cloudinary_media_context_things', array( 'default' ) );

// Query for urls in chunks.
if ( ! empty( $urls ) ) {
// Do the URLS.
$list = implode( ', ', array_fill( 0, count( $urls ), '%s' ) );
$where = "(url_hash IN( {$list} ) AND {$media_context_query} )";
$searched_things = array_merge( $searched_things, array_map( 'md5', $urls ), $media_context_things );
$wheres[] = $where;
$results = array_merge( $results, self::run_chunked_query( 'url_hash', $urls, $chunk_size, $tablename, $media_context_query, $media_context_things ) );
}
// Query for public_ids in chunks.
if ( ! empty( $public_ids ) ) {
// Do the public_ids.
$list = implode( ', ', array_fill( 0, count( $public_ids ), '%s' ) );
$where = "(public_hash IN( {$list} ) AND {$media_context_query} )";
$searched_things = array_merge( $searched_things, array_map( 'md5', $public_ids ), $media_context_things );
$wheres[] = $where;
$results = array_merge( $results, self::run_chunked_query( 'public_hash', $public_ids, $chunk_size, $tablename, $media_context_query, $media_context_things ) );
}

$results = array();

if ( ! empty( array_filter( $wheres ) ) ) {
$tablename = self::get_relationship_table();
$sql = "SELECT * from {$tablename} WHERE " . implode( ' OR ', $wheres );
$prepared = $wpdb->prepare( $sql, $searched_things ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$cache_key = md5( $prepared );
$results = wp_cache_get( $cache_key, 'cld_delivery' );
if ( empty( $results ) ) {
$results = $wpdb->get_results( $prepared, ARRAY_A );// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery
wp_cache_add( $cache_key, $results, 'cld_delivery' );
return $results;
}
/**
* Run a chunked query and merge results.
*
* @param string $field The DB field to query (url_hash or public_hash).
* @param array $items The items to query.
* @param int $chunk_size Number of items per chunk.
* @param string $tablename The table name.
* @param string $media_context_query The media context SQL.
* @param array $media_context_things The media context values.
* @return array
*/
protected static function run_chunked_query( $field, $items, $chunk_size, $tablename, $media_context_query, $media_context_things ) {
global $wpdb;

$all_results = array();
$chunks = array_chunk( $items, $chunk_size );

foreach ( $chunks as $chunk ) {
$list = implode( ', ', array_fill( 0, count( $chunk ), '%s' ) );
$where = "({$field} IN( {$list} ) AND {$media_context_query} )";
$searched_things = array_merge( array_map( 'md5', $chunk ), $media_context_things );
$sql = "SELECT * from {$tablename} WHERE {$where}";
$prepared = $wpdb->prepare( $sql, $searched_things ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$cache_key = md5( $prepared );
$chunk_results = wp_cache_get( $cache_key, 'cld_delivery' );

if ( empty( $chunk_results ) ) {
$chunk_results = $wpdb->get_results( $prepared, ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery

wp_cache_add( $cache_key, $chunk_results, 'cld_delivery' );
}

if ( ! empty( $chunk_results ) ) {
$all_results = array_merge( $all_results, $chunk_results );
}
}

return $results;
return $all_results;
}

/**
Expand Down
11 changes: 10 additions & 1 deletion php/traits/trait-params.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ trait Params_Trait {
*/
public $separator = '.';

/**
* Whether to sanitize slugs.
*
* @var boolean
*/
protected $should_sanitize_slugs = false;

/**
* Sets the params recursively.
*
Expand Down Expand Up @@ -77,7 +84,6 @@ protected function set_param_array( $parts, $param, $value ) {
* @return $this
*/
public function set_param( $param, $value = null ) {

$sanitized_param = $this->sanitize_slug( $param );
$parts = explode( $this->separator, $sanitized_param );
$param = array_shift( $parts );
Expand Down Expand Up @@ -140,6 +146,9 @@ public function remove_param( $param ) {
* @return string
*/
protected function sanitize_slug( $slug ) {
if ( ! $this->should_sanitize_slugs || ! str_contains( $slug, $this->separator ) ) {
return $slug;
}

$sanitized = array_map( 'sanitize_file_name', explode( $this->separator, $slug ) );

Expand Down
11 changes: 10 additions & 1 deletion readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Contributors: Cloudinary, XWP, Automattic
Tags: image-optimizer, core-web-vitals, video, resize, performance
Requires at least: 4.7
Tested up to: 6.8.2
Tested up to: 6.8.3
Requires PHP: 5.6
Stable tag: STABLETAG
License: GPLv2
Expand Down Expand Up @@ -146,6 +146,15 @@ Your site is now setup to start using Cloudinary.

== Changelog ==

= 3.2.14 (22 October 2025) =

Fixes and Improvements:

* Upgraded dependencies: `form-data` and `@babel/traverse` packages updated for improved stability and security
* Improved SQL queries for enhanced compatibility and performance with WP Engine environments
* Improved initialization performance by optimizing the `sanitize_slug` method usage to reduce unnecessary processing
* Fixed incompatibility causing broken media URLs when JetEngine dashboard styles loaded inline SVGs

= 3.2.13 (17 September 2025) =

Fixes and Improvements:
Expand Down