Skip to content

Email Attachments in Magento 2.4.8 #39869

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
1 of 5 tasks
fmaniconereply opened this issue Apr 28, 2025 · 13 comments
Open
1 of 5 tasks

Email Attachments in Magento 2.4.8 #39869

fmaniconereply opened this issue Apr 28, 2025 · 13 comments
Labels
Area: Framework Component: Email Issue: Confirmed Gate 3 Passed. Manual verification of the issue completed. Issue is confirmed Priority: P3 May be fixed according to the position in the backlog. Reported on 2.4.8 Indicates original Magento version for the Issue report. Reproduced on 2.4.x The issue has been reproduced on latest 2.4-develop branch Triage: Dev.Experience Issue related to Developer Experience and needs help with Triage to Confirm or Reject it

Comments

@fmaniconereply
Copy link

Preconditions and environment

  • Magento version 2.4.8

Could you explain the rationale behind having the EmailMessage class extend the Message class, rather than a class directly related to email sending, such as Mail? Additionally, since the Message class does not provide methods like attach to handle attachments, how does Magento handle email attachments in this architecture?

Steps to reproduce

Create a script for send email
use method "attach" to add attachment

Expected result

It is not possible to add an attachment.

Actual result

It is not possible to add an attachment.

Additional information

No response

Release note

No response

Triage and priority

  • Severity: S0 - Affects critical data or functionality and leaves users without workaround.
  • Severity: S1 - Affects critical data or functionality and forces users to employ a workaround.
  • Severity: S2 - Affects non-critical data or functionality and forces users to employ a workaround.
  • Severity: S3 - Affects non-critical data or functionality and does not force users to employ a workaround.
  • Severity: S4 - Affects aesthetics, professional look and feel, “quality” or “usability”.
Copy link

m2-assistant bot commented Apr 28, 2025

Hi @fmaniconereply. Thank you for your report.
To speed up processing of this issue, make sure that the issue is reproducible on the vanilla Magento instance following Steps to reproduce.


Join Magento Community Engineering Slack and ask your questions in #github channel.
⚠️ According to the Magento Contribution requirements, all issues must go through the Community Contributions Triage process. Community Contributions Triage is a public meeting.
🕙 You can find the schedule on the Magento Community Calendar page.
📞 The triage of issues happens in the queue order. If you want to speed up the delivery of your contribution, join the Community Contributions Triage session to discuss the appropriate ticket.

@fmaniconereply
Copy link
Author

A really messy solution is to do this to add an attachment:

$message = $transport->getMessage();
$symfonyEmail = $message->getSymfonyMessage();
if (!$symfonyEmail instanceof Email) {
    // Manually recreate an Email from Message
    $email = new Email();

    foreach ($symfonyEmail->getHeaders()->all() as $header) {
        $email->getHeaders()->add($header);
    }

    $email->html($symfonyEmail->getBody()->getBody());

    $emailMessage = $email;
}
$emailMessage->attach(
    $attachmentContent,
    'invoice_' . $order->getIncrementId() . '.pdf',
    'application/pdf'
);
$message->setBody($emailMessage);
$transport->sendMessage();

@engcom-November engcom-November self-assigned this Apr 29, 2025
Copy link

m2-assistant bot commented Apr 29, 2025

Hi @engcom-November. Thank you for working on this issue.
In order to make sure that issue has enough information and ready for development, please read and check the following instruction: 👇

  • 1. Verify that issue has all the required information. (Preconditions, Steps to reproduce, Expected result, Actual result).
  • 2. Verify that issue has a meaningful description and provides enough information to reproduce the issue.
  • 3. Add Area: XXXXX label to the ticket, indicating the functional areas it may be related to.
  • 4. Verify that the issue is reproducible on 2.4-develop branch
    Details- If the issue is reproducible on 2.4-develop branch, please, add the label Reproduced on 2.4.x.
    - If the issue is not reproducible, add your comment that issue is not reproducible and close the issue and stop verification process here!
  • 5. Add label Issue: Confirmed once verification is complete.
  • 6. Make sure that automatic system confirms that report has been added to the backlog.

@engcom-November engcom-November added Reported on 2.4.8 Indicates original Magento version for the Issue report. Triage: Dev.Experience Issue related to Developer Experience and needs help with Triage to Confirm or Reject it labels Apr 29, 2025
@engcom-November engcom-November removed their assignment Apr 29, 2025
@Silarn
Copy link

Silarn commented May 2, 2025

I would note that the MimePartInterface / MimeMessageInterface classes used to handle this properly but it no longer handles multi-part MimeParts.

In \Magento\Framework\Mail\MimePart it does correctly create the 'parts':

            if ($type === MimeInterface::TYPE_HTML) {
                $this->mimePart = new TextPart($content, $charset, 'html', $encoding);
            } elseif ($type === MimeInterface::TYPE_TEXT) {
                $this->mimePart = new TextPart($content, $charset, 'plain', $encoding);
            } else {
                $this->mimePart = new DataPart($content, $fileName, $type, $encoding);
            }

However, when this is converted to an actual Symfony message, only the first TextPart is used and the rest are discarded.

In \Magento\Framework\Mail\MimeMessage:

    public function __construct(array $parts)
    {
        $headers = null;
        $body = null;

        foreach ($parts as $part) {
            $mimePart = $part->getMimePart();
            if ($mimePart instanceof TextPart) {
                $headers = $mimePart->getHeaders();
                $body = $mimePart;
                break;
            }
        }

        $this->mimeMessage = new Message($headers, $body);
    }

As you can see, any TextPart types are inserted into the body and the remaining parts are discarded. Technically this does include DataPart attachments but it'll only process the first part.

To properly merge all of the parts we need to do something a bit more like the Symfony Mailer class:

    public function __construct(array $parts)
    {
        $dataParts = [];
        $headers = new Headers();
        $html = null;
        $text = null;
        /** @var MimePartInterface $part */
        foreach ($parts as $part) {
            $headers = new Headers(...$headers->all(), ...$mimePart->getHeaders()->all());
            $mimePart = $part->getMimePart();
            if ($mimePart instanceof TextPart) {
                if ($mimePart->getMediaSubtype() == 'html') {
                    $html = $mimePart;
                } elseif ($mimePart->getMediaSubtype() == 'plain') {
                    $text = $mimePart;
                } else {
                    $dataParts[] = $mimePart;
                }
            }
        }
        
        if ($html && $text) {
            $body = new AlternativePart($text, $html);
        } else if ($html) {
            $body = $html;
        } else if ($text) {
            $body = $text;
        }
        
        if ($dataParts) {
            $body = new MixedPart($body, ...$dataParts);
        }
        $this->mimeMessage = new Message($headers, $body);
    }

@Silarn
Copy link

Silarn commented May 2, 2025

I'm actually not sure that we need to include the Symfony Part headers in the Message $headers... unless we need to add additional headers to the email I'm fairly sure the Part headers will be included automatically.

Just passing in an empty Headers() may be fine.

@Silarn
Copy link

Silarn commented May 2, 2025

A really messy solution is to do this to add an attachment:

$message = $transport->getMessage();
$symfonyEmail = $message->getSymfonyMessage();
if (!$symfonyEmail instanceof Email) {
    // Manually recreate an Email from Message
    $email = new Email();

    foreach ($symfonyEmail->getHeaders()->all() as $header) {
        $email->getHeaders()->add($header);
    }

    $email->html($symfonyEmail->getBody()->getBody());

    $emailMessage = $email;
}
$emailMessage->attach(
    $attachmentContent,
    'invoice_' . $order->getIncrementId() . '.pdf',
    'application/pdf'
);
$message->setBody($emailMessage);
$transport->sendMessage();

A slightly less hacky way is to simply make use of the Symfony MixedPart:

$symfonyMessage = $message->getSymfonyMessage();
$attachment = new DataPart($attachmentContent, 'invoice_' . $order->getIncrementId() . '.pdf', 'application/octet-stream', 'base64');
$symfonyMessage->setBody(new MixedPart($symfonyMessage->getBody(), $attachment));

@Silarn
Copy link

Silarn commented May 2, 2025

Some of the other implementations in MimeMessage also look off:

 public function isMultiPart(): bool
    {
        $body = $this->mimeMessage->getBody();
        return $body instanceof AlternativePart && $body->countParts() > 1;
    }

This probably needs to check for AbstractMultipartPart rather than AlternativePart as it is the parent class of AlternativePart, MixedPart, DigestPart, etc. In addition, I don't see a 'countParts()' function, so I think this is going to fail. Probably needs to be count($body->getParts()).

Same for the getParts() function, it should check for AbstractMultipartPart rather than AlternativePart.

@engcom-Hotel engcom-Hotel self-assigned this May 5, 2025
Copy link

m2-assistant bot commented May 5, 2025

Hi @engcom-Hotel. Thank you for working on this issue.
In order to make sure that issue has enough information and ready for development, please read and check the following instruction: 👇

  • 1. Verify that issue has all the required information. (Preconditions, Steps to reproduce, Expected result, Actual result).
  • 2. Verify that issue has a meaningful description and provides enough information to reproduce the issue.
  • 3. Add Area: XXXXX label to the ticket, indicating the functional areas it may be related to.
  • 4. Verify that the issue is reproducible on 2.4-develop branch
    Details- If the issue is reproducible on 2.4-develop branch, please, add the label Reproduced on 2.4.x.
    - If the issue is not reproducible, add your comment that issue is not reproducible and close the issue and stop verification process here!
  • 5. Add label Issue: Confirmed once verification is complete.
  • 6. Make sure that automatic system confirms that report has been added to the backlog.

@engcom-Hotel
Copy link
Contributor

Hello @fmaniconereply,

Thanks for the report and collaboration!

Yes its true there is no straight way for attaching file in the mails in Magento core. Hence confirming this issue.

Thanks

@engcom-Hotel engcom-Hotel added Issue: Confirmed Gate 3 Passed. Manual verification of the issue completed. Issue is confirmed Component: Email Reproduced on 2.4.x The issue has been reproduced on latest 2.4-develop branch Priority: P3 May be fixed according to the position in the backlog. Area: Framework labels May 5, 2025
@github-jira-sync-bot
Copy link

✅ Jira issue https://jira.corp.adobe.com/browse/AC-14581 is successfully created for this GitHub issue.

Copy link

m2-assistant bot commented May 5, 2025

✅ Confirmed by @engcom-Hotel. Thank you for verifying the issue.
Issue Available: @engcom-Hotel, You will be automatically unassigned. Contributors/Maintainers can claim this issue to continue. To reclaim and continue work, reassign the ticket to yourself.

@hostep
Copy link
Contributor

hostep commented May 5, 2025

We did have a PR at one time that introduced this email attachment feature request, but for some lame reason it got rejected by internal Adobe devs. Maybe we can try again if they are willing, maybe @engcom-Hotel can first double check internally if Adobe devs are open for this feature this time?

@Silarn
Copy link

Silarn commented May 6, 2025

@hostep Not sure if this had anything to do with the switch to Symfony (which was most definitely BIC) but the above MimeMessage changes would be necessary for that PR to function as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: Framework Component: Email Issue: Confirmed Gate 3 Passed. Manual verification of the issue completed. Issue is confirmed Priority: P3 May be fixed according to the position in the backlog. Reported on 2.4.8 Indicates original Magento version for the Issue report. Reproduced on 2.4.x The issue has been reproduced on latest 2.4-develop branch Triage: Dev.Experience Issue related to Developer Experience and needs help with Triage to Confirm or Reject it
Projects
Status: Ready for Development
Development

No branches or pull requests

6 participants