Skip to content

Commit 6d05010

Browse files
committed
Enhance image URL handling when the image is from DAM inside the editor
- Added logic to extract the original URL from Optimole URLs in the `get_optimized_image_url` method. - Moved the `is_dam_url` method to the `Optml_Dam_Offload_Utils` trait for better organization. - Updated the `Normalizer` trait to handle specific URL patterns. - Introduced new tests for `get_unoptimized_url` to cover various scenarios, including non-Optimole URLs and edge cases. - Prevented cache clearing actions during tests to avoid errors related to cache compatibility. https://secure.helpscout.net/conversation/3142052654/476408?viewId=2353196
1 parent 4514c15 commit 6d05010

6 files changed

Lines changed: 231 additions & 30 deletions

File tree

inc/app_replacer.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,9 @@ public function url_has_dam_flag( $url ) {
664664
protected function get_optimized_image_url( $url, $width, $height, $resize = [] ) {
665665
$width = is_int( $width ) ? $width : 'auto';
666666
$height = is_int( $height ) ? $height : 'auto';
667+
// If the image is already using Optimole URL, we extract the source to rebuild it.
668+
$url = $this->get_unoptimized_url( $url );
669+
667670
$optimized_image = Optimole::image( $url, $this->settings->get( 'cache_buster' ) )
668671
->width( $width )
669672
->height( $height );

inc/traits/dam_offload_utils.php

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@
33
trait Optml_Dam_Offload_Utils {
44
use Optml_Normalizer;
55

6+
/**
7+
* Check if this contains the DAM flag.
8+
*
9+
* @param string $url The URL to check.
10+
*
11+
* @return bool
12+
*/
13+
private function is_dam_url( $url ) {
14+
return strpos( $url, Optml_Dam::URL_DAM_FLAG ) !== false;
15+
}
616
/**
717
* Checks that the attachment is a DAM image.
818
*
@@ -239,6 +249,31 @@ private function is_completed_offload( $id ) {
239249

240250
return false;
241251
}
252+
/**
253+
* Get the attachment ID from optimole ID.
254+
*
255+
* @param string $optimole_id The optimole ID.
256+
*
257+
* @return int
258+
*/
259+
private function get_attachement_id_from_optimole_id( string $optimole_id ): int {
260+
global $wpdb;
261+
262+
$id = $wpdb->get_var(
263+
$wpdb->prepare(
264+
"
265+
SELECT post_id
266+
FROM {$wpdb->postmeta}
267+
WHERE meta_key = %s
268+
AND meta_value = %s
269+
LIMIT 1
270+
",
271+
Optml_Dam::OM_DAM_IMPORTED_FLAG,
272+
$optimole_id
273+
)
274+
);
275+
return empty( $id ) ? 0 : (int) $id;
276+
}
242277
/**
243278
* Get the attachment ID from URL.
244279
*
@@ -253,32 +288,43 @@ private function attachment_url_to_post_id( $input_url ) {
253288
return (int) $cached;
254289
}
255290

256-
$url = $this->strip_image_size( $input_url );
291+
if ( $this->is_dam_url( $input_url ) ) {
292+
// The DAM are stored as attachments of format /id:<attachment_id>/<original_url>
293+
$pattern = '#/' . Optml_Media_Offload::KEYS['uploaded_flag'] . '([^/]+)#';
294+
if ( preg_match( $pattern, $input_url, $m ) ) {
295+
$attachment_id = $this->get_attachement_id_from_optimole_id( $m[1] );
296+
} else {
297+
$attachment_id = 0;
298+
}
299+
} else {
257300

258-
$attachment_id = attachment_url_to_postid( $url );
301+
$url = $this->strip_image_size( $input_url );
259302

260-
if ( $attachment_id === 0 && ! $this->is_scaled_url( $url ) ) {
261-
$scaled_url = $this->get_scaled_url( $url );
303+
$attachment_id = attachment_url_to_postid( $url );
262304

263-
$attachment_id = attachment_url_to_postid( $scaled_url );
264-
}
305+
if ( $attachment_id === 0 && ! $this->is_scaled_url( $url ) ) {
306+
$scaled_url = $this->get_scaled_url( $url );
265307

266-
/*
267-
* TODO: The logic is a mess, we need to refactor at some point.
268-
* Websites may transition between 'www' subdomains and apex domains, potentially breaking references to hosted images. This can cause issues when attempting to match attachment IDs if images are linked using outdated domains. The logic is checking for alternative domains and consider the use of 'scaled' prefixes in image URLs for large images, which might affect ID matching.
269-
*/
270-
if ( $attachment_id === 0 ) {
271-
if ( strpos( $url, 'www.' ) !== false ) {
272-
$variant_url = str_replace( 'www.', '', $url );
273-
$attachment_id = attachment_url_to_postid( $variant_url );
274-
} else {
275-
$variant_url = str_replace( '://', '://www.', $url );
276-
$attachment_id = attachment_url_to_postid( $variant_url );
308+
$attachment_id = attachment_url_to_postid( $scaled_url );
277309
}
278-
if ( $attachment_id === 0 && ! $this->is_scaled_url( $variant_url ) ) {
279-
$scaled_url = $this->get_scaled_url( $variant_url );
280310

281-
$attachment_id = attachment_url_to_postid( $scaled_url );
311+
/*
312+
* TODO: The logic is a mess, we need to refactor at some point.
313+
* Websites may transition between 'www' subdomains and apex domains, potentially breaking references to hosted images. This can cause issues when attempting to match attachment IDs if images are linked using outdated domains. The logic is checking for alternative domains and consider the use of 'scaled' prefixes in image URLs for large images, which might affect ID matching.
314+
*/
315+
if ( $attachment_id === 0 ) {
316+
if ( strpos( $url, 'www.' ) !== false ) {
317+
$variant_url = str_replace( 'www.', '', $url );
318+
$attachment_id = attachment_url_to_postid( $variant_url );
319+
} else {
320+
$variant_url = str_replace( '://', '://www.', $url );
321+
$attachment_id = attachment_url_to_postid( $variant_url );
322+
}
323+
if ( $attachment_id === 0 && ! $this->is_scaled_url( $variant_url ) ) {
324+
$scaled_url = $this->get_scaled_url( $variant_url );
325+
326+
$attachment_id = attachment_url_to_postid( $scaled_url );
327+
}
282328
}
283329
}
284330
Optml_Attachment_Cache::set_cached_attachment_id( $input_url, $attachment_id );

inc/traits/normalizer.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ public function get_unoptimized_url( $url ) {
5353
}
5454
// If the url is an uploaded image, return the url
5555
if ( Optml_Media_Offload::is_uploaded_image( $url ) ) {
56+
$pattern = '#/id:([^/]+)/((?:https?|http)://\S+)#';
57+
if ( preg_match( $pattern, $url, $matches ) ) {
58+
$url = $matches[0];
59+
}
5660
return $url;
5761
}
5862

inc/url_replacer.php

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -409,16 +409,6 @@ private function get_dam_url( Image $image ) {
409409
return $url;
410410
}
411411

412-
/**
413-
* Check if this contains the DAM flag.
414-
*
415-
* @param string $url The URL to check.
416-
*
417-
* @return bool
418-
*/
419-
private function is_dam_url( $url ) {
420-
return strpos( $url, Optml_Dam::URL_DAM_FLAG ) !== false;
421-
}
422412

423413
/**
424414
* Check if the URL is offloaded.

tests/bootstrap.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ function( $message ) {
6666
}
6767
}
6868
require dirname( dirname( __FILE__ ) ) . '/optimole-wp.php';
69+
70+
// Prevent cache clearing actions during tests to avoid errors from cache compatibility classes
71+
// This filter prevents optml_settings_updated and optml_clear_cache from being triggered
72+
add_filter( 'optml_dont_trigger_settings_updated', '__return_true' );
6973
}
7074

7175
tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );
@@ -83,6 +87,7 @@ function( $message ) {
8387
// Activate Optimole plugin
8488
activate_plugin( 'optimole-wp/optimole-wp.php' );
8589

90+
8691
// Set up the current logged in user
8792
global $current_user;
8893

tests/test-generic.php

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,157 @@ function test_domain_hash() {
4444
$this->assertEquals( $this->to_domain_hash("//www.domain.com/"), $value );
4545
$this->assertNotEquals( $this->to_domain_hash("https://something.com/"), $value );
4646
}
47+
48+
/**
49+
* Test get_unoptimized_url with non-Optimole URLs.
50+
*/
51+
function test_get_unoptimized_url_non_optimole() {
52+
// Initialize config for testing
53+
$settings = new Optml_Settings();
54+
$settings->update( 'service_data', [
55+
'cdn_key' => 'test123',
56+
'cdn_secret' => '12345',
57+
'whitelist' => [ 'example.com', 'example.org' ],
58+
] );
59+
Optml_Config::init( [
60+
'key' => 'test123',
61+
'secret' => '12345',
62+
] );
63+
64+
// Non-Optimole URLs should be returned as-is
65+
$url = 'http://example.org/wp-content/uploads/image.jpg';
66+
$this->assertEquals( $url, $this->get_unoptimized_url( $url ) );
67+
68+
$url = 'https://example.com/image.png';
69+
$this->assertEquals( $url, $this->get_unoptimized_url( $url ) );
70+
71+
$url = '/wp-content/uploads/image.jpg';
72+
$this->assertEquals( $url, $this->get_unoptimized_url( $url ) );
73+
}
74+
75+
/**
76+
* Test get_unoptimized_url with Optimole URLs (regular images).
77+
*/
78+
function test_get_unoptimized_url_optimole_regular() {
79+
// Initialize config for testing
80+
$settings = new Optml_Settings();
81+
$settings->update( 'service_data', [
82+
'cdn_key' => 'test123',
83+
'cdn_secret' => '12345',
84+
'whitelist' => [ 'example.com', 'example.org' ],
85+
] );
86+
Optml_Config::init( [
87+
'key' => 'test123',
88+
'secret' => '12345',
89+
] );
90+
91+
// Optimole URL with regular image - should extract original URL after second 'http'
92+
$optimole_url = 'https://test123.i.optimole.com/cb:test/w:800/h:600/q:mauto/http://example.org/wp-content/uploads/image.jpg';
93+
$expected = 'http://example.org/wp-content/uploads/image.jpg';
94+
$this->assertEquals( $expected, $this->get_unoptimized_url( $optimole_url ) );
95+
96+
// Optimole URL with https in original
97+
$optimole_url = 'https://test123.i.optimole.com/cb:test/w:800/h:600/q:mauto/https://example.org/wp-content/uploads/image.jpg';
98+
$expected = 'https://example.org/wp-content/uploads/image.jpg';
99+
$this->assertEquals( $expected, $this->get_unoptimized_url( $optimole_url ) );
100+
101+
// Optimole URL with query parameters
102+
$optimole_url = 'https://test123.i.optimole.com/cb:test/w:800/h:600/q:mauto/http://example.org/wp-content/uploads/image.jpg?param=value';
103+
$expected = 'http://example.org/wp-content/uploads/image.jpg?param=value';
104+
$this->assertEquals( $expected, $this->get_unoptimized_url( $optimole_url ) );
105+
}
106+
107+
/**
108+
* Test get_unoptimized_url with uploaded images (offloaded images).
109+
*/
110+
function test_get_unoptimized_url_uploaded_image() {
111+
// Initialize config for testing
112+
$settings = new Optml_Settings();
113+
$settings->update( 'service_data', [
114+
'cdn_key' => 'test123',
115+
'cdn_secret' => '12345',
116+
'whitelist' => [ 'example.com', 'example.org' ],
117+
] );
118+
Optml_Config::init( [
119+
'key' => 'test123',
120+
'secret' => '12345',
121+
] );
122+
123+
// Uploaded image URL with /id: pattern - should extract original URL
124+
$uploaded_url = 'https://test123.i.optimole.com/cb:test/w:800/h:600/q:mauto/id:abc123/http://example.org/wp-content/uploads/image.jpg';
125+
$expected = '/id:abc123/http://example.org/wp-content/uploads/image.jpg';
126+
$result = $this->get_unoptimized_url( $uploaded_url );
127+
$this->assertStringContainsString( '/id:abc123/', $result );
128+
$this->assertStringContainsString( 'http://example.org/wp-content/uploads/image.jpg', $result );
129+
130+
// Uploaded image URL with https in original
131+
$uploaded_url = 'https://test123.i.optimole.com/cb:test/w:800/h:600/q:mauto/id:xyz789/https://example.org/wp-content/uploads/image.jpg';
132+
$expected = '/id:xyz789/https://example.org/wp-content/uploads/image.jpg';
133+
$result = $this->get_unoptimized_url( $uploaded_url );
134+
$this->assertStringContainsString( '/id:xyz789/', $result );
135+
$this->assertStringContainsString( 'https://example.org/wp-content/uploads/image.jpg', $result );
136+
}
137+
138+
/**
139+
* Test get_unoptimized_url edge cases.
140+
*/
141+
function test_get_unoptimized_url_edge_cases() {
142+
// Initialize config for testing
143+
$settings = new Optml_Settings();
144+
$settings->update( 'service_data', [
145+
'cdn_key' => 'test123',
146+
'cdn_secret' => '12345',
147+
'whitelist' => [ 'example.com', 'example.org' ],
148+
] );
149+
Optml_Config::init( [
150+
'key' => 'test123',
151+
'secret' => '12345',
152+
] );
153+
154+
// Optimole URL without second 'http' - should return as-is
155+
$optimole_url = 'https://test123.i.optimole.com/cb:test/w:800/h:600/q:mauto';
156+
$this->assertEquals( $optimole_url, $this->get_unoptimized_url( $optimole_url ) );
157+
158+
// Empty string
159+
$this->assertEquals( '', $this->get_unoptimized_url( '' ) );
160+
161+
// URL with only one 'http' occurrence
162+
$optimole_url = 'https://test123.i.optimole.com/cb:test/w:800/h:600/q:mauto/image.jpg';
163+
$this->assertEquals( $optimole_url, $this->get_unoptimized_url( $optimole_url ) );
164+
165+
// Uploaded image URL without matching pattern - should return as-is
166+
$uploaded_url = 'https://test123.i.optimole.com/cb:test/w:800/h:600/q:mauto/id:abc123/image.jpg';
167+
$result = $this->get_unoptimized_url( $uploaded_url );
168+
// Should return the URL as-is since pattern doesn't match
169+
$this->assertEquals( $uploaded_url, $result );
170+
}
171+
172+
/**
173+
* Test get_unoptimized_url with custom domain configuration.
174+
*/
175+
function test_get_unoptimized_url_custom_domain() {
176+
// Initialize config with custom domain
177+
$settings = new Optml_Settings();
178+
$settings->update( 'service_data', [
179+
'cdn_key' => 'test123',
180+
'cdn_secret' => '12345',
181+
'whitelist' => [ 'example.com', 'example.org' ],
182+
'domain' => 'cdn.example.com',
183+
'is_cname_assigned' => 'yes',
184+
] );
185+
Optml_Config::init( [
186+
'key' => 'test123',
187+
'secret' => '12345',
188+
'domain' => 'cdn.example.com',
189+
] );
190+
191+
// Custom domain Optimole URL
192+
$optimole_url = 'https://cdn.example.com/cb:test/w:800/h:600/q:mauto/http://example.org/wp-content/uploads/image.jpg';
193+
$expected = 'http://example.org/wp-content/uploads/image.jpg';
194+
$this->assertEquals( $expected, $this->get_unoptimized_url( $optimole_url ) );
195+
196+
// Non-Optimole URL should still return as-is
197+
$url = 'http://example.org/wp-content/uploads/image.jpg';
198+
$this->assertEquals( $url, $this->get_unoptimized_url( $url ) );
199+
}
47200
}

0 commit comments

Comments
 (0)