Make WordPress Core

Changeset 60698


Ignore:
Timestamp:
09/01/2025 04:02:25 AM (12 months ago)
Author:
TimothyBlynJacobs
Message:

Mail: Support inline attachments.

MIME allows for referencing included attachments by their Content-ID header using the cid URL scheme. This can be used to embed images inline to the HTML message. For example, <img src="cid:logo">, will display the contents of message part with the Content-Id: <logo> header.

The wp_mail() function now supports including inline attachments through a new $embeds parameter. It accepts a map of Content-ID values to file paths. The wp_mail_embed_args filter can be used to customize the resulting PHPMailer::addEmbeddedImage method call.

Props jesin, swissspidy, chrisvendiadvertisingcom, SirLouen, mukesh27, yashjawale, iamadisingh.
Fixes #28059.

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/pluggable.php

    r60297 r60698  
    159159         * be set using the {@see 'wp_mail_charset'} filter.
    160160         *
     161         * When using the `$embeds` parameter to embed images for use in HTML emails,
     162         * reference the embedded file in your HTML with a `cid:` URL whose value
     163         * matches the file's Content-ID. By default, the Content-ID (`cid`) used for
     164         * each embedded file is the key in the embeds array, unless modified via the
     165         * {@see 'wp_mail_embed_args'} filter. For example:
     166         *
     167         * `<img src="cid:0" alt="Logo">`
     168         * `<img src="cid:my-image" alt="Image">`
     169         *
     170         * You may also customize the Content-ID for each file by using the
     171         * {@see 'wp_mail_embed_args'} filter and setting the `cid` value.
     172         *
    161173         * @since 1.2.1
    162174         * @since 5.5.0 is_email() is used for email validation,
    163175         *              instead of PHPMailer's default validator.
     176         * @since 6.9.0 Added $embeds parameter.
    164177         *
    165178         * @global PHPMailer\PHPMailer\PHPMailer $phpmailer
     
    170183         * @param string|string[] $headers     Optional. Additional headers.
    171184         * @param string|string[] $attachments Optional. Paths to files to attach.
     185         * @param string|string[] $embeds      Optional. Paths to files to embed.
    172186         * @return bool Whether the email was sent successfully.
    173187         */
    174         function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() ) {
     188        function wp_mail( $to, $subject, $message, $headers = '', $attachments = array(), $embeds = array() ) {
    175189                // Compact the input, apply the filters, and extract them back out.
    176190
     
    188202                 *     @type string|string[] $headers     Additional headers.
    189203                 *     @type string|string[] $attachments Paths to files to attach.
     204                 *     @type string|string[] $embeds      Paths to files to embed.
    190205                 * }
    191206                 */
    192                 $atts = apply_filters( 'wp_mail', compact( 'to', 'subject', 'message', 'headers', 'attachments' ) );
     207                $atts = apply_filters( 'wp_mail', compact( 'to', 'subject', 'message', 'headers', 'attachments', 'embeds' ) );
    193208
    194209                /**
     
    210225                 *     @type string|string[] $headers     Additional headers.
    211226                 *     @type string|string[] $attachments Paths to files to attach.
     227                 *     @type string|string[] $embeds      Paths to files to embed.
    212228                 * }
    213229                 */
     
    245261                        $attachments = explode( "\n", str_replace( "\r\n", "\n", $attachments ) );
    246262                }
     263
     264                if ( isset( $atts['embeds'] ) ) {
     265                        $embeds = $atts['embeds'];
     266                }
     267
     268                if ( ! is_array( $embeds ) ) {
     269                        $embeds = explode( "\n", str_replace( "\r\n", "\n", $embeds ) );
     270                }
     271
    247272                global $phpmailer;
    248273
     
    532557                }
    533558
     559                if ( ! empty( $embeds ) ) {
     560                        foreach ( $embeds as $key => $embed_path ) {
     561                                /**
     562                                 * Filters the arguments for PHPMailer's addEmbeddedImage() method.
     563                                 *
     564                                 * @since 6.9.0
     565                                 *
     566                                 * @param array $args {
     567                                 *     An array of arguments for `addEmbeddedImage()`.
     568                                 *     @type string $path        The path to the file.
     569                                 *     @type string $cid         The Content-ID of the image. Default: The key in the embeds array.
     570                                 *     @type string $name        The filename of the image.
     571                                 *     @type string $encoding    The encoding of the image. Default: 'base64'.
     572                                 *     @type string $type        The MIME type of the image. Default: empty string, which lets PHPMailer auto-detect.
     573                                 *     @type string $disposition The disposition of the image. Default: 'inline'.
     574                                 * }
     575                                 */
     576                                $embed_args = apply_filters(
     577                                        'wp_mail_embed_args',
     578                                        array(
     579                                                'path'        => $embed_path,
     580                                                'cid'         => (string) $key,
     581                                                'name'        => basename( $embed_path ),
     582                                                'encoding'    => 'base64',
     583                                                'type'        => '',
     584                                                'disposition' => 'inline',
     585                                        )
     586                                );
     587
     588                                try {
     589                                        $phpmailer->addEmbeddedImage(
     590                                                $embed_args['path'],
     591                                                $embed_args['cid'],
     592                                                $embed_args['name'],
     593                                                $embed_args['encoding'],
     594                                                $embed_args['type'],
     595                                                $embed_args['disposition']
     596                                        );
     597                                } catch ( PHPMailer\PHPMailer\Exception $e ) {
     598                                        continue;
     599                                }
     600                        }
     601                }
     602
    534603                /**
    535604                 * Fires after PHPMailer is initialized.
     
    564633                         *     @type string[] $headers     Additional headers.
    565634                         *     @type string[] $attachments Paths to files to attach.
     635                         *     @type string[] $embeds      Paths to files to embed.
    566636                         * }
    567637                         */
  • trunk/tests/phpunit/tests/pluggable/signatures.php

    r59828 r60698  
    138138                                'headers'     => '',
    139139                                'attachments' => array(),
     140                                'embeds'      => array(),
    140141                        ),
    141142                        'wp_authenticate'                 => array( 'username', 'password' ),
  • trunk/tests/phpunit/tests/pluggable/wpMail.php

    r58097 r60698  
    555555                $this->assertNotSame( 'user1', $phpmailer->AltBody );
    556556        }
     557
     558        /**
     559         * Test that wp_mail() can send embedded images.
     560         *
     561         * @ticket 28059
     562         * @covers ::wp_mail
     563         */
     564        public function test_wp_mail_can_send_embedded_images() {
     565                $embeds = array(
     566                        'canola' => DIR_TESTDATA . '/images/canola.jpg',
     567                        DIR_TESTDATA . '/images/test-image-2.gif',
     568                        DIR_TESTDATA . '/images/avif-lossy.avif',
     569                );
     570
     571                $message = '';
     572                foreach ( $embeds as $key => $path ) {
     573                        $message .= '<p><img src="cid:' . $key . '" alt="" /></p>';
     574                }
     575
     576                wp_mail(
     577                        'user@example.org',
     578                        'Embedded images test',
     579                        $message,
     580                        'Content-Type: text/html',
     581                        array(),
     582                        $embeds
     583                );
     584
     585                $mailer      = tests_retrieve_phpmailer_instance();
     586                $attachments = $mailer->getAttachments();
     587
     588                foreach ( $attachments as $attachment ) {
     589                        $inline_embed_exists = in_array( $attachment[0], $embeds, true ) && 'inline' === $attachment[6];
     590                        $this->assertTrue( $inline_embed_exists, 'The attachment ' . $attachment[2] . ' is not inline in the embeds array.' );
     591                }
     592                foreach ( $embeds as $key => $path ) {
     593                        $this->assertStringContainsString( 'cid:' . $key, $mailer->get_sent()->body, 'The cid ' . $key . ' is not referenced in the mail body.' );
     594                }
     595        }
     596        /**
     597         * Test that wp_mail() can send embedded images as a multiple line string.
     598         *
     599         * @ticket 28059
     600         * @covers ::wp_mail
     601         */
     602        public function test_wp_mail_string_embeds() {
     603                $embeds  = DIR_TESTDATA . '/images/canola.jpg' . "\n";
     604                $embeds .= DIR_TESTDATA . '/images/test-image-2.gif';
     605
     606                $message = '<p><img src="cid:0" alt="" /></p><p><img src="cid:1" alt="" /></p>';
     607
     608                wp_mail(
     609                        'user@example.org',
     610                        'Embedded images test',
     611                        $message,
     612                        'Content-Type: text/html',
     613                        array(),
     614                        $embeds
     615                );
     616
     617                $embeds_array = explode( "\n", $embeds );
     618                $mailer       = tests_retrieve_phpmailer_instance();
     619                $attachments  = $mailer->getAttachments();
     620
     621                foreach ( $attachments as $attachment ) {
     622                        $inline_embed_exists = in_array( $attachment[0], $embeds_array, true ) && 'inline' === $attachment[6];
     623                        $this->assertTrue( $inline_embed_exists, 'The attachment ' . $attachment[2] . ' is not inline in the embeds array.' );
     624                }
     625                foreach ( $embeds_array as $key => $path ) {
     626                        $this->assertStringContainsString( 'cid:' . $key, $mailer->get_sent()->body, 'The cid ' . $key . ' is not referenced in the mail body.' );
     627                }
     628        }
    557629}
Note: See TracChangeset for help on using the changeset viewer.