]> git.mxchange.org Git - friendica.git/commitdiff
Extract System emails from enotify
authornupplaPhil <admin+github@philipp.info>
Sat, 1 Feb 2020 19:08:54 +0000 (20:08 +0100)
committernupplaPhil <admin+github@philipp.info>
Sun, 2 Feb 2020 21:43:35 +0000 (22:43 +0100)
- Removed every SYSTEM_EMAIL occurrence in enotify
- Introduced a "SystemMailBuilder" for build system emails
- Replaced every SYSTEM_EMAIL usage in the classes with calling this builder
- Added tests for this new Builder
- Split the email templates between "base" template for email and concrete usages for different use cases

20 files changed:
boot.php
include/enotify.php
mod/lostpass.php
mod/removeme.php
src/Core/Update.php
src/Model/User.php
src/Module/Admin/Users.php
src/Object/EMail/IEmail.php
src/Util/EMailer/MailBuilder.php [new file with mode: 0644]
src/Util/EMailer/SystemMailBuilder.php [new file with mode: 0644]
src/Util/Emailer.php
tests/Util/SampleMailBuilder.php [new file with mode: 0644]
tests/src/Util/Emailer/MailBuilderTest.php [new file with mode: 0644]
view/templates/email/html.tpl [new file with mode: 0644]
view/templates/email/notify/notify_html.tpl [new file with mode: 0644]
view/templates/email/notify/notify_text.tpl [new file with mode: 0644]
view/templates/email/notify_html.tpl [deleted file]
view/templates/email/notify_text.tpl [deleted file]
view/templates/email/system/html.tpl [new file with mode: 0644]
view/templates/email/system/text.tpl [new file with mode: 0644]

index 2bcbb4ebdd8eb745e8b3c3f622787a8750247831..3852c3279af24e3dc00ca1729d6a8e38d1ec5c6c 100644 (file)
--- a/boot.php
+++ b/boot.php
@@ -152,8 +152,6 @@ define('NOTIFY_TAGSHARE',   256);
 define('NOTIFY_POKE',       512);
 define('NOTIFY_SHARE',     1024);
 
-define('SYSTEM_EMAIL',    16384);
-
 define('NOTIFY_SYSTEM',   32768);
 /* @}*/
 
index b90f96d5aa1d238b41da1a4bd7c328c649f5697d..54bf48234169948a00242dcbfc47b51056abb589 100644 (file)
@@ -74,18 +74,14 @@ function notification($params)
 
        $sender_email = $a->getSenderEmailAddress();
 
-       if ($params['type'] != SYSTEM_EMAIL) {
-               $user = DBA::selectFirst('user', ['nickname', 'page-flags'],
-                       ['uid' => $params['uid']]);
+       $user = DBA::selectFirst('user', ['nickname', 'page-flags'],
+               ['uid' => $params['uid']]);
 
-               // There is no need to create notifications for forum accounts
-               if (!DBA::isResult($user) || in_array($user["page-flags"], [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP])) {
-                       return false;
-               }
-               $nickname = $user["nickname"];
-       } else {
-               $nickname = '';
+       // There is no need to create notifications for forum accounts
+       if (!DBA::isResult($user) || in_array($user["page-flags"], [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP])) {
+               return false;
        }
+       $nickname = $user["nickname"];
 
        // with $params['show_in_notification_page'] == false, the notification isn't inserted into
        // the database, and an email is sent if applicable.
@@ -428,30 +424,6 @@ function notification($params)
                }
        }
 
-       if ($params['type'] == SYSTEM_EMAIL) {
-               // not part of the notifications.
-               // it just send a mail to the user.
-               // It will be used by the system to send emails to users (like
-               // password reset, invitations and so) using one look (but without
-               // add a notification to the user, with could be inexistent)
-               if (!isset($params['subject'])) {
-                       Logger::warning('subject isn\'t set.', ['type' => $params['type']]);
-               }
-               $subject = $params['subject'] ?? '';
-
-               if (!isset($params['preamble'])) {
-                       Logger::warning('preamble isn\'t set.', ['type' => $params['type'], 'subject' => $subject]);
-               }
-               $preamble = $params['preamble'] ?? '';
-
-               if (!isset($params['body'])) {
-                       Logger::warning('body isn\'t set.', ['type' => $params['type'], 'subject' => $subject, 'preamble' => $preamble]);
-               }
-               $body = $params['body'] ?? '';
-
-               $show_in_notification_page = false;
-       }
-
        $subject .= " (".$nickname."@".$hostname.")";
 
        $h = [
@@ -506,8 +478,7 @@ function notification($params)
 
        // send email notification if notification preferences permit
        if ((intval($params['notify_flags']) & intval($params['type']))
-               || $params['type'] == NOTIFY_SYSTEM
-               || $params['type'] == SYSTEM_EMAIL) {
+               || $params['type'] == NOTIFY_SYSTEM) {
 
                Logger::log('sending notification email');
 
@@ -563,11 +534,10 @@ function notification($params)
                Hook::callAll('enotify_mail', $datarray);
 
                // check whether sending post content in email notifications is allowed
-               // always true for SYSTEM_EMAIL
-               $content_allowed = ((!DI::config()->get('system', 'enotify_no_content')) || ($params['type'] == SYSTEM_EMAIL));
+               $content_allowed = (!DI::config()->get('system', 'enotify_no_content'));
 
                // load the template for private message notifications
-               $tpl             = Renderer::getMarkupTemplate('email/notify_html.tpl');
+               $tpl             = Renderer::getMarkupTemplate('email/notify/html.tpl');
                $email_html_body = Renderer::replaceMacros($tpl, [
                        '$banner'          => $datarray['banner'],
                        '$product'         => $datarray['product'],
@@ -587,7 +557,7 @@ function notification($params)
                ]);
 
                // load the template for private message notifications
-               $tpl             = Renderer::getMarkupTemplate('email/notify_text.tpl');
+               $tpl             = Renderer::getMarkupTemplate('email/notify/text.tpl');
                $email_text_body = Renderer::replaceMacros($tpl, [
                        '$preamble'        => $datarray['preamble'],
                        '$tsitelink'       => $datarray['tsitelink'],
index 59426a1af8980f2e39758d53c89d34ef81bea746..d125cab5fbba33a3eb2071b88f06856fd8e2f92e 100644 (file)
@@ -64,17 +64,14 @@ function lostpass_post(App $a)
                Site Location:  %2$s
                Login Name:     %3$s', $resetlink, DI::baseUrl(), $user['nickname']));
 
-       notification([
-               'type'     => SYSTEM_EMAIL,
-               'language' => $user['language'],
-               'to_name'  => $user['username'],
-               'to_email' => $user['email'],
-               'uid'      => $user['uid'],
-               'subject'  => DI::l10n()->t('Password reset requested at %s', $sitename),
-               'preamble' => $preamble,
-               'body'     => $body
-       ]);
-
+       $email = DI::emailer()
+                  ->newSystemMail((!empty($user['language'])) ? DI::l10n()->withLang($user['language']) : DI::l10n())
+                  ->withMessage(DI::l10n()->t('Password reset requested at %s', $sitename), $preamble, $body)
+                  ->forUser($user['uid'] ?? 0)
+                  ->withRecipient($user['to_email'])
+                  ->build();
+
+       DI::emailer()->send($email);
        DI::baseUrl()->redirect();
 }
 
@@ -159,16 +156,13 @@ function lostpass_generate_password($user)
                        You may change that password from your account settings page after logging in.
                ', DI::baseUrl(), $user['nickname'], $new_password));
 
-               notification([
-                       'type'     => SYSTEM_EMAIL,
-                       'language' => $user['language'],
-                       'to_name'  => $user['username'],
-                       'to_email' => $user['email'],
-                       'uid'      => $user['uid'],
-                       'subject'  => DI::l10n()->t('Your password has been changed at %s', $sitename),
-                       'preamble' => $preamble,
-                       'body'     => $body
-               ]);
+               $email = DI::emailer()
+                          ->newSystemMail((!empty($user['language'])) ? DI::l10n()->withLang($user['language']) : DI::l10n())
+                          ->withMessage(DI::l10n()->t('Your password has been changed at %s', $sitename), $preamble, $body)
+                          ->forUser($user['uid'] ?? 0)
+                          ->withRecipient($user['to_email'])
+                          ->build();
+               DI::emailer()->send($email);
        }
 
        return $o;
index 48d4e0e0fe320b18ada2cf7f19a29a04ceaedf44..86d1f04e34c15e96f29609b8a9466a5968a1b9f7 100644 (file)
@@ -40,17 +40,16 @@ function removeme_post(App $a)
                if (!DBA::isResult($admin)) {
                        continue;
                }
-               notification([
-                       'type'         => SYSTEM_EMAIL,
-                       'subject'      => DI::l10n()->t('[Friendica System Notify]') . ' ' . DI::l10n()->t('User deleted their account'),
-                       'preamble'     => DI::l10n()->t('On your Friendica node an user deleted their account. Please ensure that their data is removed from the backups.'),
-                       'body'         => DI::l10n()->t('The user id is %d', local_user()),
-                       'to_email'     => $admin['email'],
-                       'to_name'      => $admin['username'],
-                       'uid'          => $admin['uid'],
-                       'language'     => $admin['language'] ? $admin['language'] : 'en',
-                       'show_in_notification_page' => false
-               ]);
+
+               $email = DI::emailer()
+                          ->newSystemMail((!empty($admin['language'])) ? DI::l10n()->withLang($admin['language']) : DI::l10n()->withLang('en'))
+                          ->withMessage(DI::l10n()->t('[Friendica System Notify]') . ' ' . DI::l10n()->t('User deleted their account'),
+                                  DI::l10n()->t('On your Friendica node an user deleted their account. Please ensure that their data is removed from the backups.'),
+                                  DI::l10n()->t('The user id is %d', local_user()))
+                          ->forUser($admin['uid'] ?? 0)
+                          ->withRecipient($admin['email'])
+                          ->build();
+               DI::emailer()->send($email);
        }
 
        if (User::getIdFromPasswordAuthentication($a->user, trim($_POST['qxz_password']))) {
index 5a151b8380916311b057c04d1c65a9a6e3140e34..2a3b3a68d69ad9b26bcd37cc6a9578ca0ddb8c1a 100644 (file)
@@ -252,7 +252,7 @@ class Update
                        }
                        $sent[] = $admin['email'];
 
-                       $lang = (($admin['language'])?$admin['language']:'en');
+                       $lang = (($admin['language']) ? $admin['language'] : 'en');
                        $l10n = DI::l10n()->withLang($lang);
 
                        $preamble = Strings::deindent($l10n->t("
@@ -261,17 +261,15 @@ class Update
                                This needs to be fixed soon and I can't do it alone. Please contact a
                                friendica developer if you can not help me on your own. My database might be invalid.",
                                $update_id));
-                       $body = $l10n->t("The error message is\n[pre]%s[/pre]", $error_message);
-
-                       notification([
-                                       'uid'      => $admin['uid'],
-                                       'type'     => SYSTEM_EMAIL,
-                                       'to_email' => $admin['email'],
-                                       'subject'  => $l10n->t('[Friendica Notify] Database update'),
-                                       'preamble' => $preamble,
-                                       'body'     => $body,
-                                       'language' => $lang]
-                       );
+                       $body     = $l10n->t("The error message is\n[pre]%s[/pre]", $error_message);
+
+                       $email = DI::emailer()
+                                  ->newSystemMail($l10n)
+                                  ->withMessage($l10n->t('[Friendica Notify] Database update'), $preamble, $body)
+                                  ->forUser($admin['uid'] ?? 0)
+                                  ->withRecipient($admin['email'])
+                                  ->build();
+                       DI::emailer()->send($email);
                }
 
                //try the logger
@@ -301,15 +299,13 @@ class Update
                                        The friendica database was successfully updated from %s to %s.",
                                        $from_build, $to_build));
 
-                               notification([
-                                               'uid' => $admin['uid'],
-                                               'type' => SYSTEM_EMAIL,
-                                               'to_email' => $admin['email'],
-                                               'subject'  => DI::l10n()->t('[Friendica Notify] Database update'),
-                                               'preamble' => $preamble,
-                                               'body' => $preamble,
-                                               'language' => $lang]
-                               );
+                               $email = DI::emailer()
+                                          ->newSystemMail($l10n)
+                                          ->withMessage($l10n->t('[Friendica Notify] Database update'), $preamble)
+                                          ->forUser($admin['uid'] ?? 0)
+                                          ->withRecipient($admin['email'])
+                                          ->build();
+                               DI::emailer()->send($email);
                        }
                }
 
index fd7238819d23ad293d1df1e7114ca90c0287889d..cc6b96ae5853cd564b2cd1a2fcbd22b888f5bbb4 100644 (file)
@@ -897,13 +897,13 @@ class User
                        $password
                ));
 
-               return notification([
-                       'type'     => SYSTEM_EMAIL,
-                       'uid'      => $user['uid'],
-                       'to_email' => $user['email'],
-                       'subject'  => DI::l10n()->t('Registration at %s', $sitename),
-                       'body'     => $body
-               ]);
+               $email = DI::emailer()
+                          ->newSystemMail(DI::l10n())
+                          ->withMessage(DI::l10n()->t('Registration at %s', $sitename), $body)
+                          ->forUser($user['uid'] ?? 0)
+                          ->withRecipient($user['email'])
+                          ->build();
+               return DI::emailer()->send($email);
        }
 
        /**
@@ -965,15 +965,13 @@ class User
                        $password
                ));
 
-               return notification([
-                       'uid'      => $user['uid'],
-                       'language' => $user['language'],
-                       'type'     => SYSTEM_EMAIL,
-                       'to_email' => $user['email'],
-                       'subject'  => DI::l10n()->t('Registration details for %s', $sitename),
-                       'preamble' => $preamble,
-                       'body'     => $body
-               ]);
+               $email = DI::emailer()
+                          ->newSystemMail((!empty($user['language'])) ? DI::l10n()->withLang($user['language']) : DI::l10n())
+                          ->withMessage(DI::l10n()->t('Registration details for %s', $sitename), $preamble, $body)
+                          ->forUser($user['uid'] ?? 0)
+                          ->withRecipient($user['email'])
+                          ->build();
+               return DI::emailer()->send($email);
        }
 
        /**
index 0498a80da12d5c1a88060bb49d6a187187b3c3e6..5ca50d11ac51de4e8b2623d9d99a2d4733fd5f99 100644 (file)
@@ -76,15 +76,13 @@ class Users extends BaseAdmin
                        $preamble = sprintf($preamble, $user['username'], DI::config()->get('config', 'sitename'));
                        $body = sprintf($body, DI::baseUrl()->get(), $user['nickname'], $result['password'], DI::config()->get('config', 'sitename'));
 
-                       notification([
-                               'type'     => SYSTEM_EMAIL,
-                               'language' => $user['language'],
-                               'to_name'  => $user['username'],
-                               'to_email' => $user['email'],
-                               'uid'      => $user['uid'],
-                               'subject'  => DI::l10n()->t('Registration details for %s', DI::config()->get('config', 'sitename')),
-                               'preamble' => $preamble,
-                               'body'     => $body]);
+                       $email = DI::emailer()
+                                  ->newSystemMail((!empty($user['language'])) ? DI::l10n()->withLang($user['language']) : DI::l10n())
+                                  ->withMessage(DI::l10n()->t('Registration details for %s', DI::config()->get('config', 'sitename')), $preamble, $body)
+                                  ->forUser($user['uid'] ?? 0)
+                                  ->withRecipient($user['email'])
+                                  ->build();
+                       return DI::emailer()->send($email);
                }
 
                if (!empty($_POST['page_users_block'])) {
index ec8ae88158bf509f07323f6d5d25858c1572a1bf..220cddc42fd8ca9c2cc0cd9f8470cbc9d5589344 100644 (file)
@@ -2,6 +2,7 @@
 
 namespace Friendica\Object\EMail;
 
+use Friendica\Core\L10n;
 use Friendica\Util\Emailer;
 use JsonSerializable;
 
diff --git a/src/Util/EMailer/MailBuilder.php b/src/Util/EMailer/MailBuilder.php
new file mode 100644 (file)
index 0000000..419edfc
--- /dev/null
@@ -0,0 +1,186 @@
+<?php
+
+namespace Friendica\Util\EMailer;
+
+use Exception;
+use Friendica\App\BaseURL;
+use Friendica\Core\Config\IConfig;
+use Friendica\Core\L10n;
+use Friendica\Core\Renderer;
+use Friendica\Model\User;
+use Friendica\Network\HTTPException\InternalServerErrorException;
+use Friendica\Object\Email;
+use Friendica\Object\EMail\IEmail;
+
+/**
+ * A base class for building new emails
+ */
+abstract class MailBuilder
+{
+       /** @var L10n */
+       protected $l10n;
+       /** @var IConfig */
+       protected $config;
+       /** @var BaseURL */
+       protected $baseUrl;
+
+       /** @var string */
+       protected $headers;
+
+       /** @var string */
+       protected $senderName = null;
+       /** @var string */
+       protected $senderAddress = null;
+       /** @var string */
+       protected $senderNoReply = null;
+
+       /** @var string */
+       protected $recipientAddress = null;
+       /** @var int */
+       protected $recipientUid = null;
+
+       public function __construct(L10n $l10n, BaseURL $baseUrl, IConfig $config)
+       {
+               $this->l10n    = $l10n;
+               $this->baseUrl = $baseUrl;
+               $this->config  = $config;
+
+               $hostname = $baseUrl->getHostname();
+               if (strpos($hostname, ':')) {
+                       $hostname = substr($hostname, 0, strpos($hostname, ':'));
+               }
+
+               $this->headers = "";
+               $this->headers .= "Precedence: list\n";
+               $this->headers .= "X-Friendica-Host: " . $hostname . "\n";
+               $this->headers .= "X-Friendica-Platform: " . FRIENDICA_PLATFORM . "\n";
+               $this->headers .= "X-Friendica-Version: " . FRIENDICA_VERSION . "\n";
+               $this->headers .= "List-ID: <notification." . $hostname . ">\n";
+               $this->headers .= "List-Archive: <" . $baseUrl->get() . "/notifications/system>\n";
+       }
+
+       /**
+        * Gets the subject of the concrete builder, which inherits this base class
+        *
+        * @return string
+        */
+       abstract protected function getSubject();
+
+       /**
+        * Gets the HTML version of the body of the concrete builder, which inherits this base class
+        *
+        * @return string
+        */
+       abstract protected function getHtmlMessage();
+
+       /**
+        * Gets the Plaintext version of the body of the concrete builder, which inherits this base class
+        *
+        * @return string
+        */
+       abstract protected function getPlaintextMessage();
+
+       /**
+        * Adds the User ID to the email in case the mail sending needs additional properties of this user
+        *
+        * @param int $uid The User ID
+        *
+        * @return static
+        */
+       public function forUser(int $uid)
+       {
+               $this->recipientUid = $uid;
+
+               return $this;
+       }
+
+       /**
+        * Adds the sender to the email (if not called/set, the sender will get loaded with the help of the user id)
+        *
+        * @param string      $name    The name of the sender
+        * @param string      $address The (email) address of the sender
+        * @param string|null $noReply Optional "no-reply" (email) address (if not set, it's the same as the address)
+        *
+        * @return static
+        */
+       public function withSender(string $name, string $address, string $noReply = null)
+       {
+               $this->senderName    = $name;
+               $this->senderAddress = $address;
+               $this->senderNoReply = $noReply ?? $this->senderNoReply;
+
+               return $this;
+       }
+
+       /**
+        * Adds a recipient to the email
+        *
+        * @param string $address The (email) address of the recipient
+        *
+        * @return static
+        */
+       public function withRecipient(string $address)
+       {
+               $this->recipientAddress = $address;
+
+               return $this;
+       }
+
+       /**
+        * Build a email based on the given attributes
+        *
+        * @param bool $raw True, if the email shouldn't get extended by the default email-template
+        *
+        * @return IEmail A new generated email
+        *
+        * @throws InternalServerErrorException
+        * @throws Exception
+        */
+       public function build(bool $raw = false)
+       {
+               if (empty($this->recipientAddress)) {
+                       throw new InternalServerErrorException('Recipient address is missing.');
+               }
+
+               if ((empty($this->senderName) || empty($this->senderAddress)) &&
+                   !empty($this->recipientUid)) {
+                       $user = User::getById($this->recipientUid, ['username', 'email']);
+
+                       if (!empty($user)) {
+                               $this->senderName    = $user['username'];
+                               $this->senderAddress = $user['email'];
+                       }
+               }
+
+               if (empty($this->senderAddress) || empty($this->senderName)) {
+                       throw new InternalServerErrorException('Sender address or name is missing.');
+               }
+
+               $this->senderNoReply = $this->senderNoReply ?? $this->senderAddress;
+
+               $msgHtml = $this->getHtmlMessage() ?? '';
+
+               if (!$raw) {
+                       // load the template for private message notifications
+                       $tpl     = Renderer::getMarkupTemplate('email/notify/html.tpl');
+                       $msgHtml = Renderer::replaceMacros($tpl, [
+                               '$banner'      => $this->l10n->t('Friendica Notification'),
+                               '$product'     => FRIENDICA_PLATFORM,
+                               '$htmlversion' => $msgHtml,
+                               '$sitename'    => $this->config->get('config', 'sitename'),
+                               '$siteurl'     => $this->baseUrl->get(true),
+                       ]);
+               }
+
+               return new Email(
+                       $this->senderName,
+                       $this->senderAddress,
+                       $this->senderNoReply,
+                       $this->recipientAddress,
+                       $this->getSubject() ?? '',
+                       $msgHtml,
+                       $this->getPlaintextMessage() ?? '',
+                       $this->headers,
+                       $this->recipientUid ?? null);
+       }
+}
diff --git a/src/Util/EMailer/SystemMailBuilder.php b/src/Util/EMailer/SystemMailBuilder.php
new file mode 100644 (file)
index 0000000..56ae620
--- /dev/null
@@ -0,0 +1,110 @@
+<?php
+
+namespace Friendica\Util\EMailer;
+
+use Exception;
+use Friendica\App\BaseURL;
+use Friendica\Content\Text\BBCode;
+use Friendica\Core\Config\IConfig;
+use Friendica\Core\L10n;
+use Friendica\Core\Renderer;
+use Friendica\Network\HTTPException\InternalServerErrorException;
+use Friendica\Util\Emailer;
+
+/**
+ * Builder for system-wide emails without any dependency to concrete entities (like items, activities, ..)
+ */
+class SystemMailBuilder extends MailBuilder
+{
+       /** @var string */
+       protected $subject;
+       /** @var string */
+       protected $preamble;
+       /** @var string */
+       protected $body;
+
+       /** @var string */
+       protected $siteAdmin;
+
+       public function __construct(L10n $l10n, BaseURL $baseUrl, IConfig $config)
+       {
+               parent::__construct($l10n, $baseUrl, $config);
+
+               $siteName = $this->config->get('config', 'sitename');
+
+               if ($this->config->get('config', 'admin_name')) {
+                       $this->siteAdmin = $l10n->t('%1$s, %2$s Administrator', $this->config->get('config', 'admin_name'), $siteName);
+               } else {
+                       $this->siteAdmin = $l10n->t('%s Administrator', $siteName);
+               }
+       }
+
+       /**
+        * Adds a message
+        *
+        * @param string      $subject  The subject of the email
+        * @param string      $preamble The preamble of the email
+        * @param string|null $body     The body of the email (if not set, the preamble will get used as body)
+        *
+        * @return static
+        */
+       public function withMessage(string $subject, string $preamble, string $body = null)
+       {
+               if (!isset($body)) {
+                       $body = $preamble;
+               }
+
+               $this->subject  = $subject;
+               $this->preamble = $preamble;
+               $this->body     = $body;
+
+               return $this;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       protected function getSubject()
+       {
+               return $this->subject;
+       }
+
+       /**
+        * {@inheritDoc}
+        *
+        * @throws InternalServerErrorException
+        * @throws Exception
+        */
+       protected function getHtmlMessage()
+       {
+               $htmlVersion = BBCode::convert($this->body);
+
+               // load the template for private message notifications
+               $tpl = Renderer::getMarkupTemplate('email/system/html.tpl');
+               return Renderer::replaceMacros($tpl, [
+                       '$preamble'    => str_replace("\n", "<br>\n", $this->preamble),
+                       '$thanks'      => $this->l10n->t('thanks'),
+                       '$site_admin'  => $this->siteAdmin,
+                       '$htmlversion' => $htmlVersion,
+               ]);
+       }
+
+       /**
+        * {@inheritDoc}
+        *
+        * @throws Exception
+        */
+       protected function getPlaintextMessage()
+       {
+               $textVersion = BBCode::toPlaintext($this->body);
+
+               // load the template for private message notifications
+               $tpl = Renderer::getMarkupTemplate('email/system/text.tpl');
+               return Renderer::replaceMacros($tpl, [
+                       '$preamble'    => $this->preamble,
+                       '$thanks'      => $this->l10n->t('thanks'),
+                       '$site_admin'  => $this->siteAdmin,
+                       '$textversion' => $textVersion,
+               ]);
+       }
+}
index 19755bebdae594563de7be44f32fa16096116d36..ed4ea4d87c5606ee0c69297e1df1556384d17284 100644 (file)
@@ -7,10 +7,12 @@ namespace Friendica\Util;
 use Friendica\App;
 use Friendica\Core\Config\IConfig;
 use Friendica\Core\Hook;
+use Friendica\Core\L10n;
 use Friendica\Core\PConfig\IPConfig;
 use Friendica\Network\HTTPException\InternalServerErrorException;
 use Friendica\Object\EMail\IEmail;
 use Friendica\Protocol\Email;
+use Friendica\Util\EMailer\SystemMailBuilder;
 use Psr\Log\LoggerInterface;
 
 /**
@@ -35,6 +37,18 @@ class Emailer
                $this->baseUrl     = $baseURL;
        }
 
+       /**
+        * Creates a new system email
+        *
+        * @param L10n $l10n The chosen language for the new email
+        *
+        * @return SystemMailBuilder
+        */
+       public function newSystemMail(L10n $l10n)
+       {
+               return new SystemMailBuilder($l10n, $this->baseUrl, $this->config);
+       }
+
        /**
         * Send a multipart/alternative message with Text and HTML versions
         *
diff --git a/tests/Util/SampleMailBuilder.php b/tests/Util/SampleMailBuilder.php
new file mode 100644 (file)
index 0000000..59638ee
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+
+namespace Friendica\Test\Util;
+
+use Friendica\Util\EMailer\MailBuilder;
+
+class SampleMailBuilder extends MailBuilder
+{
+       /** @var string */
+       protected $subject;
+       /** @var string */
+       protected $html;
+       /** @var string */
+       protected $text;
+
+       /**
+        * Adds a test message
+        *
+        * @param string $subject The subject of the email
+        * @param string $html    The preamble of the email
+        * @param string $text    The body of the email (if not set, the preamble will get used as body)
+        *
+        * @return static
+        */
+       public function withMessage(string $subject, string $html, string $text)
+       {
+               $this->subject = $subject;
+               $this->html    = $html;
+               $this->text    = $text;
+
+               return $this;
+       }
+
+       /**
+        * @inheritDoc
+        */
+       protected function getSubject()
+       {
+               return $this->subject;
+       }
+
+       /**
+        * @inheritDoc
+        */
+       protected function getHtmlMessage()
+       {
+               return $this->html;
+       }
+
+       /**
+        * @inheritDoc
+        */
+       protected function getPlaintextMessage()
+       {
+               return $this->text;
+       }
+}
diff --git a/tests/src/Util/Emailer/MailBuilderTest.php b/tests/src/Util/Emailer/MailBuilderTest.php
new file mode 100644 (file)
index 0000000..564e79f
--- /dev/null
@@ -0,0 +1,176 @@
+<?php
+
+namespace Friendica\Test\src\Util\Emailer;
+
+use Friendica\App\BaseURL;
+use Friendica\Core\Config\IConfig;
+use Friendica\Core\L10n;
+use Friendica\Object\EMail\IEmail;
+use Friendica\Test\MockedTest;
+use Friendica\Test\Util\SampleMailBuilder;
+use Friendica\Test\Util\VFSTrait;
+use Friendica\Util\EMailer\MailBuilder;
+
+/**
+ * This class tests the "MailBuilder" (@see MailBuilder )
+ * Since it's an abstract class and every extended class of it has dependencies, we use a "SampleMailBuilder" (@see SampleMailBuilder ) to make this class work
+ */
+class MailBuilderTest extends MockedTest
+{
+       use VFSTrait;
+
+       /** @var IConfig */
+       private $config;
+       /** @var L10n */
+       private $l10n;
+       /** @var BaseURL */
+       private $baseUrl;
+
+       /** @var string */
+       private $defaultHeaders;
+
+       public function setUp()
+       {
+               parent::setUp();
+
+               $this->setUpVfsDir();
+
+               $this->config  = \Mockery::mock(IConfig::class);
+               $this->l10n    = \Mockery::mock(L10n::class);
+               $this->baseUrl = \Mockery::mock(BaseURL::class);
+               $this->baseUrl->shouldReceive('getHostname')->andReturn('friendica.local');
+               $this->baseUrl->shouldReceive('get')->andReturn('http://friendica.local');
+
+               $this->defaultHeaders = "";
+       }
+
+       public function assertEmail(IEmail $email, array $asserts)
+       {
+               $this->assertEquals($asserts['subject'] ?? $email->getSubject(), $email->getSubject());
+               $this->assertEquals($asserts['html'] ?? $email->getMessage(), $email->getMessage());
+               $this->assertEquals($asserts['text'] ?? $email->getMessage(true), $email->getMessage(true));
+               $this->assertEquals($asserts['toAddress'] ?? $email->getToAddress(), $email->getToAddress());
+               $this->assertEquals($asserts['fromAddress'] ?? $email->getFromAddress(), $email->getFromAddress());
+               $this->assertEquals($asserts['fromName'] ?? $email->getFromName(), $email->getFromName());
+               $this->assertEquals($asserts['replyTo'] ?? $email->getReplyTo(), $email->getReplyTo());
+               $this->assertEquals($asserts['uid'] ?? $email->getRecipientUid(), $email->getRecipientUid());
+               $this->assertEquals($asserts['header'] ?? $email->getAdditionalMailHeader(), $email->getAdditionalMailHeader());
+       }
+
+       /**
+        * Test if the builder instance can get created
+        */
+       public function testBuilderInstance()
+       {
+               $builder = new SampleMailBuilder($this->l10n, $this->baseUrl, $this->config);
+
+               $this->assertInstanceOf(MailBuilder::class, $builder);
+       }
+
+       /**
+        * Test if the builder can create full rendered emails
+        *
+        * @todo Create test once "Renderer" and "BBCode" are dynamic
+        */
+       public function testBuilderWithNonRawEmail()
+       {
+               $this->markTestIncomplete('Cannot easily mock Renderer and BBCode, so skipping tests wit them');
+       }
+
+       /**
+        * Test if the builder can create a "simple" raw mail
+        */
+       public function testBuilderWithRawEmail()
+       {
+               $builder = new SampleMailBuilder($this->l10n, $this->baseUrl, $this->config);
+
+               $testEmail = $builder
+                       ->withMessage('Subject', 'Html', 'text')
+                       ->withRecipient('recipient@friendica.local')
+                       ->withSender('Sender', 'sender@friendica.local', 'no-reply@friendica.local')
+                       ->forUser(100)
+                       ->build(true);
+
+               $this->assertEmail($testEmail, [
+                       'subject' => 'Subject',
+                       'html' => 'Html',
+                       'text' => 'text',
+                       'toAddress' => 'recipient@friendica.local',
+                       'fromName' => 'Sender',
+                       'fromAddress' => 'sender@friendica.local',
+                       'noReply' => 'no-reply@friendica.local',
+                       'uid' => 100,
+                       'headers' => $this->defaultHeaders,
+               ]);
+       }
+
+       /**
+        * Test if the builder throws an exception in case no recipient
+        *
+        * @expectedException \Friendica\Network\HTTPException\InternalServerErrorException
+        * @expectedExceptionMessage Recipient address is missing.
+        */
+       public function testBuilderWithEmptyMail()
+       {
+               $builder = new SampleMailBuilder($this->l10n, $this->baseUrl, $this->config);
+
+               $builder->build(true);
+       }
+
+       /**
+        * Test if the builder throws an exception in case no sender
+        *
+        * @expectedException \Friendica\Network\HTTPException\InternalServerErrorException
+        * @expectedExceptionMessage Sender address or name is missing.
+        */
+       public function testBuilderWithEmptySender()
+       {
+               $builder = new SampleMailBuilder($this->l10n, $this->baseUrl, $this->config);
+
+               $builder
+                       ->withRecipient('test@friendica.local')
+                       ->build(true);
+       }
+
+       /**
+        * Test if the builder is capable of creating "empty" mails if needed (not the decision of the builder if so ..)
+        */
+       public function testBuilderWithoutMessage()
+       {
+               $builder = new SampleMailBuilder($this->l10n, $this->baseUrl, $this->config);
+
+               $testEmail = $builder
+                       ->withRecipient('recipient@friendica.local')
+                       ->withSender('Sender', 'sender@friendica.local')
+                       ->build(true);
+
+               $this->assertEmail($testEmail, [
+                       'toAddress' => 'recipient@friendica.local',
+                       'fromName' => 'Sender',
+                       'fromAddress' => 'sender@friendica.local',
+                       'noReply' => 'sender@friendica.local', // no-reply is set same as address in case it's not set
+                       'headers' => $this->defaultHeaders,
+               ]);
+       }
+
+       /**
+        * Test if the builder sets for the text the same as for
+        */
+       public function testBuilderWithJustPreamble()
+       {
+               $builder = new SampleMailBuilder($this->l10n, $this->baseUrl, $this->config);
+
+               $testEmail = $builder
+                       ->withRecipient('recipient@friendica.local')
+                       ->withSender('Sender', 'sender@friendica.local')
+                       ->build(true);
+
+               $this->assertEmail($testEmail, [
+                       'toAddress' => 'recipient@friendica.local',
+                       'fromName' => 'Sender',
+                       'fromAddress' => 'sender@friendica.local',
+                       'noReply' => 'sender@friendica.local', // no-reply is set same as address in case it's not set,
+                       'headers' => $this->defaultHeaders,
+               ]);
+       }
+}
diff --git a/view/templates/email/html.tpl b/view/templates/email/html.tpl
new file mode 100644 (file)
index 0000000..65af182
--- /dev/null
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional //EN">
+<html>
+<head>
+       <title>{{$banner}}</title>
+       <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+</head>
+<body>
+       <table style="border:1px solid #ccc">
+       <tbody>
+               <tr>
+                       <td colspan="2" style="background:#084769; color:#FFFFFF; font-weight:bold; font-family:'lucida grande', tahoma, verdana,arial, sans-serif; padding: 4px 8px; vertical-align: middle; font-size:16px; letter-spacing: -0.03em; text-align: left;">
+                               <img style="width:32px;height:32px; float:left;" src='{{$siteurl}}/images/friendica-32.png'>
+                               <div style="padding:7px; margin-left: 5px; float:left; font-size:18px;letter-spacing:1px;">{{$product}}</div>
+                               <div style="clear: both;"></div>
+                       </td>
+               </tr>
+
+               {{$htmlversion nofilter}}
+       </tbody>
+       </table>
+</body>
+</html>
diff --git a/view/templates/email/notify/notify_html.tpl b/view/templates/email/notify/notify_html.tpl
new file mode 100644 (file)
index 0000000..d9eeb84
--- /dev/null
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional //EN">
+<html>
+<head>
+       <title>{{$banner}}</title>
+       <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+</head>
+<body>
+       <table style="border:1px solid #ccc">
+       <tbody>
+               <tr>
+                       <td colspan="2" style="background:#084769; color:#FFFFFF; font-weight:bold; font-family:'lucida grande', tahoma, verdana,arial, sans-serif; padding: 4px 8px; vertical-align: middle; font-size:16px; letter-spacing: -0.03em; text-align: left;">
+                               <img style="width:32px;height:32px; float:left;" src='{{$siteurl}}/images/friendica-32.png'>
+                               <div style="padding:7px; margin-left: 5px; float:left; font-size:18px;letter-spacing:1px;">{{$product}}</div>
+                               <div style="clear: both;"></div>
+                       </td>
+               </tr>
+
+               <tr><td colspan="2" style="padding-top:22px;">{{$preamble nofilter}}</td></tr>
+
+{{if $content_allowed}}
+       {{if $source_photo}}
+               <tr>
+                       <td style="padding-left:22px;padding-top:22px;width:60px;" valign="top" rowspan=3><a href="{{$source_link}}"><img style="border:0px;width:48px;height:48px;" src="{{$source_photo}}"></a></td>
+                       <td style="padding-top:22px;"><a href="{{$source_link}}">{{$source_name}}</a></td>
+               </tr>
+               {{/if}}
+               <tr><td style="font-weight:bold;padding-bottom:5px;">{{$title}}</td></tr>
+               <tr><td style="padding-right:22px;">{{$htmlversion nofilter}}</td></tr>
+{{/if}}
+               <tr><td colspan="2" style="padding-top:11px;">{{$hsitelink nofilter}}</td></tr>
+               <tr><td colspan="2" style="padding-bottom:11px;">{{$hitemlink nofilter}}</td></tr>
+               <tr><td></td><td>{{$thanks}}</td></tr>
+               <tr><td></td><td>{{$site_admin}}</td></tr>
+       </tbody>
+       </table>
+</body>
+</html>
diff --git a/view/templates/email/notify/notify_text.tpl b/view/templates/email/notify/notify_text.tpl
new file mode 100644 (file)
index 0000000..28b644d
--- /dev/null
@@ -0,0 +1,14 @@
+
+{{$preamble nofilter}}
+
+{{if $content_allowed}}
+{{$title nofilter}}
+
+{{$textversion nofilter}}
+
+{{/if}}
+{{$tsitelink nofilter}}
+{{$titemlink nofilter}}
+
+{{$thanks nofilter}}
+{{$site_admin nofilter}}
diff --git a/view/templates/email/notify_html.tpl b/view/templates/email/notify_html.tpl
deleted file mode 100644 (file)
index d9eeb84..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional //EN">
-<html>
-<head>
-       <title>{{$banner}}</title>
-       <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
-</head>
-<body>
-       <table style="border:1px solid #ccc">
-       <tbody>
-               <tr>
-                       <td colspan="2" style="background:#084769; color:#FFFFFF; font-weight:bold; font-family:'lucida grande', tahoma, verdana,arial, sans-serif; padding: 4px 8px; vertical-align: middle; font-size:16px; letter-spacing: -0.03em; text-align: left;">
-                               <img style="width:32px;height:32px; float:left;" src='{{$siteurl}}/images/friendica-32.png'>
-                               <div style="padding:7px; margin-left: 5px; float:left; font-size:18px;letter-spacing:1px;">{{$product}}</div>
-                               <div style="clear: both;"></div>
-                       </td>
-               </tr>
-
-               <tr><td colspan="2" style="padding-top:22px;">{{$preamble nofilter}}</td></tr>
-
-{{if $content_allowed}}
-       {{if $source_photo}}
-               <tr>
-                       <td style="padding-left:22px;padding-top:22px;width:60px;" valign="top" rowspan=3><a href="{{$source_link}}"><img style="border:0px;width:48px;height:48px;" src="{{$source_photo}}"></a></td>
-                       <td style="padding-top:22px;"><a href="{{$source_link}}">{{$source_name}}</a></td>
-               </tr>
-               {{/if}}
-               <tr><td style="font-weight:bold;padding-bottom:5px;">{{$title}}</td></tr>
-               <tr><td style="padding-right:22px;">{{$htmlversion nofilter}}</td></tr>
-{{/if}}
-               <tr><td colspan="2" style="padding-top:11px;">{{$hsitelink nofilter}}</td></tr>
-               <tr><td colspan="2" style="padding-bottom:11px;">{{$hitemlink nofilter}}</td></tr>
-               <tr><td></td><td>{{$thanks}}</td></tr>
-               <tr><td></td><td>{{$site_admin}}</td></tr>
-       </tbody>
-       </table>
-</body>
-</html>
diff --git a/view/templates/email/notify_text.tpl b/view/templates/email/notify_text.tpl
deleted file mode 100644 (file)
index 28b644d..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-
-{{$preamble nofilter}}
-
-{{if $content_allowed}}
-{{$title nofilter}}
-
-{{$textversion nofilter}}
-
-{{/if}}
-{{$tsitelink nofilter}}
-{{$titemlink nofilter}}
-
-{{$thanks nofilter}}
-{{$site_admin nofilter}}
diff --git a/view/templates/email/system/html.tpl b/view/templates/email/system/html.tpl
new file mode 100644 (file)
index 0000000..ebef956
--- /dev/null
@@ -0,0 +1,3 @@
+<tr><td style="padding-right:22px;">{{$htmlversion nofilter}}</td></tr>
+<tr><td></td><td>{{$thanks}}</td></tr>
+<tr><td></td><td>{{$site_admin}}</td></tr>
\ No newline at end of file
diff --git a/view/templates/email/system/text.tpl b/view/templates/email/system/text.tpl
new file mode 100644 (file)
index 0000000..4dd7c8c
--- /dev/null
@@ -0,0 +1,7 @@
+
+{{$preamble nofilter}}
+
+{{$textversion nofilter}}
+
+{{$thanks nofilter}}
+{{$site_admin nofilter}}