Skip to content

Commit 03a08e3

Browse files
Merge pull request #46628 from nextcloud/backport/46419/stable28
[stable28] fix(mail): Fix big logos in mail templates for Outlook
2 parents cd24a86 + b087e14 commit 03a08e3

File tree

13 files changed

+151
-5
lines changed

13 files changed

+151
-5
lines changed

apps/settings/tests/Mailer/NewUserMailHelperTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ protected function setUp(): void {
8585
$this->defaults,
8686
$this->urlGenerator,
8787
$this->l10nFactory,
88+
null,
89+
null,
8890
'test.TestTemplate',
8991
[]
9092
);

apps/theming/lib/ImageManager.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,10 @@ public function delete(string $key): void {
209209
} catch (NotFoundException $e) {
210210
} catch (NotPermittedException $e) {
211211
}
212+
213+
if ($key === 'logo') {
214+
$this->config->deleteAppValue('theming', 'logoDimensions');
215+
}
212216
}
213217

214218
public function updateImage(string $key, string $tmpFile): string {
@@ -272,6 +276,25 @@ public function updateImage(string $key, string $tmpFile): string {
272276

273277
$target->putContent(file_get_contents($tmpFile));
274278

279+
if ($key === 'logo') {
280+
$content = file_get_contents($tmpFile);
281+
$newImage = @imagecreatefromstring($content);
282+
if ($newImage !== false) {
283+
$this->config->setAppValue('theming', 'logoDimensions', imagesx($newImage) . 'x' . imagesy($newImage));
284+
} elseif (str_starts_with($detectedMimeType, 'image/svg')) {
285+
$matched = preg_match('/viewbox=["\']\d* \d* (\d*\.?\d*) (\d*\.?\d*)["\']/i', $content, $matches);
286+
if ($matched) {
287+
$this->config->setAppValue('theming', 'logoDimensions', $matches[1] . 'x' . $matches[2]);
288+
} else {
289+
$this->logger->warning('Could not read logo image dimensions to optimize for mail header');
290+
$this->config->deleteAppValue('theming', 'logoDimensions');
291+
}
292+
} else {
293+
$this->logger->warning('Could not read logo image dimensions to optimize for mail header');
294+
$this->config->deleteAppValue('theming', 'logoDimensions');
295+
}
296+
}
297+
275298
return $detectedMimeType;
276299
}
277300

lib/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,6 +1655,7 @@
16551655
'OC\\Repair\\RemoveLinkShares' => $baseDir . '/lib/private/Repair/RemoveLinkShares.php',
16561656
'OC\\Repair\\RepairDavShares' => $baseDir . '/lib/private/Repair/RepairDavShares.php',
16571657
'OC\\Repair\\RepairInvalidShares' => $baseDir . '/lib/private/Repair/RepairInvalidShares.php',
1658+
'OC\\Repair\\RepairLogoDimension' => $baseDir . '/lib/private/Repair/RepairLogoDimension.php',
16581659
'OC\\Repair\\RepairMimeTypes' => $baseDir . '/lib/private/Repair/RepairMimeTypes.php',
16591660
'OC\\RichObjectStrings\\Validator' => $baseDir . '/lib/private/RichObjectStrings/Validator.php',
16601661
'OC\\Route\\CachingRouter' => $baseDir . '/lib/private/Route/CachingRouter.php',

lib/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,6 +1688,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
16881688
'OC\\Repair\\RemoveLinkShares' => __DIR__ . '/../../..' . '/lib/private/Repair/RemoveLinkShares.php',
16891689
'OC\\Repair\\RepairDavShares' => __DIR__ . '/../../..' . '/lib/private/Repair/RepairDavShares.php',
16901690
'OC\\Repair\\RepairInvalidShares' => __DIR__ . '/../../..' . '/lib/private/Repair/RepairInvalidShares.php',
1691+
'OC\\Repair\\RepairLogoDimension' => __DIR__ . '/../../..' . '/lib/private/Repair/RepairLogoDimension.php',
16911692
'OC\\Repair\\RepairMimeTypes' => __DIR__ . '/../../..' . '/lib/private/Repair/RepairMimeTypes.php',
16921693
'OC\\RichObjectStrings\\Validator' => __DIR__ . '/../../..' . '/lib/private/RichObjectStrings/Validator.php',
16931694
'OC\\Route\\CachingRouter' => __DIR__ . '/../../..' . '/lib/private/Route/CachingRouter.php',

lib/private/Mail/EMailTemplate.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ class EMailTemplate implements IEMailTemplate {
106106
<tbody>
107107
<tr style="padding:0;text-align:left;vertical-align:top">
108108
<center data-parsed="" style="background-color:%s;min-width:175px;max-height:175px; padding:35px 0px;border-radius:200px">
109-
<img class="logo float-center" src="%s" alt="%s" align="center" style="-ms-interpolation-mode:bicubic;clear:both;display:block;float:none;margin:0 auto;outline:0;text-align:center;text-decoration:none;max-height:105px;max-width:105px;width:auto;height:auto">
109+
<img class="logo float-center" src="%s" alt="%s" align="center" style="-ms-interpolation-mode:bicubic;clear:both;display:block;float:none;margin:0 auto;outline:0;text-align:center;text-decoration:none;max-height:105px;max-width:105px;width:auto;height:auto"%s>
110110
</center>
111111
</tr>
112112
</tbody>
@@ -338,6 +338,8 @@ public function __construct(
338338
protected Defaults $themingDefaults,
339339
protected IURLGenerator $urlGenerator,
340340
protected IFactory $l10nFactory,
341+
protected ?int $logoWidth,
342+
protected ?int $logoHeight,
341343
protected string $emailId,
342344
protected array $data,
343345
) {
@@ -360,8 +362,14 @@ public function addHeader(): void {
360362
}
361363
$this->headerAdded = true;
362364

365+
$logoSizeDimensions = '';
366+
if ($this->logoWidth && $this->logoHeight) {
367+
// Provide a logo size when we have the dimensions so that it displays nicely in Outlook
368+
$logoSizeDimensions = ' width="' . $this->logoWidth . '" height="' . $this->logoHeight . '"';
369+
}
370+
363371
$logoUrl = $this->urlGenerator->getAbsoluteURL($this->themingDefaults->getLogo(false));
364-
$this->htmlBody .= vsprintf($this->header, [$this->themingDefaults->getDefaultColorPrimary(), $logoUrl, $this->themingDefaults->getName()]);
372+
$this->htmlBody .= vsprintf($this->header, [$this->themingDefaults->getDefaultColorPrimary(), $logoUrl, $this->themingDefaults->getName(), $logoSizeDimensions]);
365373
}
366374

367375
/**

lib/private/Mail/Mailer.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@
7979
* @package OC\Mail
8080
*/
8181
class Mailer implements IMailer {
82+
// Do not move this block or change it's content without contacting the release crew
83+
public const DEFAULT_DIMENSIONS = '252x120';
84+
// Do not move this block or change it's content without contacting the release crew
85+
86+
public const MAX_LOGO_SIZE = 105;
87+
8288
private ?MailerInterface $instance = null;
8389

8490
public function __construct(
@@ -136,10 +142,37 @@ public function createEMailTemplate(string $emailId, array $data = []): IEMailTe
136142
);
137143
}
138144

145+
$logoDimensions = $this->config->getAppValue('theming', 'logoDimensions', self::DEFAULT_DIMENSIONS);
146+
if (str_contains($logoDimensions, 'x')) {
147+
[$width, $height] = explode('x', $logoDimensions);
148+
$width = (int) $width;
149+
$height = (int) $height;
150+
151+
if ($width > self::MAX_LOGO_SIZE || $height > self::MAX_LOGO_SIZE) {
152+
if ($width === $height) {
153+
$logoWidth = self::MAX_LOGO_SIZE;
154+
$logoHeight = self::MAX_LOGO_SIZE;
155+
} elseif ($width > $height) {
156+
$logoWidth = self::MAX_LOGO_SIZE;
157+
$logoHeight = (int) (($height / $width) * self::MAX_LOGO_SIZE);
158+
} else {
159+
$logoWidth = (int) (($width / $height) * self::MAX_LOGO_SIZE);
160+
$logoHeight = self::MAX_LOGO_SIZE;
161+
}
162+
} else {
163+
$logoWidth = $width;
164+
$logoHeight = $height;
165+
}
166+
} else {
167+
$logoWidth = $logoHeight = null;
168+
}
169+
139170
return new EMailTemplate(
140171
$this->defaults,
141172
$this->urlGenerator,
142173
$this->l10nFactory,
174+
$logoWidth,
175+
$logoHeight,
143176
$emailId,
144177
$data
145178
);

lib/private/Repair.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
use OC\Repair\RemoveLinkShares;
7979
use OC\Repair\RepairDavShares;
8080
use OC\Repair\RepairInvalidShares;
81+
use OC\Repair\RepairLogoDimension;
8182
use OC\Repair\RepairMimeTypes;
8283
use OC\Template\JSCombiner;
8384
use OCA\DAV\Migration\DeleteSchedulingObjects;
@@ -213,6 +214,7 @@ public static function getRepairSteps(): array {
213214
\OCP\Server::get(AddMissingSecretJob::class),
214215
\OCP\Server::get(AddRemoveOldTasksBackgroundJob::class),
215216
\OCP\Server::get(AddMetadataGenerationJob::class),
217+
\OCP\Server::get(RepairLogoDimension::class),
216218
];
217219
}
218220

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
namespace OC\Repair;
10+
11+
use OCA\Theming\ImageManager;
12+
use OCP\IConfig;
13+
use OCP\Migration\IOutput;
14+
use OCP\Migration\IRepairStep;
15+
use OCP\Server;
16+
17+
class RepairLogoDimension implements IRepairStep {
18+
public function __construct(
19+
protected IConfig $config,
20+
) {
21+
}
22+
23+
public function getName(): string {
24+
return 'Cache logo dimension to fix size in emails on Outlook';
25+
}
26+
27+
public function run(IOutput $output): void {
28+
$logoDimensions = $this->config->getAppValue('theming', 'logoDimensions');
29+
if (preg_match('/^\d+x\d+$/', $logoDimensions)) {
30+
$output->info('Logo dimensions are already known');
31+
return;
32+
}
33+
34+
try {
35+
/** @var ImageManager $imageManager */
36+
$imageManager = Server::get(ImageManager::class);
37+
} catch (\Throwable) {
38+
$output->info('Theming is disabled');
39+
return;
40+
}
41+
42+
if (!$imageManager->hasImage('logo')) {
43+
$output->info('Theming is not used to provide a logo');
44+
return;
45+
}
46+
47+
$simpleFile = $imageManager->getImage('logo', false);
48+
49+
$image = @imagecreatefromstring($simpleFile->getContent());
50+
51+
$dimensions = '';
52+
if ($image !== false) {
53+
$dimensions = imagesx($image) . 'x' . imagesy($image);
54+
} elseif (str_starts_with($simpleFile->getMimeType(), 'image/svg')) {
55+
$matched = preg_match('/viewbox=["\']\d* \d* (\d*\.?\d*) (\d*\.?\d*)["\']/i', $simpleFile->getContent(), $matches);
56+
if ($matched) {
57+
$dimensions = $matches[1] . 'x' . $matches[2];
58+
}
59+
}
60+
61+
if (!$dimensions) {
62+
$output->warning('Failed to read dimensions from logo');
63+
$this->config->deleteAppValue('theming', 'logoDimensions');
64+
return;
65+
}
66+
67+
$dimensions = imagesx($image) . 'x' . imagesy($image);
68+
$this->config->setAppValue('theming', 'logoDimensions', $dimensions);
69+
$output->info('Updated logo dimensions: ' . $dimensions);
70+
}
71+
}

tests/data/emails/new-account-email-custom.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<tbody>
2424
<tr style="padding:0;text-align:left;vertical-align:top">
2525
<center data-parsed="" style="background-color:#0082c9;min-width:175px;max-height:175px; padding:35px 0px;border-radius:200px">
26-
<img class="logo float-center" src="https://example.org/img/logo-mail-header.png" alt="TestCloud" align="center" style="-ms-interpolation-mode:bicubic;clear:both;display:block;float:none;margin:0 auto;outline:0;text-align:center;text-decoration:none;max-height:105px;max-width:105px;width:auto;height:auto">
26+
<img class="logo float-center" src="https://example.org/img/logo-mail-header.png" alt="TestCloud" align="center" style="-ms-interpolation-mode:bicubic;clear:both;display:block;float:none;margin:0 auto;outline:0;text-align:center;text-decoration:none;max-height:105px;max-width:105px;width:auto;height:auto" width="252" height="120">
2727
</center>
2828
</tr>
2929
</tbody>

tests/data/emails/new-account-email-single-button.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<tbody>
2424
<tr style="padding:0;text-align:left;vertical-align:top">
2525
<center data-parsed="" style="background-color:#0082c9;min-width:175px;max-height:175px; padding:35px 0px;border-radius:200px">
26-
<img class="logo float-center" src="https://example.org/img/logo-mail-header.png" alt="TestCloud" align="center" style="-ms-interpolation-mode:bicubic;clear:both;display:block;float:none;margin:0 auto;outline:0;text-align:center;text-decoration:none;max-height:105px;max-width:105px;width:auto;height:auto">
26+
<img class="logo float-center" src="https://example.org/img/logo-mail-header.png" alt="TestCloud" align="center" style="-ms-interpolation-mode:bicubic;clear:both;display:block;float:none;margin:0 auto;outline:0;text-align:center;text-decoration:none;max-height:105px;max-width:105px;width:auto;height:auto" width="252" height="120">
2727
</center>
2828
</tr>
2929
</tbody>

0 commit comments

Comments
 (0)