Make WordPress Core

Changeset 60698 for trunk


Ignore:
Timestamp:
09/01/2025 04:02:25 AM (6 weeks 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.