Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public function send(VEvent $vevent,
$fromEMail = Util::getDefaultEmailAddress('reminders-noreply');

$template = $this->mailer->createEMailTemplate('dav.calendarReminder');
$template->setLanguage($l10n->getLanguageCode());
$template->addHeader();
$this->addSubjectAndHeading($template, $l10n, $vevent);
$this->addEventDetails($template, $l10n, $calendarDisplayName ?? $this->getCalendarDisplayNameFallback($lang), $vevent);
Expand Down
1 change: 1 addition & 0 deletions apps/dav/lib/CalDAV/Schedule/IMipPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ public function schedule(Message $iTipMessage) {
$fromName = $this->imipService->getFrom($senderName, $this->defaults->getName());

$template = $this->mailer->createEMailTemplate('dav.calendarInvite.' . $method, $data);
$template->setLanguage($this->imipService->getLanguageCode());
$template->addHeader();
$template->addBodySender($data['invitee_name'], $senderName !== null ? $sender : '');

Expand Down
7 changes: 7 additions & 0 deletions apps/dav/lib/CalDAV/Schedule/IMipService.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ public function getFrom(?string $senderName, string $default): string {
return $this->l10n->t('%1$s via %2$s', [$senderName, $default]);
}

/**
* Language of the attendee the email is written for
*/
public function getLanguageCode(): string {
return $this->l10n->getLanguageCode();
}

public static function readPropertyWithDefault(VEvent $vevent, string $property, string $default) {
if (isset($vevent->$property)) {
$value = $vevent->$property->getValue();
Expand Down
1 change: 1 addition & 0 deletions apps/dav/lib/Listener/CalendarDelegateActionListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ private function sendNotification(
'calendar' => $calendarName,
'event' => $summary,
]);
$template->setLanguage($l->getLanguageCode());
$template->addHeader();
$template->setSubject($subject);
$template->addBodySender($actorName, $actor->getEMailAddress() ?? '');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,16 @@ public function testSendWithAttendeesWhenOwnerIsOrganizer(): void {
->willReturnArgument(0);
$enL10N->method('l')
->willReturnArgument(0);
$enL10N->method('getLanguageCode')
->willReturn('en');

$deL10N = $this->createMock(IL10N::class);
$deL10N->method('t')
->willReturnArgument(0);
$deL10N->method('l')
->willReturnArgument(0);
$deL10N->method('getLanguageCode')
->willReturn('de');

$this->l10nFactory
->method('getUserLanguage')
Expand Down Expand Up @@ -181,6 +185,12 @@ public function testSendWithAttendeesWhenOwnerIsOrganizer(): void {
$message21 = $this->getMessageMock('foo3@example.org', $template2);
$message22 = $this->getMessageMock('foo4@example.org', $template2);
$message23 = $this->getMessageMock('uid1@example.com', $template2);
$template1->expects($this->once())
->method('setLanguage')
->with('de');
$template2->expects($this->once())
->method('setLanguage')
->with('en');

$this->mailer->expects(self::exactly(2))
->method('createEMailTemplate')
Expand Down
4 changes: 4 additions & 0 deletions apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ public function testParsingSingle(): void {
->willReturn($this->user);
$this->service->expects(self::once())
->method('getFrom');
$this->service->method('getLanguageCode')->willReturn('ar');
$this->emailTemplate->expects(self::once())
->method('setLanguage')
->with('ar');
$this->emailTemplate->expects(self::once())
->method('addBodySender')
->with('Mr. Wizard', 'gandalf@wiz.ard');
Expand Down
6 changes: 6 additions & 0 deletions apps/dav/tests/unit/CalDAV/Schedule/IMipServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,12 @@ public function testGetFrom(): void {
$this->assertEquals($expected, $actual);
}

public function testGetLanguageCode(): void {
$this->l10n->method('getLanguageCode')->willReturn('ar');

$this->assertEquals('ar', $this->service->getLanguageCode());
}

public function testIsSystemUserWhenUserExists(): void {
$email = 'user@example.com';
$user = $this->createMock(IUser::class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ protected function setUp(): void {
static fn (string $text, array $parameters = []): string => vsprintf($text, $parameters)
);
$l10n->method('l')->willReturn('date');
$l10n->method('getLanguageCode')->willReturn('de');
$l10nFactory = $this->createMock(IFactory::class);
$l10nFactory->method('get')->willReturn($l10n);

Expand Down Expand Up @@ -114,6 +115,9 @@ public function testMailShowsActorAndCalendar(?string $actorEmail, string $expec
$owner->method('getEMailAddress')->willReturn('alice@example.com');
$this->userManager->method('get')->with('alice')->willReturn($owner);

$this->template->expects(self::once())
->method('setLanguage')
->with('de');
$this->template->expects(self::once())
->method('addBodySender')
->with('Bob', $expectedSubline);
Expand Down
1 change: 1 addition & 0 deletions apps/encryption/lib/Crypto/EncryptAll.php
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ protected function sendPasswordsByMail(): void {
'user' => $recipient->getUID(),
'password' => $password,
]);
$template->setLanguage($l->getLanguageCode());

$template->setSubject($l->t('one-time password for server-side-encryption'));
// 'Hey there,<br><br>The administration enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>
Expand Down
1 change: 1 addition & 0 deletions apps/files_sharing/lib/SharesReminderJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ private function generateEMailTemplate(IL10N $l, array $folder): IEMailTemplate
$emailTemplate = $this->mailer->createEMailTemplate('files_sharing.SharesReminder', [
'folder' => $folder,
]);
$emailTemplate->setLanguage($l->getLanguageCode());
$emailTemplate->addHeader();
$emailTemplate->setSubject(
$l->t('Remember to upload the files to %s', [$folder['name']])
Expand Down
4 changes: 4 additions & 0 deletions apps/files_sharing/tests/SharesReminderJobTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@ public function testSharesReminder(
$this->mailer->method('createMessage')->willReturn($messageMock);
$templateMock = $this->createMock(IEMailTemplate::class);
$this->mailer->method('createEMailTemplate')->willReturn($templateMock);
$templateMock
->expects(($shouldBeReminded ? $this->once() : $this->never()))
->method('setLanguage')
->with('en');
$templateMock
->expects(($shouldBeReminded ? $this->once() : $this->never()))
->method('addBodyDetails')
Expand Down
1 change: 1 addition & 0 deletions apps/settings/lib/Controller/MailSettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ public function sendTestMail() {
$template = $this->mailer->createEMailTemplate('settings.TestEmail', [
'displayname' => $displayName,
]);
$template->setLanguage($this->l10n->getLanguageCode());

$template->setSubject($this->l10n->t('Email setting test'));
$template->addHeader();
Expand Down
2 changes: 2 additions & 0 deletions apps/settings/lib/Hooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ public function onChangePassword(PasswordUpdatedEvent $handle): void {
'instanceUrl' => $instanceUrl,
'event' => $handle,
]);
$template->setLanguage($l->getLanguageCode());

$template->setSubject($l->t('Password for %1$s changed on %2$s', [$user->getDisplayName(), $instanceName]));
$template->addHeader();
Expand Down Expand Up @@ -177,6 +178,7 @@ public function onChangeEmail(UserChangedEvent $handle): void {
'oldEMailAddress' => $oldMailAddress,
'instanceUrl' => $instanceUrl,
]);
$template->setLanguage($l->getLanguageCode());

$template->setSubject($l->t('Email address for %1$s changed on %2$s', [$user->getDisplayName(), $instanceName]));
$template->addHeader();
Expand Down
1 change: 1 addition & 0 deletions apps/settings/lib/Mailer/NewUserMailHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ public function generateTemplate(IUser $user, $generatePasswordResetToken = fals
'instancename' => $this->themingDefaults->getName(),
'resetTokenGenerated' => $generatePasswordResetToken,
]);
$emailTemplate->setLanguage($l10n->getLanguageCode());

$emailTemplate->setSubject($l10n->t('Your %s account was created', [$this->themingDefaults->getName()]));
$emailTemplate->addHeader();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ public function testSendTestMail(): void {
->willReturnCallback(function ($text, $parameters = []) {
return vsprintf($text, $parameters);
});
$this->l->method('getLanguageCode')->willReturn('de');
$this->userSession
->expects($this->any())
->method('getUser')
Expand All @@ -176,6 +177,9 @@ public function testSendTestMail(): void {
->method('createMessage')
->willReturn($this->createMock(Message::class));
$emailTemplate = $this->createMock(IEMailTemplate::class);
$emailTemplate->expects($this->once())
->method('setLanguage')
->with('de');
$this->mailer
->expects($this->once())
->method('createEMailTemplate')
Expand Down
3 changes: 3 additions & 0 deletions apps/settings/tests/HooksTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ protected function setUp(): void {

$l10n = $this->createMock(IL10N::class);
$l10n->method('t')->willReturnCallback(fn (string $text, array $parameters = []): string => vsprintf($text, $parameters));
$l10n->method('getLanguageCode')->willReturn('de');
$l10nFactory = $this->createMock(IFactory::class);
$l10nFactory->method('get')->with('settings')->willReturn($l10n);

Expand Down Expand Up @@ -80,6 +81,7 @@ protected function setUp(): void {
public function testPasswordChangedMail(): void {
$this->user->method('getEMailAddress')->willReturn('alice@example.com');

$this->template->expects($this->once())->method('setLanguage')->with('de');
$this->template->expects($this->once())->method('addBodyText')
->with('Your password on https://cloud.example.com/ was changed.');
$this->template->expects($this->once())->method('addBodyNote')
Expand All @@ -93,6 +95,7 @@ public function testEmailChangedMail(): void {
$this->user->method('getEMailAddress')->willReturn('new@example.com');
$order = [];

$this->template->expects($this->once())->method('setLanguage')->with('de');
$this->template->expects($this->once())->method('addBodyText')
->with('Your email address on https://cloud.example.com/ was changed.');
$this->template->expects($this->once())->method('addBodyDetails')
Expand Down
4 changes: 4 additions & 0 deletions apps/sharebymail/lib/ShareByMailProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ protected function sendEmail(IShare $share, array $emails): void {
'shareWith' => $shareWith,
'note' => $note
]);
$emailTemplate->setLanguage($this->l->getLanguageCode());

$emailTemplate->setSubject($this->l->t('%1$s shared %2$s with you', [$initiatorDisplayName, $filename]));
$emailTemplate->addHeader();
Expand Down Expand Up @@ -465,6 +466,7 @@ protected function sendPassword(IShare $share, string $password, array $emails):
'initiatorEmail' => $initiatorEmailAddress,
'shareWith' => $shareWith,
]);
$emailTemplate->setLanguage($this->l->getLanguageCode());

$emailTemplate->setSubject($this->l->t('Password to access %1$s shared to you by %2$s', [$filename, $initiatorDisplayName]));
$emailTemplate->addHeader();
Expand Down Expand Up @@ -543,6 +545,7 @@ protected function sendNote(IShare $share): void {
$message = $this->mailer->createMessage();

$emailTemplate = $this->mailer->createEMailTemplate('shareByMail.sendNote');
$emailTemplate->setLanguage($this->l->getLanguageCode());

$emailTemplate->setSubject($this->l->t('%s added a note to a file shared with you', [$initiatorDisplayName]));
$emailTemplate->addHeader();
Expand Down Expand Up @@ -609,6 +612,7 @@ protected function sendPasswordToOwner(IShare $share, string $password): bool {
'initiatorEmail' => $initiatorEMailAddress,
'shareWith' => $shareWith,
]);
$emailTemplate->setLanguage($this->l->getLanguageCode());

$emailTemplate->setSubject($this->l->t('Password to access %1$s shared by you with %2$s', [$filename, $shareWith]));
$emailTemplate->addHeader();
Expand Down
14 changes: 12 additions & 2 deletions apps/sharebymail/tests/ShareByMailProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ protected function setUp(): void {
->willReturnCallback(function ($text, $parameters = []) {
return vsprintf($text, $parameters);
});
$this->l->method('getLanguageCode')->willReturn('de');
$this->config = $this->createMock(IConfig::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->rootFolder = $this->createMock('OCP\Files\IRootFolder');
Expand Down Expand Up @@ -437,7 +438,9 @@ public function testCreateSendPasswordByMailWithEnforcedPasswordProtectionWithPe
->willReturnCallback(function () use (&$calls) {
$expected = array_shift($calls);
$this->assertEquals($expected, func_get_args());
return $this->createMock(IEMailTemplate::class);
$template = $this->createMock(IEMailTemplate::class);
$template->expects($this->once())->method('setLanguage')->with('de');
return $template;
});

// Main email notification is sent as well as the password
Expand Down Expand Up @@ -616,7 +619,9 @@ public function testCreateSendPasswordByTalkWithEnforcedPasswordProtectionWithPe
->willReturnCallback(function () use (&$calls) {
$expected = array_shift($calls);
$this->assertEquals($expected, func_get_args());
return $this->createMock(IEMailTemplate::class);
$template = $this->createMock(IEMailTemplate::class);
$template->expects($this->once())->method('setLanguage')->with('de');
return $template;
});

// Main email notification is sent as well as the password to owner
Expand Down Expand Up @@ -1334,6 +1339,10 @@ public function testSendMailNotificationWithSameUserAndUserEmail(): void {
->expects($this->once())
->method('createEMailTemplate')
->willReturn($template);
$template
->expects($this->once())
->method('setLanguage')
->with('de');
$template
->expects($this->once())
->method('addHeader');
Expand Down Expand Up @@ -1987,6 +1996,7 @@ public function testSendNote(bool $replyToInitiator, string $senderSubline): voi
$this->mailer->method('createEMailTemplate')->willReturn($template);
$this->mailer->expects($this->once())->method('send')->with($message);

$template->expects($this->once())->method('setLanguage')->with('de');
$template->expects($this->once())->method('addBodySender')->with('Sharer', $senderSubline);
$template->expects($this->once())->method('addHeading')->with('Sharer shared file.txt with you', 'Sharer shared file.txt with you');
$template->expects($this->once())->method('addBodyNote')->with('A note & more', 'Note');
Expand Down
1 change: 1 addition & 0 deletions core/BackgroundJobs/CheckCodeIntegrityJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ private function sendMail(IUser $admin, array $summary): void {
$l = $this->l10nFactory->get('core', $this->l10nFactory->getUserLanguage($admin));

$template = $this->mailer->createEMailTemplate('core.CodeIntegrityChanged', $summary);
$template->setLanguage($l->getLanguageCode());
$template->setSubject($l->t('The code integrity check result has changed'));
$template->addHeader();
$template->addHeading($l->t('The code integrity check result has changed'));
Expand Down
1 change: 1 addition & 0 deletions core/Controller/LostController.php
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ protected function sendEmail(string $input): void {
$emailTemplate = $this->mailer->createEMailTemplate('core.ResetPassword', [
'link' => $link,
]);
$emailTemplate->setLanguage($this->l10n->getLanguageCode());

$emailTemplate->setSubject($this->l10n->t('%s password reset', [$this->defaults->getName()]));
$emailTemplate->addHeader();
Expand Down
1 change: 1 addition & 0 deletions lib/private/Accounts/AccountManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ protected function sendEmailVerificationEmail(IUser $user, string $email): bool
if (!$this->l10n) {
$this->l10n = $this->l10nFactory->get('core');
}
$emailTemplate->setLanguage($this->l10n->getLanguageCode());

$emailTemplate->setSubject($this->l10n->t('%s email verification', [$this->defaults->getName()]));
$emailTemplate->addHeader();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public function handle(Event $event): void {
private function getWipingStartedMessage(RemoteWipeStarted $event, IUser $user): IMessage {
$message = $this->mailer->createMessage();
$emailTemplate = $this->mailer->createEMailTemplate('auth.RemoteWipeStarted');
$emailTemplate->setLanguage($this->l10n->getLanguageCode());
$plainHeading = $this->l10n->t('Wiping of device %s has started', [$event->getToken()->getName()]);
$htmlHeading = $this->l10n->t('Wiping of device »%s« has started', [$event->getToken()->getName()]);
$emailTemplate->setSubject(
Expand Down Expand Up @@ -123,6 +124,7 @@ private function getWipingStartedMessage(RemoteWipeStarted $event, IUser $user):
private function getWipingFinishedMessage(RemoteWipeFinished $event, IUser $user): IMessage {
$message = $this->mailer->createMessage();
$emailTemplate = $this->mailer->createEMailTemplate('auth.RemoteWipeFinished');
$emailTemplate->setLanguage($this->l10n->getLanguageCode());
$plainHeading = $this->l10n->t('Wiping of device %s has finished', [$event->getToken()->getName()]);
$htmlHeading = $this->l10n->t('Wiping of device »%s« has finished', [$event->getToken()->getName()]);
$emailTemplate->setSubject(
Expand Down
33 changes: 32 additions & 1 deletion lib/private/Mail/EMailTemplate.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class EMailTemplate implements IEMailTemplate {
protected bool $bodyListOpened = false;
/** indicated if the footer is added */
protected bool $footerAdded = false;
/** language of the recipient, set by setLanguage */
protected ?string $language = null;
/** name of the person the email is sent on behalf of, set by addBodySender */
protected ?string $senderName = null;
/** @var array<array{name: string, content: string, mimeType: string}> images to embed inline, referenced via cid: */
Expand Down Expand Up @@ -308,6 +310,13 @@ public function addHeader(): void {
$this->htmlBody .= vsprintf($this->header, [$this->themingDefaults->getDefaultColorPrimary(), $logoSrc, $this->themingDefaults->getName(), $logoSizeDimensions]);
}

#[\Override]
public function setLanguage(string $language): void {
if ($language !== '') {
$this->language = $language;
}
}

/**
* Images that must be embedded inline in the message, referenced via `cid:<name>` in the HTML body
*
Expand Down Expand Up @@ -746,6 +755,7 @@ protected function ensureBodyIsClosed(): void {
*/
#[\Override]
public function addFooter(string $text = '', ?string $lang = null): void {
$lang ??= $this->language;
$plainText = null;
if ($text === '') {
$l10n = $this->l10nFactory->get('lib', $lang);
Expand Down Expand Up @@ -797,7 +807,28 @@ public function renderHtml(): string {
$this->ensureBodyIsClosed();
$this->htmlBody .= $this->tail;
}
return $this->htmlBody;
if ($this->language === null) {
return $this->htmlBody;
}

$direction = $this->l10nFactory->getLanguageDirection($this->language);
$lang = htmlspecialchars(str_replace('_', '-', $this->language));
$html = str_replace('lang="en" xml:lang="en"', 'lang="' . $lang . '" xml:lang="' . $lang . '" dir="' . $direction . '"', $this->htmlBody);
return $direction === 'rtl' ? $this->mirrorStyles($html) : $html;
}

/**
* Swap left and right in the template's own styles: the style attributes
* and the style element. Text content is never touched.
*/
protected function mirrorStyles(string $html): string {
$mirror = static function (array $match): string {
// 4-value margin and padding shorthands are top, right, bottom, left
$css = preg_replace('/\\b(margin|padding):([^\\s;"!]+) ([^\\s;"!]+) ([^\\s;"!]+) ([^\\s;"!]+)/', '$1:$2 $5 $4 $3', $match[2]) ?? $match[2];
return $match[1] . strtr($css, ['left' => 'right', 'right' => 'left']) . $match[3];
};
$html = preg_replace_callback('/(style=")([^"]*)(")/', $mirror, $html) ?? $html;
return preg_replace_callback('/(<style[^>]*>)(.*?)(<\\/style>)/s', $mirror, $html) ?? $html;
}

/**
Expand Down
Loading
Loading