mirror of
https://github.com/nextcloud/server.git
synced 2026-10-24 12:17:23 +02:00
The reason for this is that the change that introduced the embedded logo broke the logo for all apps that did not call useTemplate. This change uses the logo url by default and replaces it with cid:logo only when the image is actually attached to the mail. Signed-off-by: Salvatore Martire <4652631+salmart-dev@users.noreply.github.com> Assisted-by: ClaudeCode:claude-opus-5-5
343 lines
8.7 KiB
PHP
343 lines
8.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
/**
|
|
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
|
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
namespace OC\Mail;
|
|
|
|
use OCP\Mail\Headers\AutoSubmitted;
|
|
use OCP\Mail\IAttachment;
|
|
use OCP\Mail\IEMailTemplate;
|
|
use OCP\Mail\IMessage;
|
|
use Symfony\Component\Mime\Address;
|
|
use Symfony\Component\Mime\Email;
|
|
use Symfony\Component\Mime\Exception\InvalidArgumentException;
|
|
use Symfony\Component\Mime\Exception\RfcComplianceException;
|
|
|
|
/**
|
|
* Class Message provides a wrapper around Symfony\Component\Mime\Email (Used to be around SwiftMail)
|
|
*
|
|
* @package OC\Mail
|
|
*/
|
|
class Message implements IMessage {
|
|
private array $to = [];
|
|
private array $from = [];
|
|
private array $replyTo = [];
|
|
private array $cc = [];
|
|
private array $bcc = [];
|
|
|
|
public function __construct(
|
|
private Email $symfonyEmail,
|
|
private bool $plainTextOnly,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* @since 13.0.0
|
|
* @return $this
|
|
*/
|
|
#[\Override]
|
|
public function attach(IAttachment $attachment): IMessage {
|
|
/** @var Attachment $attachment */
|
|
$attachment->attach($this->symfonyEmail);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Can be used to "attach content inline" as message parts with specific MIME type and encoding.
|
|
* {@inheritDoc}
|
|
* @since 26.0.0
|
|
*/
|
|
#[\Override]
|
|
public function attachInline(string $body, string $name, ?string $contentType = null): IMessage {
|
|
# To be sure this works with iCalendar messages, we encode with 8bit instead of
|
|
# quoted-printable encoding. We save the current encoder, replace the current
|
|
# encoder with an 8bit encoder and after we've finished, we reset the encoder
|
|
# to the previous one. Originally intended to be added after the message body,
|
|
# as it is curently unknown if all mail clients handle this properly if added
|
|
# before.
|
|
$this->symfonyEmail->embed($body, $name, $contentType);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Converts the [['displayName' => 'email'], ['displayName2' => 'email2']] arrays to valid Adresses
|
|
*
|
|
* @param array $addresses Array of mail addresses
|
|
* @return Address[]
|
|
* @throws RfcComplianceException|InvalidArgumentException
|
|
*/
|
|
protected function convertAddresses(array $addresses): array {
|
|
$convertedAddresses = [];
|
|
|
|
if (empty($addresses)) {
|
|
return [];
|
|
}
|
|
|
|
array_walk($addresses, function ($readableName, $email) use (&$convertedAddresses): void {
|
|
if (is_numeric($email)) {
|
|
$convertedAddresses[] = new Address($readableName);
|
|
} else {
|
|
$convertedAddresses[] = new Address($email, $readableName);
|
|
}
|
|
});
|
|
|
|
return $convertedAddresses;
|
|
}
|
|
|
|
/**
|
|
* Set the from address of this message.
|
|
*
|
|
* If no "From" address is used \OC\Mail\Mailer will use mail_from_address and mail_domain from config.php
|
|
*
|
|
* @param array $addresses Example: array('sender@domain.org', 'other@domain.org' => 'A name')
|
|
* @return $this
|
|
*/
|
|
#[\Override]
|
|
public function setFrom(array $addresses): IMessage {
|
|
$this->from = $addresses;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get the from address of this message.
|
|
*/
|
|
public function getFrom(): array {
|
|
return $this->from;
|
|
}
|
|
|
|
/**
|
|
* Set the Reply-To address of this message
|
|
* @return $this
|
|
*/
|
|
#[\Override]
|
|
public function setReplyTo(array $addresses): IMessage {
|
|
$this->replyTo = $addresses;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Returns the Reply-To address of this message
|
|
*/
|
|
public function getReplyTo(): array {
|
|
return $this->replyTo;
|
|
}
|
|
|
|
/**
|
|
* Set the to addresses of this message.
|
|
*
|
|
* @param array $recipients Example: array('recipient@domain.org', 'other@domain.org' => 'A name')
|
|
* @return $this
|
|
*/
|
|
#[\Override]
|
|
public function setTo(array $recipients): IMessage {
|
|
$this->to = $recipients;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get the to address of this message.
|
|
*/
|
|
public function getTo(): array {
|
|
return $this->to;
|
|
}
|
|
|
|
/**
|
|
* Set the CC recipients of this message.
|
|
*
|
|
* @param array $recipients Example: array('recipient@domain.org', 'other@domain.org' => 'A name')
|
|
* @return $this
|
|
*/
|
|
#[\Override]
|
|
public function setCc(array $recipients): IMessage {
|
|
$this->cc = $recipients;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get the cc address of this message.
|
|
*/
|
|
public function getCc(): array {
|
|
return $this->cc;
|
|
}
|
|
|
|
/**
|
|
* Set the BCC recipients of this message.
|
|
*
|
|
* @param array $recipients Example: array('recipient@domain.org', 'other@domain.org' => 'A name')
|
|
* @return $this
|
|
*/
|
|
#[\Override]
|
|
public function setBcc(array $recipients): IMessage {
|
|
$this->bcc = $recipients;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get the Bcc address of this message.
|
|
*/
|
|
public function getBcc(): array {
|
|
return $this->bcc;
|
|
}
|
|
|
|
/**
|
|
* @return $this
|
|
*/
|
|
#[\Override]
|
|
public function setSubject(string $subject): IMessage {
|
|
$this->symfonyEmail->subject($subject);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get the from subject of this message.
|
|
*/
|
|
public function getSubject(): string {
|
|
return $this->symfonyEmail->getSubject() ?? '';
|
|
}
|
|
|
|
/**
|
|
* @return $this
|
|
*/
|
|
#[\Override]
|
|
public function setPlainBody(string $body): IMessage {
|
|
$this->symfonyEmail->text($body);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get the plain body of this message.
|
|
*/
|
|
public function getPlainBody(): string {
|
|
/** @var string $body */
|
|
$body = $this->symfonyEmail->getTextBody() ?? '';
|
|
return $body;
|
|
}
|
|
|
|
/**
|
|
* @return $this
|
|
*/
|
|
#[\Override]
|
|
public function setHtmlBody(string $body): IMessage {
|
|
if (!$this->plainTextOnly) {
|
|
$this->symfonyEmail->html($body);
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set the underlying Email instance
|
|
*/
|
|
public function setSymfonyEmail(Email $symfonyEmail): void {
|
|
$this->symfonyEmail = $symfonyEmail;
|
|
}
|
|
|
|
/**
|
|
* Get the underlying Email instance
|
|
*/
|
|
public function getSymfonyEmail(): Email {
|
|
return $this->symfonyEmail;
|
|
}
|
|
|
|
/**
|
|
* @return $this
|
|
*/
|
|
public function setBody(string $body, string $contentType): IMessage {
|
|
if (!$this->plainTextOnly || $contentType !== 'text/html') {
|
|
if ($contentType === 'text/html') {
|
|
$this->symfonyEmail->html($body);
|
|
} else {
|
|
$this->symfonyEmail->text($body);
|
|
}
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set the recipients on the symphony email
|
|
*
|
|
* Since
|
|
*
|
|
* setTo
|
|
* setFrom
|
|
* setReplyTo
|
|
* setCc
|
|
* setBcc
|
|
*
|
|
* could throw a \Symfony\Component\Mime\Exception\RfcComplianceException
|
|
* or a \Symfony\Component\Mime\Exception\InvalidArgumentException
|
|
* we wrap the calls here. We then have the validation errors all in one place and can
|
|
* throw shortly before \OC\Mail\Mailer::send
|
|
*
|
|
* @throws InvalidArgumentException|RfcComplianceException
|
|
*/
|
|
public function setRecipients(): void {
|
|
$this->symfonyEmail->to(...$this->convertAddresses($this->getTo()));
|
|
$this->symfonyEmail->from(...$this->convertAddresses($this->getFrom()));
|
|
$this->symfonyEmail->replyTo(...$this->convertAddresses($this->getReplyTo()));
|
|
$this->symfonyEmail->cc(...$this->convertAddresses($this->getCc()));
|
|
$this->symfonyEmail->bcc(...$this->convertAddresses($this->getBcc()));
|
|
}
|
|
|
|
/**
|
|
* @return $this
|
|
*/
|
|
#[\Override]
|
|
public function useTemplate(IEMailTemplate $emailTemplate): IMessage {
|
|
$this->setSubject($emailTemplate->renderSubject());
|
|
$this->setPlainBody($emailTemplate->renderText());
|
|
if (!$this->plainTextOnly) {
|
|
if ($emailTemplate instanceof EMailTemplate) {
|
|
$this->setHtmlBody($emailTemplate->renderHtmlWithInlineImages());
|
|
foreach ($emailTemplate->getInlineImages() as $image) {
|
|
$this->attachInline($image['content'], $image['name'], $image['mimeType']);
|
|
}
|
|
} else {
|
|
$this->setHtmlBody($emailTemplate->renderHtml());
|
|
}
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add the Auto-Submitted header to the email, preventing most automated
|
|
* responses to automated messages.
|
|
*
|
|
* @param AutoSubmitted::VALUE_* $value (one of AutoSubmitted::VALUE_NO, AutoSubmitted::VALUE_AUTO_GENERATED, AutoSubmitted::VALUE_AUTO_REPLIED)
|
|
* @return $this
|
|
*/
|
|
#[\Override]
|
|
public function setAutoSubmitted(string $value): IMessage {
|
|
$headers = $this->symfonyEmail->getHeaders();
|
|
|
|
if ($headers->has(AutoSubmitted::HEADER)) {
|
|
// if the header already exsists, remove it.
|
|
// the value can be modified with some implementations
|
|
// of the interface \Swift_Mime_Header, however the
|
|
// interface doesn't, and this makes the static-code
|
|
// analysis unhappy.
|
|
// @todo check if symfony mailer can modify the autosubmitted header
|
|
$headers->remove(AutoSubmitted::HEADER);
|
|
}
|
|
|
|
$headers->addTextHeader(AutoSubmitted::HEADER, $value);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get the current value of the Auto-Submitted header. Defaults to "no"
|
|
* which is equivalent to the header not existing at all
|
|
*/
|
|
public function getAutoSubmitted(): string {
|
|
$headers = $this->symfonyEmail->getHeaders();
|
|
|
|
return $headers->has(AutoSubmitted::HEADER)
|
|
? $headers->get(AutoSubmitted::HEADER)->getBodyAsString() : AutoSubmitted::VALUE_NO;
|
|
}
|
|
}
|