Skip to content

Commit c24a3b9

Browse files
authored
gw-cid-image-handler.php: Added new snippet. (#1169)
1 parent 0b6d8f6 commit c24a3b9

File tree

1 file changed

+214
-0
lines changed

1 file changed

+214
-0
lines changed
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
<?php
2+
/**
3+
* Gravity Wiz // Gravity Forms // CID Image Handler
4+
* https://gravitywiz.com/gravity-forms-cid-image-handler/
5+
*
6+
* Automatically detect and embed any img tag with a CID:URL src in email messages. This snippet eliminates the need
7+
* to manually register each image - it automatically finds CID images and handles replacing the URL with an absolute
8+
* path for PHPMailer embedding. Supports both simple CID identifiers and full URLs.
9+
*
10+
* Usage:
11+
*
12+
* 1. [Install and activate this snippet.](https://gravitywiz.com/documentation/managing-snippets/#where-do-i-put-snippets)
13+
* 2. Use any of these formats in your email content:
14+
* - <img src="cid:my-logo" alt="Logo" />
15+
* - <img src="cid:2025/07/image.jpg" alt="Image" />
16+
* - <img src="cid:https://domain.com/wp-content/uploads/image.jpg" alt="Full URL" />
17+
* 3. Images will be automatically detected, located, and embedded inline.
18+
*
19+
* Plugin Name: GW CID Image Handler
20+
* Plugin URI: https://gravitywiz.com/gravity-forms-cid-image-handler/
21+
* Description: Automatically detect and embed any img tag with a CID:URL src in email messages. Supports both simple CID identifiers and full URLs.
22+
* Author: Gravity Wiz
23+
* Version: 1.0
24+
* Author URI: https://gravitywiz.com
25+
*/
26+
class GW_CID_Image_Handler {
27+
28+
private static $_instance = null;
29+
30+
public static function get_instance() {
31+
if ( self::$_instance === null ) {
32+
self::$_instance = new self;
33+
}
34+
return self::$_instance;
35+
}
36+
37+
private function __construct() {
38+
add_action( 'init', array( $this, 'init' ) );
39+
}
40+
41+
public function init() {
42+
// Hook into PHPMailer to process CID images
43+
add_action( 'phpmailer_init', array( $this, 'process_cid_images' ) );
44+
}
45+
46+
/**
47+
* Process CID images in PHPMailer
48+
*
49+
* @param PHPMailer $phpmailer The PHPMailer instance
50+
*/
51+
public function process_cid_images( $phpmailer ) {
52+
// Skip if no HTML body is set
53+
if ( empty( $phpmailer->Body ) ) {
54+
return;
55+
}
56+
57+
// Find all img tags with cid: sources using regex (supports full URLs)
58+
preg_match_all( '/<img[^>]*src=["\']cid:([^"\']*)["\'][^>]*>/i', $phpmailer->Body, $matches, PREG_SET_ORDER );
59+
60+
if ( empty( $matches ) ) {
61+
return;
62+
}
63+
64+
// Ensure HTML email (GF sends HTML, but this is a safeguard)
65+
if ( ! $phpmailer->ContentType || stripos( $phpmailer->ContentType, 'text/plain' ) !== false ) {
66+
$phpmailer->isHTML( true );
67+
}
68+
69+
foreach ( $matches as $match ) {
70+
$full_img_tag = $match[0];
71+
$original_cid = $match[1];
72+
73+
// Try to find the absolute path for this CID
74+
$path = $this->find_cid_image_path( $original_cid );
75+
76+
if ( $path && file_exists( $path ) ) {
77+
$basename = basename( $path );
78+
$mime = mime_content_type( $path );
79+
80+
// For full URLs, we need to create a simpler CID identifier for embedding
81+
// but keep the original CID in the HTML as-is
82+
if ( filter_var( $original_cid, FILTER_VALIDATE_URL ) ) {
83+
// Create a simple CID from the filename
84+
$filename_only = pathinfo( $basename, PATHINFO_FILENAME );
85+
$clean_filename = preg_replace( '/[^a-zA-Z0-9_-]/', '', $filename_only );
86+
$simple_cid = $clean_filename . '_' . substr( md5( $original_cid ), 0, 8 );
87+
88+
// Replace the original CID in the email body with our simple CID
89+
$phpmailer->Body = str_replace( 'cid:' . $original_cid, 'cid:' . $simple_cid, $phpmailer->Body );
90+
91+
// Use the simple CID for embedding
92+
$embed_cid = $simple_cid;
93+
} else {
94+
// For non-URL CIDs, use as-is
95+
$embed_cid = $original_cid;
96+
}
97+
98+
// Embed the image inline
99+
// Arguments: file path, CID, name, encoding, mime type
100+
$phpmailer->AddEmbeddedImage( $path, $embed_cid, $basename, 'base64', $mime );
101+
}
102+
}
103+
}
104+
105+
/**
106+
* Find the absolute file path for a CID image
107+
*
108+
* @param string $cid The content ID (without 'cid:' prefix)
109+
* @return string|false The absolute path if found, false otherwise
110+
*/
111+
private function find_cid_image_path( $cid ) {
112+
// Check if CID is a full URL (e.g., https://wand.local/wp-content/uploads/2025/07/lego-wizard.jpg)
113+
if ( filter_var( $cid, FILTER_VALIDATE_URL ) ) {
114+
// Parse the URL to get the path component
115+
$parsed_url = parse_url( $cid );
116+
if ( ! $parsed_url || empty( $parsed_url['path'] ) ) {
117+
return false;
118+
}
119+
120+
// Convert URL path to local file system path
121+
$url_path = $parsed_url['path'];
122+
123+
// Try different approaches to map URL path to file system path
124+
$possible_paths = array();
125+
126+
// Method 1: Direct mapping from document root
127+
if ( defined( 'ABSPATH' ) ) {
128+
$possible_paths[] = rtrim( ABSPATH, '/' ) . $url_path;
129+
}
130+
131+
// Method 2: If URL path contains wp-content, map to WP_CONTENT_DIR
132+
if ( strpos( $url_path, '/wp-content/' ) !== false ) {
133+
$content_path = substr( $url_path, strpos( $url_path, '/wp-content/' ) + 12 ); // +12 for '/wp-content/'
134+
$possible_paths[] = WP_CONTENT_DIR . '/' . ltrim( $content_path, '/' );
135+
}
136+
137+
// Method 3: If URL path contains uploads, map to uploads directory
138+
if ( strpos( $url_path, '/uploads/' ) !== false ) {
139+
$uploads = wp_upload_dir();
140+
$upload_path = substr( $url_path, strpos( $url_path, '/uploads/' ) + 9 ); // +9 for '/uploads/'
141+
$possible_paths[] = trailingslashit( $uploads['basedir'] ) . ltrim( $upload_path, '/' );
142+
}
143+
144+
// Check each possible path
145+
foreach ( $possible_paths as $path ) {
146+
if ( file_exists( $path ) ) {
147+
return $path;
148+
}
149+
}
150+
151+
// Fallback: try the filename search with the basename from URL
152+
$cid = basename( $url_path );
153+
}
154+
155+
// Original logic for non-URL CIDs or when URL mapping fails
156+
$uploads = wp_upload_dir();
157+
$relative_path = ltrim( $cid, '/' );
158+
159+
// Try direct path from uploads directory
160+
$path = trailingslashit( $uploads['basedir'] ) . $relative_path;
161+
if ( file_exists( $path ) ) {
162+
return $path;
163+
}
164+
165+
// Try with wp-content/uploads prefix
166+
$relative = '/uploads/' . $relative_path;
167+
$path = trailingslashit( WP_CONTENT_DIR ) . ltrim( $relative, '/' );
168+
if ( file_exists( $path ) ) {
169+
return $path;
170+
}
171+
172+
// Try searching for the filename in common WordPress directories
173+
$filename = basename( $cid );
174+
$search_paths = array(
175+
$uploads['basedir'],
176+
trailingslashit( WP_CONTENT_DIR ) . 'uploads',
177+
get_template_directory() . '/images',
178+
get_stylesheet_directory() . '/images',
179+
);
180+
181+
foreach ( $search_paths as $search_path ) {
182+
if ( ! is_dir( $search_path ) ) {
183+
continue;
184+
}
185+
186+
// Search recursively for the filename
187+
$iterator = new RecursiveIteratorIterator(
188+
new RecursiveDirectoryIterator( $search_path, RecursiveDirectoryIterator::SKIP_DOTS )
189+
);
190+
191+
foreach ( $iterator as $file ) {
192+
if ( $file->isFile() && $file->getFilename() === $filename ) {
193+
return $file->getPathname();
194+
}
195+
}
196+
}
197+
198+
// If all else fails, try to resolve it as an absolute path
199+
if ( file_exists( $cid ) ) {
200+
return $cid;
201+
}
202+
203+
return false;
204+
}
205+
}
206+
207+
# Configuration
208+
209+
// Initialize the CID Image Handler
210+
function gw_cid_image_handler() {
211+
return GW_CID_Image_Handler::get_instance();
212+
}
213+
214+
gw_cid_image_handler();

0 commit comments

Comments
 (0)