Skip to content

Commit 650f757

Browse files
authored
Merge pull request #1008 from Codeinwp/fix/dam-replacer
Enhance image URL handling when the image is from DAM inside the editor
2 parents e9a840a + 02ce6f2 commit 650f757

6 files changed

Lines changed: 213 additions & 10 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: 48 additions & 0 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,6 +288,19 @@ private function attachment_url_to_post_id( $input_url ) {
253288
return (int) $cached;
254289
}
255290

291+
if ( Optml_Media_Offload::is_uploaded_image( $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+
if ( $attachment_id !== 0 ) {
297+
Optml_Attachment_Cache::set_cached_attachment_id( $input_url, $attachment_id );
298+
299+
return $attachment_id;
300+
}
301+
}
302+
}
303+
256304
$url = $this->strip_image_size( $input_url );
257305

258306
$attachment_id = attachment_url_to_postid( $url );

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)