diff --git a/apps/theming/lib/ThemingDefaults.php b/apps/theming/lib/ThemingDefaults.php index 2007bf29411aa..7aeb3e7107a6f 100644 --- a/apps/theming/lib/ThemingDefaults.php +++ b/apps/theming/lib/ThemingDefaults.php @@ -296,6 +296,16 @@ public function getLogo($useSvg = true): string { return $this->urlGenerator->linkToRoute('theming.Theming.getImage', [ 'key' => 'logo', 'useSvg' => $useSvg, 'v' => $cacheBusterCounter ]); } + #[\Override] + public function getLogoImage(): ?array { + try { + $file = $this->imageManager->getImage('logo', false); + return ['content' => $file->getContent(), 'mimeType' => $file->getMimeType()]; + } catch (\Exception $e) { + return parent::getLogoImage(); + } + } + /** * Themed background image url * diff --git a/lib/private/Mail/EMailTemplate.php b/lib/private/Mail/EMailTemplate.php index ad1d2071ff8a1..46dbbab2c1490 100644 --- a/lib/private/Mail/EMailTemplate.php +++ b/lib/private/Mail/EMailTemplate.php @@ -34,6 +34,8 @@ class EMailTemplate implements IEMailTemplate { protected bool $bodyListOpened = false; /** indicated if the footer is added */ protected bool $footerAdded = false; + /** @var array images to embed inline, referenced via cid: */ + protected array $inlineImages = []; protected string $head = << @@ -355,8 +357,25 @@ public function addHeader(): void { $logoSizeDimensions = ' width="' . $this->logoWidth . '" height="' . $this->logoHeight . '"'; } - $logoUrl = $this->urlGenerator->getAbsoluteURL($this->themingDefaults->getLogo(false)); - $this->htmlBody .= vsprintf($this->header, [$this->themingDefaults->getDefaultColorPrimary(), $logoUrl, $this->themingDefaults->getName(), $logoSizeDimensions]); + $logoImage = $this->themingDefaults->getLogoImage(); + if ($logoImage !== null) { + // Embed the logo directly in the message instead of linking to it, so mail + // clients don't have to fetch it from the internet (some (e.g. gmail) block that). + $logoSrc = 'cid:logo'; + $this->inlineImages[] = ['name' => 'logo'] + $logoImage; + } else { + $logoSrc = $this->urlGenerator->getAbsoluteURL($this->themingDefaults->getLogo(false)); + } + $this->htmlBody .= vsprintf($this->header, [$this->themingDefaults->getDefaultColorPrimary(), $logoSrc, $this->themingDefaults->getName(), $logoSizeDimensions]); + } + + /** + * Images that must be embedded inline in the message, referenced via `cid:` in the HTML body + * + * @return array + */ + public function getInlineImages(): array { + return $this->inlineImages; } /** diff --git a/lib/private/Mail/Message.php b/lib/private/Mail/Message.php index b19570c9cc075..5de41c4e03495 100644 --- a/lib/private/Mail/Message.php +++ b/lib/private/Mail/Message.php @@ -292,6 +292,11 @@ public function useTemplate(IEMailTemplate $emailTemplate): IMessage { $this->setPlainBody($emailTemplate->renderText()); if (!$this->plainTextOnly) { $this->setHtmlBody($emailTemplate->renderHtml()); + if ($emailTemplate instanceof EMailTemplate) { + foreach ($emailTemplate->getInlineImages() as $image) { + $this->attachInline($image['content'], $image['name'], $image['mimeType']); + } + } } return $this; } diff --git a/lib/private/legacy/OC_Defaults.php b/lib/private/legacy/OC_Defaults.php index 0710ad1fc91ec..dae88f329a126 100644 --- a/lib/private/legacy/OC_Defaults.php +++ b/lib/private/legacy/OC_Defaults.php @@ -321,6 +321,24 @@ public function getLogo($useSvg = true) { return $logo . '?v=' . hash('sha1', implode('.', Util::getVersion())); } + /** + * Raw logo image data (raster, not SVG) for embedding directly into emails, + * so mail clients don't have to fetch it from the internet. + * + * @return array{content: string, mimeType: string}|null null when unavailable + */ + public function getLogoImage(): ?array { + if ($this->themeExist('getLogoImage')) { + return $this->theme->getLogoImage(); + } + + $content = @file_get_contents(\OC::$SERVERROOT . '/core/img/logo/logo.png'); + if ($content === false) { + return null; + } + return ['content' => $content, 'mimeType' => 'image/png']; + } + public function getTextColorPrimary() { if ($this->themeExist('getTextColorPrimary')) { return $this->theme->getTextColorPrimary(); diff --git a/lib/public/Defaults.php b/lib/public/Defaults.php index 6de22caa41ed7..ecc8e98eee04a 100644 --- a/lib/public/Defaults.php +++ b/lib/public/Defaults.php @@ -173,6 +173,16 @@ public function getLogo(bool $useSvg = true): string { return $this->defaults->getLogo($useSvg); } + /** + * Raw logo image data (raster) for embedding directly into emails + * + * @return array{content: string, mimeType: string}|null null when unavailable + * @since 35.0.0 + */ + public function getLogoImage(): ?array { + return $this->defaults->getLogoImage(); + } + /** * Returns primary color * @return string diff --git a/tests/lib/Mail/EMailTemplateTest.php b/tests/lib/Mail/EMailTemplateTest.php index 6bf225a4778a3..8bfa8670ea255 100644 --- a/tests/lib/Mail/EMailTemplateTest.php +++ b/tests/lib/Mail/EMailTemplateTest.php @@ -217,4 +217,20 @@ public function testEMailTemplateAlternativePlainTexts(): void { $expectedTXT = file_get_contents(\OC::$SERVERROOT . '/tests/data/emails/new-account-email-custom-text-alternative.txt'); $this->assertSame($expectedTXT, $this->emailTemplate->renderText()); } + + public function testEMailTemplateEmbedsLogo(): void { + $this->defaults->method('getDefaultColorPrimary')->willReturn('#0082c9'); + $this->defaults->method('getName')->willReturn('TestCloud'); + $this->defaults->method('getLogoImage') + ->willReturn(['content' => 'PNGDATA', 'mimeType' => 'image/png']); + $this->urlGenerator->expects($this->never())->method('getAbsoluteURL'); + + $this->emailTemplate->addHeader(); + + $this->assertStringContainsString('src="cid:logo"', $this->emailTemplate->renderHtml()); + $this->assertSame( + [['name' => 'logo', 'content' => 'PNGDATA', 'mimeType' => 'image/png']], + $this->emailTemplate->getInlineImages() + ); + } }