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
23 changes: 16 additions & 7 deletions lib/Controller/FederatedInvitesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -777,29 +777,38 @@ private function sendInvitationEmail(string $token, string $senderProvider, stri

$emailTemplate->setSubject($subject);
$emailTemplate->addHeader();
$emailTemplate->addHeading($this->il10->t('%1$s invites you to exchange contact information.', [$initiatorDisplayName]), false);
$emailTemplate->addHeading($this->il10->t('%1$s invites you to exchange contact information.', [$initiatorDisplayName]));

if ($isSenderCopy) {
$emailTemplate->addBodyText($this->il10->t('This is a copy of the invitation you\'ve sent to %1$s', [$recipientEmail]));
}

$emailTemplate->addBodyText($this->il10->t('Hi there,'));
$emailTemplate->addBodyText($this->il10->t('%1$s invites you to exchange cloud accounts and contact information.', [$initiatorDisplayName]));
$emailTemplate->addBodyText($this->il10->t('This will allow you to share data with each other.'));

if (trim($message) !== '') {
$emailTemplate->addBodyListItem($message, $this->il10->t('Message:'));
$messageLabel = $this->il10->t('Message:');
$emailTemplate->addBodyListItem($message, $messageLabel, '', '', '', mb_strlen($messageLabel));
}

$emailTemplate->addBodyText($this->il10->t('To accept this invite, click the button below and sign in with your cloud provider:'));
$emailTemplate->addBodyButton(
$this->il10->t('Accept invitation'),
$inviteLink
);
$emailTemplate->addBodyText(
htmlspecialchars($this->il10->t('If the button does not work, copy this link into your browser: %1$s', [$inviteLink]), ENT_QUOTES, 'UTF-8'),
false
);

$emailTemplate->addBodyText($this->il10->t('Invitation details:'));
$emailTemplate->addBodyText($this->il10->t('Invite code: %1$s', ["$token@$senderProvider"]));
$emailTemplate->addBodyText($this->il10->t('Encoded invite: %1$s', [$encoded]));
$details = [
$this->il10->t('Invitation details:'),
$this->il10->t('Invite code: %1$s', ["$token@$senderProvider"]),
$this->il10->t('Encoded invite: %1$s', [$encoded]),
];
$emailTemplate->addBodyText(
'<small>' . implode('<br>', array_map(static fn (string $line): string => htmlspecialchars($line, ENT_QUOTES, 'UTF-8'), $details)) . '</small>',
implode(PHP_EOL, $details)
);

$emailTemplate->addFooter();

Expand Down
52 changes: 34 additions & 18 deletions tests/unit/Controller/FederatedInvitesControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

use ChristophWurst\Nextcloud\Testing\TestCase;
use OC\App\CompareVersion;
use OC\Mail\EMailTemplate;
use OCA\Contacts\Db\FederatedInvite;
use OCA\Contacts\Db\FederatedInviteMapper;
use OCA\Contacts\Service\FederatedInvitesService;
Expand Down Expand Up @@ -795,27 +794,44 @@ public function testSetOcmInviteBoolSettingReturnsForbiddenOnUnknownKey(): void
}

/**
* Renders the controller's IEMailTemplate through the real server implementation
* and captures the resulting bodies when the template is attached to the message.
* Captures the bodies built through IEMailTemplate when the template is attached to the message.
* Text is HTML-escaped only when the plain text falls back to it, as documented by IEMailTemplate.
*/
private function createTemplateCapturingMessage(?string &$capturedHtml, ?string &$capturedPlain): \OCP\Mail\IMessage|MockObject {
$this->languageFactory->method('get')->willReturn($this->l10n);
$this->mailer->method('createEMailTemplate')->willReturnCallback(
fn (string $emailId, array $data = []): \OCP\Mail\IEMailTemplate => new EMailTemplate(
$this->defaults,
$this->urlGenerator,
$this->languageFactory,
null,
null,
$emailId,
$data,
)
);
$html = '';
$plain = '';
$append = static function (string $text, string|bool $plainText) use (&$html, &$plain): void {
if ($plainText === '' || $plainText === true) {
$html .= htmlspecialchars($text) . "\n";
$plain .= $text . "\n";
return;
}
$html .= $text . "\n";
if ($plainText !== false) {
$plain .= $plainText . "\n";
}
};

$template = $this->createMock(\OCP\Mail\IEMailTemplate::class);
$template->method('addHeading')->willReturnCallback(static fn (string $title, string|bool $plainTitle = '') => $append($title, $plainTitle));
$template->method('addBodyText')->willReturnCallback(static fn (string $text, string|bool $plainText = '') => $append($text, $plainText));
$template->method('addBodyListItem')->willReturnCallback(static function (string $text, string $metaInfo = '', string $icon = '', string|bool $plainText = '', string|bool $plainMetaInfo = '') use ($append): void {
$append($metaInfo, $plainMetaInfo);
$append($text, $plainText);
});
$template->method('addBodyButton')->willReturnCallback(static function (string $text, string $url, string|bool $plainText = '') use (&$html, &$plain): void {
$html .= htmlspecialchars($text) . ' ' . htmlspecialchars($url) . "\n";
if ($plainText !== false) {
$plain .= ($plainText === '' ? $text : $plainText) . ': ' . $url . "\n";
}
});
$this->mailer->method('createEMailTemplate')->willReturn($template);

$message = $this->createMock(\OCP\Mail\IMessage::class);
$message->method('useTemplate')->willReturnCallback(function (\OCP\Mail\IEMailTemplate $template) use (&$capturedHtml, &$capturedPlain, $message) {
$capturedHtml = $template->renderHtml();
$capturedPlain = $template->renderText();
$message->method('useTemplate')->willReturnCallback(function (\OCP\Mail\IEMailTemplate $usedTemplate) use (&$capturedHtml, &$capturedPlain, &$html, &$plain, $template, $message) {
$this->assertSame($template, $usedTemplate);
$capturedHtml = $html;
$capturedPlain = $plain;
return $message;
});
return $message;
Expand Down
Loading