diff --git a/appinfo/info.xml b/appinfo/info.xml
index 48ea20306dbae066a06d4a21f6c0ab6537fb369e..6076c77bd586b1e6fc26c126e1ff6279a24813c4 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -17,4 +17,7 @@
OCA\EmailRecovery\Settings\RecoveryEmailSettings
+
+ OCA\EmailRecovery\BackgroundJob\WeeklyRecoveryNotificationJob
+
diff --git a/img/important.svg b/img/important.svg
new file mode 100644
index 0000000000000000000000000000000000000000..d2ec0fd7d0e100324da11aa69ac6b11346c771e4
--- /dev/null
+++ b/img/important.svg
@@ -0,0 +1,9 @@
+
diff --git a/l10n/de.json b/l10n/de.json
index 72dcd1c72d7ccbea05487f0d71e03b443b4c4f65..4d8661b803a26b17fd2129e38d1b10341ee721df 100644
--- a/l10n/de.json
+++ b/l10n/de.json
@@ -24,6 +24,8 @@
"Could not verify recovery email because the token is invalid": "Wiederherstellungs-E-Mail konnte nicht verifiziert werden, da das Token ungültig ist",
"Unverified recovery email:": "Nicht verifizierte Wiederherstellungs-E-Mail:",
"Please set your recovery email address to use your email account without restrictions.": "Bitte geben Sie Ihre Wiederherstellungs-E-Mail-Adresse an, damit Sie Ihr E-Mail ohne Beschränkungen nutzen können.",
- "SET RECOVERY EMAIL NOW": "MEINE WIEDERHERSTELLUNGS-E-MAIL-ADRESSE ANGEBEN"
+ "SET RECOVERY EMAIL NOW": "MEINE WIEDERHERSTELLUNGS-E-MAIL-ADRESSE ANGEBEN",
+ "20240101_weekly_reminder_subject": "**Erforderliche Handlung: Geben Sie eine E-Mail-Adresse zur Wiederherstellung an**",
+ "20240101_weekly_reminder_body": "Hallo **{username}**,\n\nAus Sicherheitsgründen müssen Sie eine Wiederherstellungs-E-Mail-Adresse für Ihr Murena Cloud-Konto angeben. Da Sie dies noch nicht getan haben, ist die Benutzung Ihrer E-Mail-Adresse eingeschränkt. Bitte geben Sie eine gültige E-Mail-Adresse zur Wiederherstellung an, damit die Nutzungsbeschränkungen Ihrer E-Mail-Adresse aufgehoben werden.\n\n[MEINE WIEDERHERSTELLUNGS-E-MAIL-ADRESSE ANGEBEN](https://murena.io/settings/user/security#recovery-email-form)"
},"pluralForm" :"nplurals=2; plural=n != 1;"
}
\ No newline at end of file
diff --git a/l10n/de_DE.json b/l10n/de_DE.json
index b05534e374b74fd93dab393be91396244aa76b1b..170d678f3d3c60fbc8222edf2f450d64f085ec2f 100644
--- a/l10n/de_DE.json
+++ b/l10n/de_DE.json
@@ -22,6 +22,8 @@
"You verified recovery email successfully.": "Sie haben die Wiederherstellungs-E-Mail erfolgreich verifiziert.",
"Could not verify recovery email because the token is expired": "Wiederherstellungs-E-Mail konnte nicht verifiziert werden, da das Token abgelaufen ist",
"Could not verify recovery email because the token is invalid": "Wiederherstellungs-E-Mail konnte nicht verifiziert werden, da das Token ungültig ist",
- "Unverified recovery email:": "Nicht verifizierte Wiederherstellungs-E-Mail:"
+ "Unverified recovery email:": "Nicht verifizierte Wiederherstellungs-E-Mail:",
+ "20240101_weekly_reminder_subject": "**Erforderliche Handlung: Geben Sie eine E-Mail-Adresse zur Wiederherstellung an**",
+ "20240101_weekly_reminder_body": "Hallo **{username}**,\n\nAus Sicherheitsgründen müssen Sie eine Wiederherstellungs-E-Mail-Adresse für Ihr Murena Cloud-Konto angeben. Da Sie dies noch nicht getan haben, ist die Benutzung Ihrer E-Mail-Adresse eingeschränkt. Bitte geben Sie eine gültige E-Mail-Adresse zur Wiederherstellung an, damit die Nutzungsbeschränkungen Ihrer E-Mail-Adresse aufgehoben werden.\n\n[MEINE WIEDERHERSTELLUNGS-E-MAIL-ADRESSE ANGEBEN](https://murena.io/settings/user/security#recovery-email-form)"
},"pluralForm" :"nplurals=2; plural=n != 1;"
}
\ No newline at end of file
diff --git a/l10n/en.json b/l10n/en.json
index 5fad55ae50253898ba99d9cbb02788024357106b..b4b133a2482651d840ec10401c21b5186fa63184 100644
--- a/l10n/en.json
+++ b/l10n/en.json
@@ -23,7 +23,9 @@
"Could not verify recovery email because the token is invalid": "Could not verify recovery email because the token is invalid",
"Unverified recovery email:": "Unverified recovery email:",
"Please set your recovery email address to use your email account without restrictions.": "Please set your recovery email address to use your email account without restrictions.",
- "SET RECOVERY EMAIL NOW": "SET RECOVERY EMAIL NOW"
+ "SET RECOVERY EMAIL NOW": "SET RECOVERY EMAIL NOW",
+ "20240101_weekly_reminder_subject": "**Action required: Set up your recovery email**",
+ "20240101_weekly_reminder_body": "Dear **{username}**,\n\nFor security reasons you need to set a recovery email address for your Murena Cloud account. As you haven't done so yet, the usage of your email address is restricted. Please set and validate a recovery email address to remove all usage restriction on your email address.\n\n[SET MY RECOVERY EMAIL ADDRESS](https://murena.io/settings/user/security#recovery-email-form)"
},
"pluralForm": "nplurals=2; plural=(n != 1);"
}
\ No newline at end of file
diff --git a/l10n/es.json b/l10n/es.json
index 2b64cbb08a1cc74b9c8a5457268ba30a4725116f..e6c4d6d1e35d2adc1ed785790a94a84acd1b0ceb 100644
--- a/l10n/es.json
+++ b/l10n/es.json
@@ -24,6 +24,8 @@
"Could not verify recovery email because the token is invalid": "No se ha podido verificar el correo electrónico de recuperación porque el token no es válido",
"Unverified recovery email:": "Correo electrónico de recuperación no verificado:",
"Please set your recovery email address to use your email account without restrictions.": "Configura tu dirección de correo electrónico de recuperación para utilizar tu cuenta de correo electrónico sin restricciones.",
- "SET RECOVERY EMAIL NOW": "CONFIGURAR CORREO ELECTRÓNICO DE RECUPERACIÓN"
+ "SET RECOVERY EMAIL NOW": "CONFIGURAR CORREO ELECTRÓNICO DE RECUPERACIÓN",
+ "20240101_weekly_reminder_subject": "**Acción necesaria: Configura tu correo electrónico de recuperación**",
+ "20240101_weekly_reminder_body": "Estimado **{username}**,\n\nPor motivos de seguridad, debes proveer una dirección de email de recuperación para tu cuenta Murena Cloud. Como aún no lo haz hecho, el uso de tu dirección de correo electrónico está restringido. Configura y confirma una dirección de correo alternativa de recuperación para eliminar todas las restricciones de uso de tu correo Murena.\n\n[CONFIGURAR DIRECCIÓN DE EMAIL DE RECUPERACIÓN](https://murena.io/settings/user/security#recovery-email-form)"
},"pluralForm" :"nplurals=2; plural=n != 1;"
}
\ No newline at end of file
diff --git a/l10n/fr.json b/l10n/fr.json
index dc818ef7ca7e5818de8e46d073564891f68a718d..46ad294d783487ccc50f82d7d232a09b5e58b0e4 100644
--- a/l10n/fr.json
+++ b/l10n/fr.json
@@ -24,6 +24,8 @@
"Could not verify recovery email because the token is invalid": "Impossible de vérifier l'e-mail de récupération car le jeton n'est pas valide.",
"Unverified recovery email:": "Courriel de récupération non vérifié :",
"Please set your recovery email address to use your email account without restrictions.": "Merci de définir une adresse e-mail de récupération pour bénéficier d'un usage de votre compte sans restrictions.",
- "SET RECOVERY EMAIL NOW": "DEFINIR L'ADRESSE DE RECUPERATION MAINTENANT"
+ "SET RECOVERY EMAIL NOW": "DEFINIR L'ADRESSE DE RECUPERATION MAINTENANT",
+ "20240101_weekly_reminder_subject": "**Action requise: Définissez votre adresse de récupération**",
+ "20240101_weekly_reminder_body": "Cher/Chère **{username}**,\n\nPour des raisons de sécurité, vous devez définir une adresse de récupération pour votre compte Murena Cloud. Comme vous ne l'avez pas encore fait, votre utilisation de votre adresse e-mail est restreinte. Merci de définir et valider un adresse e-mail de récupération pour que toutes les restrictions en place sur votre adresse e-mail puissent être levées.\n\n[DEFINIR MON ADRESSE DE RECUPERATION](https://murena.io/settings/user/security#recovery-email-form)"
},"pluralForm" :"nplurals=2; plural=n > 1;"
}
\ No newline at end of file
diff --git a/l10n/it.json b/l10n/it.json
index 13444723e2349f081291719dfcd8c98f3a060ac3..c7e7a4d923fefe1be0fd947f5c73db6efea5362a 100644
--- a/l10n/it.json
+++ b/l10n/it.json
@@ -24,6 +24,8 @@
"Could not verify recovery email because the token is invalid": "Impossibile verificare l'email di recupero perché il token non è valido",
"Unverified recovery email:": "Email di recupero non verificata:",
"Please set your recovery email address to use your email account without restrictions.": "Per poter usare l'account email senza restrizioni devi impostare l'indirizzo email di recovery.",
- "SET RECOVERY EMAIL NOW": "IMPOSTA ADESSO LA EMAIL DI RECOVERY"
+ "SET RECOVERY EMAIL NOW": "IMPOSTA ADESSO LA EMAIL DI RECOVERY",
+ "20240101_weekly_reminder_subject": "**Azione richiesta: imposta un indirizzo email di recovery**",
+ "20240101_weekly_reminder_body": "Caro **{username}**,\n\nPer ragioni di sicurezza, devi impostrare un indirizzo email di recovery per il tuo account Murena Cloud. Dato che non l'hai ancora fatto, l'uso del tuo indirizzo email è stato attualmente limitato. Per rimuovere tutte le limitazioni dovresti impostare e validare una email di recovery.\n\n[IMPOSTA L'INDIRIZZO E-MAIL DI RECOVERY](https://murena.io/settings/user/security#recovery-email-form)"
},"pluralForm" :"nplurals=2; plural=n != 1;"
}
\ No newline at end of file
diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index ca33cdf57682b04a66dc36adbd02e74010116f29..d98ff3a0487e6ccbd03202c133f2cbca53e62e09 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -34,6 +34,8 @@ use OCA\EmailRecovery\Listeners\BeforeTemplateRenderedListener;
use OCP\User\Events\UserConfigChangedEvent;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
+use OCP\Notification\IManager as INotificationManager;
+use OCA\EmailRecovery\Notification\Notifier;
class Application extends App implements IBootstrap {
public const APP_ID = 'email-recovery';
@@ -47,5 +49,9 @@ class Application extends App implements IBootstrap {
$context->registerEventListener(UserConfigChangedEvent::class, UserConfigChangedListener::class);
}
public function boot(IBootContext $context): void {
+ $context->injectFn([$this, 'registerNotifier']);
+ }
+ public function registerNotifier(INotificationManager $manager): void {
+ $manager->registerNotifierService(Notifier::class);
}
}
diff --git a/lib/BackgroundJob/WeeklyRecoveryNotificationJob.php b/lib/BackgroundJob/WeeklyRecoveryNotificationJob.php
new file mode 100644
index 0000000000000000000000000000000000000000..3f4b94a8e36f8c48ba96f3b0ea94fdaa5c97772d
--- /dev/null
+++ b/lib/BackgroundJob/WeeklyRecoveryNotificationJob.php
@@ -0,0 +1,255 @@
+
+ *
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see
+ *
+ */
+
+namespace OCA\EmailRecovery\BackgroundJob;
+
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\TimedJob;
+use OCP\IConfig;
+use OCP\IUserManager;
+use OCP\Mail\IEMailTemplate;
+use OCP\Mail\IMailer;
+use Psr\Log\LoggerInterface;
+use OCA\EmailRecovery\AppInfo\Application;
+use OCA\EmailRecovery\Service\NotificationService;
+use OCP\IUser;
+use OCP\Notification\INotification;
+use OCP\Notification\IManager as INotificationManager;
+use OCP\IURLGenerator;
+
+class WeeklyRecoveryNotificationJob extends TimedJob {
+ private IConfig $config;
+ private IMailer $mailer;
+ private LoggerInterface $logger;
+ private IUserManager $userManager;
+ private NotificationService $notificationService;
+ private $uids = [];
+ private ITimeFactory $timeFactory;
+ private INotificationManager $notificationManager;
+ private IURLGenerator $urlGenerator;
+
+ public function __construct(
+ IConfig $config,
+ IMailer $mailer,
+ IUserManager $userManager,
+ LoggerInterface $logger,
+ NotificationService $notificationService,
+ ITimeFactory $timeFactory,
+ INotificationManager $notificationManager,
+ IURLGenerator $urlGenerator
+ ) {
+ parent::__construct($timeFactory);
+
+ // $this->setInterval(2 * 60); // Run every 2 minutes
+ // $this->setInterval(60 * 60 * 24); // Run once per day
+ $this->setInterval(7 * 24 * 60 * 60); // Run for 7 days
+ $this->setTimeSensitivity(self::TIME_INSENSITIVE);
+
+ $this->config = $config;
+ $this->mailer = $mailer;
+ $this->logger = $logger;
+ $this->userManager = $userManager;
+ $this->notificationService = $notificationService;
+ $this->timeFactory = $timeFactory;
+ $this->notificationManager = $notificationManager;
+ $this->urlGenerator = $urlGenerator;
+ }
+
+ protected function run($argument): void {
+ try {
+ $this->prepareValidUserIds();
+ $messageId = $this->config->getSystemValue('weekly_reminder_messageid');
+ $this->sendCloudNotifications($messageId);
+ $this->sendEmails($messageId);
+ } catch (\Exception $e) {
+ $this->logger->error('Error sending notification emails to users', ['exception' => $e]);
+ return;
+ }
+ }
+
+ /**
+ * Prepares valid user IDs and stores them in the 'uids' array.
+ * @return void
+ */
+ private function prepareValidUserIds(): void {
+ $users = $this->config->getUsersForUserValue(Application::APP_ID, 'recovery-email', '');
+ foreach ($users as $username) {
+ $user = $this->userManager->get($username);
+ if ($this->isUserValid($user)) {
+ array_push($this->uids, $user->getUID());
+ }
+ }
+ }
+ /**
+ * Send cloud notification to users
+ *
+ * @return void
+ */
+ private function sendCloudNotifications(string $messageId): void {
+ try {
+ $datetime = $this->timeFactory->getDateTime();
+ $notification = $this->notificationManager->createNotification();
+ $notificationType = 'important';
+ $notification->setApp(Application::APP_ID)
+ ->setDateTime($datetime)
+ ->setObject(Application::APP_ID . '-' . strtolower($notificationType), $messageId)
+ ->setIcon($this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath(Application::APP_ID, strtolower($notificationType) . '.svg')));
+
+ $this->sendNotificationToUsers($messageId, $notification, $this->uids);
+ } catch (\Exception $e) {
+ $this->logger->error('Error sending recovery email cloud notifications to users. Error:' . $e->getMessage(), ['exception' => $e]);
+ }
+ }
+ /**
+ * Sends a notification with the specified messageId and notification object to a list of users.
+ *
+ * @param string $messageId The identifier for the notification message.
+ * @param INotification $notification The notification object to be sent.
+ * @param array $users An array of usernames to whom the notification will be sent.
+ *
+ * @return void
+ */
+ protected function sendNotificationToUsers(string $messageId, INotification $notification, array $users): void {
+ foreach ($users as $username) {
+ try {
+ $user = $this->userManager->get($username);
+ $this->sendNotificationToUser($messageId, $user, $notification);
+ } catch (\Exception $e) {
+ $this->logger->error('Error sending recovery email cloud notifications to users. Error:' . $e->getMessage(), ['exception' => $e]);
+ }
+ }
+ }
+ /**
+ * Sends a notification with the specified messageId and notification object to a user.
+ *
+ * @param string $messageId The identifier for the notification message.
+ * @param IUser $user The user to whom the notification will be sent.
+ * @param INotification $notification The notification object to be sent.
+ *
+ * @return void
+ */
+ protected function sendNotificationToUser(string $messageId, IUser $user, INotification $notification): void {
+ $uid = $user->getUID();
+ $displayName = $user->getDisplayName();
+ try {
+ $notification->setSubject('cli', [$messageId, $displayName])->setMessage('cli', [$messageId, $displayName])->setUser($uid);
+ $this->notificationManager->notify($notification);
+ $this->logger->debug('Notificaiton sent to ' . $uid . ' successfully.');
+ } catch (\Exception $e) {
+ $this->logger->error('Failed to send notification to ' . $uid . '. Error:' . $e->getMessage(), ['exception' => $e]);
+ }
+ }
+ /**
+ * Sends recovery emails to all users.
+ *
+ * @param string $messageId The identifier for the notification message.
+ *
+ * @return void
+ */
+ private function sendEmails(string $messageId): void {
+ foreach ($this->uids as $uid) {
+ try {
+ $user = $this->userManager->get($uid);
+ $username = $user->getDisplayName();
+ $emailAddress = $user->getEMailAddress();
+
+ $language = $this->config->getUserValue($uid, 'core', 'lang', null);
+ $translations = $this->notificationService->getTranslatedSubjectAndMessage($messageId, $language);
+ $subject = $translations['subject'];
+ $message = $translations['message'];
+
+ $parsedSubject = $this->notificationService->getParsedString($subject, $username);
+ $subject = $parsedSubject['message'];
+ $parsedMessage = $this->notificationService->getParsedString($message, $username);
+ $message = $parsedMessage['message'];
+
+ $this->sendEmail($subject, $message, $emailAddress);
+ $this->logger->debug('Recovery email sent to ' . $emailAddress . ' successfully.');
+ } catch (\Exception $e) {
+ $this->logger->error('Error sending notification email to user ' . $uid.'. Error:' . $e->getMessage(), ['exception' => $e]);
+ }
+ }
+ }
+
+ /**
+ * Send an email.
+ *
+ * @param string $subject The subject of the email.
+ * @param string $message The body/content of the email.
+ * @param string $emailAddress The recipient's email address.
+ *
+ * @return void
+ */
+ private function sendEmail(string $subject, string $message, string $emailAddress): void {
+ // Convert Markdown-style links to HTML anchor tags
+ $message = preg_replace('/\[(.*?)\]\((.*?)\)/', "$1", $message);
+ $template = $this->mailer->createEMailTemplate(Application::APP_ID . '::sendMail');
+ $template->setSubject($subject);
+ $template->addHeader();
+ $template->addHeading($subject);
+ $this->setMailBody($template, $message);
+ $template->addFooter();
+
+ $email = $this->mailer->createMessage();
+ $email->useTemplate($template);
+ $email->setTo([$emailAddress]);
+
+ $this->mailer->send($email);
+ }
+ /**
+ * Special-treat list items and strip empty lines
+ *
+ * @param IEMailTemplate $template
+ * @param string $message
+ *
+ * @return void
+ */
+ private function setMailBody(IEMailTemplate $template, string $message): void {
+ $lines = explode("\n", $message);
+ $finalHtml = "";
+ $finalText = "";
+ foreach ($lines as $line) {
+ if (trim($line) === '') {
+ continue;
+ }
+ $finalHtml .= "
" . $line . "
";
+ $finalText .= $line;
+ }
+ $template->addBodyText($finalHtml, $finalText);
+ }
+ /**
+ * Validate a user.
+ *
+ * @param IUser|null $user The user to be validated.
+ *
+ * @return bool Returns true if the user is valid, false otherwise.
+ */
+ private function isUserValid(?IUser $user): bool {
+ if (!($user instanceof IUser)) {
+ return false;
+ }
+ $emailAddress = $user->getEMailAddress();
+ return ($emailAddress && $user->isEnabled() && $this->mailer->validateMailAddress($emailAddress));
+ }
+}
diff --git a/lib/Notification/Notifier.php b/lib/Notification/Notifier.php
new file mode 100644
index 0000000000000000000000000000000000000000..d71d5d951031feed5232e814ae4c5201f01c2a52
--- /dev/null
+++ b/lib/Notification/Notifier.php
@@ -0,0 +1,112 @@
+
+ *
+ * @author Joas Schilling
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+namespace OCA\EmailRecovery\Notification;
+
+use OCP\L10N\IFactory;
+use OCP\Notification\INotification;
+use OCP\Notification\INotifier;
+use OCA\EmailRecovery\AppInfo\Application;
+use Psr\Log\LoggerInterface;
+use OCA\EmailRecovery\Service\NotificationService;
+
+class Notifier implements INotifier {
+ /** @var IFactory */
+ protected $l10nFactory;
+ /** @var LoggerInterface */
+ private $logger;
+
+ /** @var NotificationService */
+ private $NotificationService;
+
+ public function __construct(
+ IFactory $l10nFactory,
+ LoggerInterface $logger,
+ NotificationService $NotificationService
+ ) {
+ $this->l10nFactory = $l10nFactory;
+ $this->logger = $logger;
+ $this->NotificationService = $NotificationService;
+ }
+
+ /**
+ * Identifier of the notifier, only use [a-z0-9_]
+ *
+ * @return string
+ * @since 17.0.0
+ */
+ public function getID(): string {
+ return Application::APP_ID;
+ }
+
+ /**
+ * Human readable name describing the notifier
+ *
+ * @return string
+ * @since 17.0.0
+ */
+ public function getName(): string {
+ return $this->l10nFactory->get(Application::APP_ID)->t('Email Recovery');
+ }
+
+ /**
+ * @param INotification $notification
+ * @param string $languageCode The code of the language that should be used to prepare the notification
+ * @return INotification
+ * @throws \InvalidArgumentException When the notification was not prepared by a notifier
+ */
+ public function prepare(INotification $notification, string $languageCode): INotification {
+ if ($notification->getApp() !== Application::APP_ID) {
+ // Not my app => throw
+ throw new \InvalidArgumentException('Unknown app');
+ }
+ try {
+ $subjectParams = $notification->getSubjectParameters();
+ $messageId = $subjectParams[0];
+ $username = $subjectParams[1];
+
+ $translations = $this->NotificationService->getTranslatedSubjectAndMessage($messageId, $languageCode);
+ $subject = $translations['subject'];
+ $message = $translations['message'];
+
+ $parsedSubject = $this->NotificationService->getParsedString($subject, $username);
+ $subjectParameters = $parsedSubject['parameters'];
+ $plainSubject = $parsedSubject['message'];
+ $richSubject = $parsedSubject['richString'];
+
+ $parsedMessage = $this->NotificationService->getParsedString($message, $username);
+ $messageParameters = $parsedMessage['parameters'];
+ $plainMessage = $parsedMessage['message'];
+ $richMessage = $parsedMessage['richString'];
+
+ $notification->setParsedSubject($plainSubject)->setRichSubject($richSubject, $subjectParameters);
+ $notification->setParsedMessage($plainMessage)->setRichMessage($richMessage, $messageParameters);
+
+ return $notification;
+ } catch (\Exception $e) {
+ throw new \InvalidArgumentException('Unknown app');
+ }
+ }
+}
diff --git a/lib/Service/NotificationService.php b/lib/Service/NotificationService.php
new file mode 100644
index 0000000000000000000000000000000000000000..376df29f719fe5671f7f04e0179c0472b25a94f2
--- /dev/null
+++ b/lib/Service/NotificationService.php
@@ -0,0 +1,131 @@
+l10nFactory = $l10nFactory;
+ }
+
+ /**
+ * @param string $message
+ * @param string $username
+ */
+ public function getParsedString(string $message, string $username) {
+ $richString = $message;
+
+ $data = $this->prepareRichString($message, $richString, $username, 'url');
+ $message = $data['message'];
+ $richString = $data['richString'];
+
+ $data = $this->prepareRichString($message, $richString, $username, 'username');
+ $message = $data['message'];
+ $richString = $data['richString'];
+
+ $data = $this->prepareRichString($message, $richString, $username, 'bold');
+ $message = $data['message'];
+ $richString = $data['richString'];
+
+ return $this->assignVariables($message, $richString, $username);
+ }
+ /**
+ * @param string $message
+ * @param string $richString
+ * @param string $username
+ * @param string $type
+ */
+ private function prepareRichString($message, $richString, $username, $type) {
+ switch ($type) {
+ case 'url':
+ $richString = preg_replace('/\[(.*?)\]\((.*?)\)/', '{$1[$2]}', $message);
+ break;
+
+ case 'bold':
+ preg_match_all('#\*{2}(.*?)\*{2}#', $message, $matches);
+ if (sizeof($matches)) {
+ foreach ($matches[1] as $match) {
+ $richString = str_replace('**' . $match . '**', '{' . $match . '}', $richString);
+ $message = str_replace('**' . $match . '**', $match, $message);
+ }
+ }
+ break;
+
+ case 'username':
+ $richString = str_replace('{username}', $username, $richString);
+ $message = str_replace('{username}', $username, $message);
+ break;
+ }
+ return ['message' => $message, 'richString' => $richString];
+ }
+ /**
+ * @param string $message
+ * @param string $richString
+ * @param string $username
+ */
+ private function assignVariables($message, $richString, $username) {
+ preg_match_all('/{(.*?)}/', $richString, $matches);
+ $messageParameters = [];
+ if (sizeof($matches)) {
+ foreach ($matches[1] as $key => $match) {
+ $result = $this->checkURL($match, $richString);
+ $link = $result['link'];
+ $match = $result['match'];
+ $richString = $result['richString'];
+ $matchKey = 'key_' . $key;
+
+ $messageParameters[$matchKey] =
+ [
+ 'type' => ($match === 'username') ? 'user' : 'highlight',
+ 'id' => '',
+ 'name' => ($match === 'username') ? $username : $match,
+ 'link' => isset($link) ? $link : ''
+ ];
+
+ $richString = str_replace($match, $matchKey, $richString);
+ }
+ }
+ $placeholders = $replacements = [];
+ foreach ($messageParameters as $placeholder => $parameter) {
+ $placeholders[] = '{' . $placeholder . '}';
+ $replacements[] = $parameter['name'];
+ }
+ $message = str_replace($placeholders, $replacements, $message);
+
+ return ['message' => $message, 'richString' => $richString, 'parameters' => $messageParameters];
+ }
+
+ /**
+ * @param string $match
+ * @param string $richString
+ */
+ private function checkURL($match, $richString) {
+ preg_match('#\[(.*?)\]#', $match, $result);
+ $link = (sizeof($result)) ? $result[1] : '';
+
+ if (isset($link)) {
+ $match = str_replace('[' . $link . ']', '', $match);
+ $richString = str_replace('[' . $link . ']', '', $richString);
+ }
+ return ['link' => $link, 'match' => $match, 'richString' => $richString];
+ }
+
+ /**
+ * @param string $messageId
+ * @param string|null $language
+ */
+ public function getTranslatedSubjectAndMessage(string $messageId, $language) {
+ if (is_null($language)) {
+ $language = 'en';
+ }
+ $l = $this->l10nFactory->get(Application::APP_ID, $language);
+ return ['subject' => $l->t($messageId . '_subject'), 'message' => $l->t($messageId . '_body')];
+ }
+}
diff --git a/scss/email-recovery.scss b/scss/email-recovery.scss
index 4fbf9ae0111c983d1dcad2b158d119a89fbe0d70..40e81757b84825eeafee05e1064bc995d91611cc 100644
--- a/scss/email-recovery.scss
+++ b/scss/email-recovery.scss
@@ -102,3 +102,15 @@ body#body-public #header {
top:186px;
}
}
+[data-object-type="email-recovery-important"] a.external {
+ background-color: var(--color-primary-element);
+ color: var(--color-primary-element-text);
+ text-decoration: none;
+ display: block;
+ text-align: center;
+ padding: 10px 12px;
+ border-radius: 30px;
+ font-size: 12px;
+ font-weight: bold;
+ width: fit-content;
+}