Merge pull request #8215 from nupplaphil/task/extract_email

Cleanup enotify & Extract System emails
This commit is contained in:
Hypolite Petovan 2020-02-04 15:10:27 -05:00 committed by GitHub
commit e37b1c8794
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 783 additions and 152 deletions

View file

@ -152,8 +152,6 @@ define('NOTIFY_TAGSHARE', 256);
define('NOTIFY_POKE', 512); define('NOTIFY_POKE', 512);
define('NOTIFY_SHARE', 1024); define('NOTIFY_SHARE', 1024);
define('SYSTEM_EMAIL', 16384);
define('NOTIFY_SYSTEM', 32768); define('NOTIFY_SYSTEM', 32768);
/* @}*/ /* @}*/

View file

@ -72,20 +72,15 @@ function notification($params)
$hostname = substr($hostname, 0, strpos($hostname, ':')); $hostname = substr($hostname, 0, strpos($hostname, ':'));
} }
$sender_email = $a->getSenderEmailAddress(); $sender_email = DI::emailer()->getSiteEmailAddress();
if ($params['type'] != SYSTEM_EMAIL) { $user = User::getById($params['uid'], ['nickname', 'page-flags']);
$user = DBA::selectFirst('user', ['nickname', 'page-flags'],
['uid' => $params['uid']]);
// There is no need to create notifications for forum accounts // 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])) { if (!DBA::isResult($user) || in_array($user["page-flags"], [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP])) {
return false; return false;
} }
$nickname = $user["nickname"]; $nickname = $user["nickname"];
} else {
$nickname = '';
}
// with $params['show_in_notification_page'] == false, the notification isn't inserted into // with $params['show_in_notification_page'] == false, the notification isn't inserted into
// the database, and an email is sent if applicable. // the database, and an email is sent if applicable.
@ -428,30 +423,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.")"; $subject .= " (".$nickname."@".$hostname.")";
$h = [ $h = [
@ -506,8 +477,7 @@ function notification($params)
// send email notification if notification preferences permit // send email notification if notification preferences permit
if ((intval($params['notify_flags']) & intval($params['type'])) if ((intval($params['notify_flags']) & intval($params['type']))
|| $params['type'] == NOTIFY_SYSTEM || $params['type'] == NOTIFY_SYSTEM) {
|| $params['type'] == SYSTEM_EMAIL) {
Logger::log('sending notification email'); Logger::log('sending notification email');
@ -548,7 +518,6 @@ function notification($params)
$datarray['source_link'] = $params['source_link'] ?? ''; $datarray['source_link'] = $params['source_link'] ?? '';
$datarray['source_photo'] = $params['source_photo'] ?? ''; $datarray['source_photo'] = $params['source_photo'] ?? '';
$datarray['uid'] = $params['uid']; $datarray['uid'] = $params['uid'];
$datarray['username'] = $params['to_name'] ?? '';
$datarray['hsitelink'] = $hsitelink; $datarray['hsitelink'] = $hsitelink;
$datarray['tsitelink'] = $tsitelink; $datarray['tsitelink'] = $tsitelink;
$datarray['hitemlink'] = '<a href="' . $itemlink . '">' . $itemlink . '</a>'; $datarray['hitemlink'] = '<a href="' . $itemlink . '">' . $itemlink . '</a>';
@ -564,11 +533,10 @@ function notification($params)
Hook::callAll('enotify_mail', $datarray); Hook::callAll('enotify_mail', $datarray);
// check whether sending post content in email notifications is allowed // check whether sending post content in email notifications is allowed
// always true for SYSTEM_EMAIL $content_allowed = (!DI::config()->get('system', 'enotify_no_content'));
$content_allowed = ((!DI::config()->get('system', 'enotify_no_content')) || ($params['type'] == SYSTEM_EMAIL));
// load the template for private message notifications // 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, [ $email_html_body = Renderer::replaceMacros($tpl, [
'$banner' => $datarray['banner'], '$banner' => $datarray['banner'],
'$product' => $datarray['product'], '$product' => $datarray['product'],
@ -578,7 +546,6 @@ function notification($params)
'$source_name' => $datarray['source_name'], '$source_name' => $datarray['source_name'],
'$source_link' => $datarray['source_link'], '$source_link' => $datarray['source_link'],
'$source_photo' => $datarray['source_photo'], '$source_photo' => $datarray['source_photo'],
'$username' => $datarray['username'],
'$hsitelink' => $datarray['hsitelink'], '$hsitelink' => $datarray['hsitelink'],
'$hitemlink' => $datarray['hitemlink'], '$hitemlink' => $datarray['hitemlink'],
'$thanks' => $datarray['thanks'], '$thanks' => $datarray['thanks'],
@ -589,17 +556,9 @@ function notification($params)
]); ]);
// load the template for private message notifications // 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, [ $email_text_body = Renderer::replaceMacros($tpl, [
'$banner' => $datarray['banner'],
'$product' => $datarray['product'],
'$preamble' => $datarray['preamble'], '$preamble' => $datarray['preamble'],
'$sitename' => $datarray['sitename'],
'$siteurl' => $datarray['siteurl'],
'$source_name' => $datarray['source_name'],
'$source_link' => $datarray['source_link'],
'$source_photo' => $datarray['source_photo'],
'$username' => $datarray['username'],
'$tsitelink' => $datarray['tsitelink'], '$tsitelink' => $datarray['tsitelink'],
'$titemlink' => $datarray['titemlink'], '$titemlink' => $datarray['titemlink'],
'$thanks' => $datarray['thanks'], '$thanks' => $datarray['thanks'],

View file

@ -64,17 +64,14 @@ function lostpass_post(App $a)
Site Location: %2$s Site Location: %2$s
Login Name: %3$s', $resetlink, DI::baseUrl(), $user['nickname'])); Login Name: %3$s', $resetlink, DI::baseUrl(), $user['nickname']));
notification([ $email = DI::emailer()
'type' => SYSTEM_EMAIL, ->newSystemMail()
'language' => $user['language'], ->withMessage(DI::l10n()->t('Password reset requested at %s', $sitename), $preamble, $body)
'to_name' => $user['username'], ->forUser($user)
'to_email' => $user['email'], ->withRecipient($user['email'])
'uid' => $user['uid'], ->build();
'subject' => DI::l10n()->t('Password reset requested at %s', $sitename),
'preamble' => $preamble,
'body' => $body
]);
DI::emailer()->send($email);
DI::baseUrl()->redirect(); 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. You may change that password from your account settings page after logging in.
', DI::baseUrl(), $user['nickname'], $new_password)); ', DI::baseUrl(), $user['nickname'], $new_password));
notification([ $email = DI::emailer()
'type' => SYSTEM_EMAIL, ->newSystemMail()
'language' => $user['language'], ->withMessage(DI::l10n()->t('Your password has been changed at %s', $sitename), $preamble, $body)
'to_name' => $user['username'], ->forUser($user)
'to_email' => $user['email'], ->withRecipient($user['email'])
'uid' => $user['uid'], ->build();
'subject' => DI::l10n()->t('Your password has been changed at %s', $sitename), DI::emailer()->send($email);
'preamble' => $preamble,
'body' => $body
]);
} }
return $o; return $o;

View file

@ -40,17 +40,17 @@ function removeme_post(App $a)
if (!DBA::isResult($admin)) { if (!DBA::isResult($admin)) {
continue; continue;
} }
notification([
'type' => SYSTEM_EMAIL, $email = DI::emailer()
'subject' => DI::l10n()->t('[Friendica System Notify]') . ' ' . DI::l10n()->t('User deleted their account'), ->newSystemMail()
'preamble' => DI::l10n()->t('On your Friendica node an user deleted their account. Please ensure that their data is removed from the backups.'), ->withMessage(
'body' => DI::l10n()->t('The user id is %d', local_user()), DI::l10n()->t('[Friendica System Notify]') . ' ' . DI::l10n()->t('User deleted their account'),
'to_email' => $admin['email'], DI::l10n()->t('On your Friendica node an user deleted their account. Please ensure that their data is removed from the backups.'),
'to_name' => $admin['username'], DI::l10n()->t('The user id is %d', local_user()))
'uid' => $admin['uid'], ->forUser($admin)
'language' => $admin['language'] ? $admin['language'] : 'en', ->withRecipient($admin['email'])
'show_in_notification_page' => false ->build();
]); DI::emailer()->send($email);
} }
if (User::getIdFromPasswordAuthentication($a->user, trim($_POST['qxz_password']))) { if (User::getIdFromPasswordAuthentication($a->user, trim($_POST['qxz_password']))) {

View file

@ -242,27 +242,6 @@ class App
$this->baseURL->get(); $this->baseURL->get();
} }
/**
* Generates the site's default sender email address
*
* @return string
* @throws HTTPException\InternalServerErrorException
*/
public function getSenderEmailAddress()
{
$sender_email = $this->config->get('config', 'sender_email');
if (empty($sender_email)) {
$hostname = $this->baseURL->getHostname();
if (strpos($hostname, ':')) {
$hostname = substr($hostname, 0, strpos($hostname, ':'));
}
$sender_email = 'noreply@' . $hostname;
}
return $sender_email;
}
/** /**
* Returns the current theme name. May be overriden by the mobile theme name. * Returns the current theme name. May be overriden by the mobile theme name.
* *

View file

@ -252,7 +252,7 @@ class Update
} }
$sent[] = $admin['email']; $sent[] = $admin['email'];
$lang = (($admin['language'])?$admin['language']:'en'); $lang = $admin['language'] ?? 'en';
$l10n = DI::l10n()->withLang($lang); $l10n = DI::l10n()->withLang($lang);
$preamble = Strings::deindent($l10n->t(" $preamble = Strings::deindent($l10n->t("
@ -263,15 +263,13 @@ class Update
$update_id)); $update_id));
$body = $l10n->t("The error message is\n[pre]%s[/pre]", $error_message); $body = $l10n->t("The error message is\n[pre]%s[/pre]", $error_message);
notification([ $email = DI::emailer()
'uid' => $admin['uid'], ->newSystemMail()
'type' => SYSTEM_EMAIL, ->withMessage($l10n->t('[Friendica Notify] Database update'), $preamble, $body)
'to_email' => $admin['email'], ->forUser($admin)
'subject' => $l10n->t('[Friendica Notify] Database update'), ->withRecipient($admin['email'])
'preamble' => $preamble, ->build();
'body' => $body, DI::emailer()->send($email);
'language' => $lang]
);
} }
//try the logger //try the logger
@ -301,15 +299,13 @@ class Update
The friendica database was successfully updated from %s to %s.", The friendica database was successfully updated from %s to %s.",
$from_build, $to_build)); $from_build, $to_build));
notification([ $email = DI::emailer()
'uid' => $admin['uid'], ->newSystemMail()
'type' => SYSTEM_EMAIL, ->withMessage($l10n->t('[Friendica Notify] Database update'), $preamble)
'to_email' => $admin['email'], ->forUser($admin)
'subject' => DI::l10n()->t('[Friendica Notify] Database update'), ->withRecipient($admin['email'])
'preamble' => $preamble, ->build();
'body' => $preamble, DI::emailer()->send($email);
'language' => $lang]
);
} }
} }

View file

@ -897,13 +897,13 @@ class User
$password $password
)); ));
return notification([ $email = DI::emailer()
'type' => SYSTEM_EMAIL, ->newSystemMail()
'uid' => $user['uid'], ->withMessage(DI::l10n()->t('Registration at %s', $sitename), $body)
'to_email' => $user['email'], ->forUser($user)
'subject' => DI::l10n()->t('Registration at %s', $sitename), ->withRecipient($user['email'])
'body' => $body ->build();
]); return DI::emailer()->send($email);
} }
/** /**
@ -965,15 +965,13 @@ class User
$password $password
)); ));
return notification([ $email = DI::emailer()
'uid' => $user['uid'], ->newSystemMail()
'language' => $user['language'], ->withMessage(DI::l10n()->t('Registration details for %s', $sitename), $preamble, $body)
'type' => SYSTEM_EMAIL, ->forUser($user)
'to_email' => $user['email'], ->withRecipient($user['email'])
'subject' => DI::l10n()->t('Registration details for %s', $sitename), ->build();
'preamble' => $preamble, return DI::emailer()->send($email);
'body' => $body
]);
} }
/** /**

View file

@ -13,6 +13,7 @@ use Friendica\Module\BaseAdmin;
use Friendica\Module\Register; use Friendica\Module\Register;
use Friendica\Protocol\PortableContact; use Friendica\Protocol\PortableContact;
use Friendica\Util\BasePath; use Friendica\Util\BasePath;
use Friendica\Util\EMailer\MailBuilder;
use Friendica\Util\Strings; use Friendica\Util\Strings;
use Friendica\Worker\Delivery; use Friendica\Worker\Delivery;
@ -110,6 +111,7 @@ class Site extends BaseAdmin
$sitename = (!empty($_POST['sitename']) ? Strings::escapeTags(trim($_POST['sitename'])) : ''); $sitename = (!empty($_POST['sitename']) ? Strings::escapeTags(trim($_POST['sitename'])) : '');
$sender_email = (!empty($_POST['sender_email']) ? Strings::escapeTags(trim($_POST['sender_email'])) : ''); $sender_email = (!empty($_POST['sender_email']) ? Strings::escapeTags(trim($_POST['sender_email'])) : '');
$banner = (!empty($_POST['banner']) ? trim($_POST['banner']) : false); $banner = (!empty($_POST['banner']) ? trim($_POST['banner']) : false);
$email_banner = (!empty($_POST['email_banner']) ? trim($_POST['email_banner']) : false);
$shortcut_icon = (!empty($_POST['shortcut_icon']) ? Strings::escapeTags(trim($_POST['shortcut_icon'])) : ''); $shortcut_icon = (!empty($_POST['shortcut_icon']) ? Strings::escapeTags(trim($_POST['shortcut_icon'])) : '');
$touch_icon = (!empty($_POST['touch_icon']) ? Strings::escapeTags(trim($_POST['touch_icon'])) : ''); $touch_icon = (!empty($_POST['touch_icon']) ? Strings::escapeTags(trim($_POST['touch_icon'])) : '');
$additional_info = (!empty($_POST['additional_info']) ? trim($_POST['additional_info']) : ''); $additional_info = (!empty($_POST['additional_info']) ? trim($_POST['additional_info']) : '');
@ -301,6 +303,12 @@ class Site extends BaseAdmin
DI::config()->set('system', 'banner', $banner); DI::config()->set('system', 'banner', $banner);
} }
if (empty($email_banner)) {
DI::config()->delete('system', 'email_banner');
} else {
DI::config()->set('system', 'email_banner', $email_banner);
}
if (empty($additional_info)) { if (empty($additional_info)) {
DI::config()->delete('config', 'info'); DI::config()->delete('config', 'info');
} else { } else {
@ -489,6 +497,12 @@ class Site extends BaseAdmin
$banner = '<a href="https://friendi.ca"><img id="logo-img" src="images/friendica-32.png" alt="logo" /></a><span id="logo-text"><a href="https://friendi.ca">Friendica</a></span>'; $banner = '<a href="https://friendi.ca"><img id="logo-img" src="images/friendica-32.png" alt="logo" /></a><span id="logo-text"><a href="https://friendi.ca">Friendica</a></span>';
} }
$email_banner = DI::config()->get('system', 'email_banner');
if ($email_banner == false) {
$email_banner = MailBuilder::DEFAULT_EMAIL_BANNER;
}
$additional_info = DI::config()->get('config', 'info'); $additional_info = DI::config()->get('config', 'info');
// Automatically create temporary paths // Automatically create temporary paths
@ -571,6 +585,7 @@ class Site extends BaseAdmin
'$sitename' => ['sitename', DI::l10n()->t('Site name'), DI::config()->get('config', 'sitename'), ''], '$sitename' => ['sitename', DI::l10n()->t('Site name'), DI::config()->get('config', 'sitename'), ''],
'$sender_email' => ['sender_email', DI::l10n()->t('Sender Email'), DI::config()->get('config', 'sender_email'), DI::l10n()->t('The email address your server shall use to send notification emails from.'), '', '', 'email'], '$sender_email' => ['sender_email', DI::l10n()->t('Sender Email'), DI::config()->get('config', 'sender_email'), DI::l10n()->t('The email address your server shall use to send notification emails from.'), '', '', 'email'],
'$banner' => ['banner', DI::l10n()->t('Banner/Logo'), $banner, ''], '$banner' => ['banner', DI::l10n()->t('Banner/Logo'), $banner, ''],
'$email_banner' => ['email_banner', DI::l10n()->t('Email Banner/Logo'), $email_banner, ''],
'$shortcut_icon' => ['shortcut_icon', DI::l10n()->t('Shortcut icon'), DI::config()->get('system', 'shortcut_icon'), DI::l10n()->t('Link to an icon that will be used for browsers.')], '$shortcut_icon' => ['shortcut_icon', DI::l10n()->t('Shortcut icon'), DI::config()->get('system', 'shortcut_icon'), DI::l10n()->t('Link to an icon that will be used for browsers.')],
'$touch_icon' => ['touch_icon', DI::l10n()->t('Touch icon'), DI::config()->get('system', 'touch_icon'), DI::l10n()->t('Link to an icon that will be used for tablets and mobiles.')], '$touch_icon' => ['touch_icon', DI::l10n()->t('Touch icon'), DI::config()->get('system', 'touch_icon'), DI::l10n()->t('Link to an icon that will be used for tablets and mobiles.')],
'$additional_info' => ['additional_info', DI::l10n()->t('Additional Info'), $additional_info, DI::l10n()->t('For public servers: you can add additional information here that will be listed at %s/servers.', Search::getGlobalDirectory())], '$additional_info' => ['additional_info', DI::l10n()->t('Additional Info'), $additional_info, DI::l10n()->t('For public servers: you can add additional information here that will be listed at %s/servers.', Search::getGlobalDirectory())],

View file

@ -76,15 +76,13 @@ class Users extends BaseAdmin
$preamble = sprintf($preamble, $user['username'], DI::config()->get('config', 'sitename')); $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')); $body = sprintf($body, DI::baseUrl()->get(), $user['nickname'], $result['password'], DI::config()->get('config', 'sitename'));
notification([ $email = DI::emailer()
'type' => SYSTEM_EMAIL, ->newSystemMail()
'language' => $user['language'], ->withMessage(DI::l10n()->t('Registration details for %s', DI::config()->get('config', 'sitename')), $preamble, $body)
'to_name' => $user['username'], ->forUser($user)
'to_email' => $user['email'], ->withRecipient($user['email'])
'uid' => $user['uid'], ->build();
'subject' => DI::l10n()->t('Registration details for %s', DI::config()->get('config', 'sitename')), return DI::emailer()->send($email);
'preamble' => $preamble,
'body' => $body]);
} }
if (!empty($_POST['page_users_block'])) { if (!empty($_POST['page_users_block'])) {

View file

@ -77,7 +77,7 @@ class Invite extends BaseModule
} }
$additional_headers = 'From: ' . $app->user['email'] . "\n" $additional_headers = 'From: ' . $app->user['email'] . "\n"
. 'Sender: ' . $app->getSenderEmailAddress() . "\n" . 'Sender: ' . DI::emailer()->getSiteEmailAddress() . "\n"
. 'Content-type: text/plain; charset=UTF-8' . "\n" . 'Content-type: text/plain; charset=UTF-8' . "\n"
. 'Content-transfer-encoding: 8bit'; . 'Content-transfer-encoding: 8bit';

View file

@ -0,0 +1,199 @@
<?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;
use Psr\Log\LoggerInterface;
/**
* A base class for building new emails
*/
abstract class MailBuilder
{
/** @var string The default email banner in case nothing else is defined */
const DEFAULT_EMAIL_BANNER = 'images/friendica-32.png';
/** @var L10n */
protected $l10n;
/** @var IConfig */
protected $config;
/** @var BaseURL */
protected $baseUrl;
/** @var LoggerInterface */
protected $logger;
/** @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, LoggerInterface $logger)
{
$this->l10n = $l10n;
$this->baseUrl = $baseUrl;
$this->config = $config;
$this->logger = $logger;
$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 array $user The user entity/array, for which the email should be sent
*
* @return static
* @todo Once the user array is replaced with a user entity, replace this array parameter as well
*/
public function forUser(array $user)
{
$this->recipientUid = $user['uid'] ?? 0;
try {
$this->l10n = $user['language'] ? $this->l10n->withLang($user['language']) : $this->l10n;
} catch (Exception $e) {
$this->logger->warning('cannot use language.', ['user' => $user, 'exception' => $e]);
}
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)) &&
!empty($this->recipientUid)) {
$user = User::getById($this->recipientUid, ['email']);
if (!empty($user['email'])) {
$this->recipientAddress = $user['email'];
}
}
if (empty($this->recipientAddress)) {
throw new InternalServerErrorException('Recipient address is missing.');
}
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/html.tpl');
$msgHtml = Renderer::replaceMacros($tpl, [
'$title' => $this->l10n->t('Friendica Notification'),
'$product' => FRIENDICA_PLATFORM,
'$htmlversion' => $msgHtml,
'$sitename' => $this->config->get('config', 'sitename'),
'$banner' => $this->config->get('system', 'email_banner',
$this->baseUrl->get(true) . DIRECTORY_SEPARATOR . self::DEFAULT_EMAIL_BANNER),
]);
}
return new Email(
$this->senderName,
$this->senderAddress,
$this->senderNoReply,
$this->recipientAddress,
$this->getSubject() ?? '',
$msgHtml,
$this->getPlaintextMessage() ?? '',
$this->headers,
$this->recipientUid ?? null);
}
}

View file

@ -0,0 +1,114 @@
<?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 Psr\Log\LoggerInterface;
/**
* 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, LoggerInterface $logger,
string $siteEmailAddress, string $siteName)
{
parent::__construct($l10n, $baseUrl, $config, $logger);
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);
}
// Set the system wide site address/name as sender (default for system mails)
$this->senderName = $siteName;
$this->senderAddress = $siteEmailAddress;
$this->senderNoReply = $siteEmailAddress;
}
/**
* 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,
]);
}
}

View file

@ -7,10 +7,12 @@ namespace Friendica\Util;
use Friendica\App; use Friendica\App;
use Friendica\Core\Config\IConfig; use Friendica\Core\Config\IConfig;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\Core\L10n;
use Friendica\Core\PConfig\IPConfig; use Friendica\Core\PConfig\IPConfig;
use Friendica\Network\HTTPException\InternalServerErrorException; use Friendica\Network\HTTPException\InternalServerErrorException;
use Friendica\Object\EMail\IEmail; use Friendica\Object\EMail\IEmail;
use Friendica\Protocol\Email; use Friendica\Protocol\Email;
use Friendica\Util\EMailer\SystemMailBuilder;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
/** /**
@ -26,13 +28,65 @@ class Emailer
private $logger; private $logger;
/** @var App\BaseURL */ /** @var App\BaseURL */
private $baseUrl; private $baseUrl;
/** @var L10n */
private $l10n;
public function __construct(IConfig $config, IPConfig $pConfig, App\BaseURL $baseURL, LoggerInterface $logger) /** @var string */
private $siteEmailAddress;
/** @var string */
private $siteEmailName;
public function __construct(IConfig $config, IPConfig $pConfig, App\BaseURL $baseURL, LoggerInterface $logger,
L10n $defaultLang)
{ {
$this->config = $config; $this->config = $config;
$this->pConfig = $pConfig; $this->pConfig = $pConfig;
$this->logger = $logger; $this->logger = $logger;
$this->baseUrl = $baseURL; $this->baseUrl = $baseURL;
$this->l10n = $defaultLang;
$this->siteEmailAddress = $this->config->get('config', 'sender_email');
if (empty($sysEmailAddress)) {
$hostname = $this->baseUrl->getHostname();
if (strpos($hostname, ':')) {
$hostname = substr($hostname, 0, strpos($hostname, ':'));
}
$this->siteEmailAddress = 'noreply@' . $hostname;
}
$this->siteEmailName = $this->config->get('config', 'sitename', 'Friendica Social Network');
}
/**
* Gets the site's default sender email address
*
* @return string
*/
public function getSiteEmailAddress()
{
return $this->siteEmailAddress;
}
/**
* Gets the site's default sender name
*
* @return string
*/
public function getSiteEmailName()
{
return $this->siteEmailName;
}
/**
* Creates a new system email
*
* @return SystemMailBuilder
*/
public function newSystemMail()
{
return new SystemMailBuilder($this->l10n, $this->baseUrl, $this->config, $this->logger,
$this->getSiteEmailAddress(), $this->getSiteEmailName());
} }
/** /**

View file

@ -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;
}
}

View file

@ -0,0 +1,177 @@
<?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;
use Psr\Log\NullLogger;
/**
* 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, new NullLogger());
$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, new NullLogger());
$testEmail = $builder
->withMessage('Subject', 'Html', 'text')
->withRecipient('recipient@friendica.local')
->withSender('Sender', 'sender@friendica.local', 'no-reply@friendica.local')
->forUser(['uid' => 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, new NullLogger());
$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, new NullLogger());
$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, new NullLogger());
$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, new NullLogger());
$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,
]);
}
}

View file

@ -0,0 +1,57 @@
<?php
namespace Friendica\Test\src\Util\Emailer;
use Friendica\App\BaseURL;
use Friendica\Core\Config\IConfig;
use Friendica\Core\L10n;
use Friendica\Test\MockedTest;
use Friendica\Test\Util\VFSTrait;
use Friendica\Util\EMailer\MailBuilder;
use Friendica\Util\EMailer\SystemMailBuilder;
use Psr\Log\NullLogger;
class SystemMailBuilderTest 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->config->shouldReceive('get')->with('config', 'admin_name')->andReturn('Admin');
$this->l10n = \Mockery::mock(L10n::class);
$this->l10n->shouldReceive('t')->andReturnUsing(function ($msg) {
return $msg;
});
$this->baseUrl = \Mockery::mock(BaseURL::class);
$this->baseUrl->shouldReceive('getHostname')->andReturn('friendica.local');
$this->baseUrl->shouldReceive('get')->andReturn('http://friendica.local');
$this->defaultHeaders = "";
}
/**
* Test if the builder instance can get created
*/
public function testBuilderInstance()
{
$builder = new SystemMailBuilder($this->l10n, $this->baseUrl, $this->config, new NullLogger(), 'moreply@friendica.local', 'FriendicaSite');
$this->assertInstanceOf(MailBuilder::class, $builder);
$this->assertInstanceOf(SystemMailBuilder::class, $builder);
}
}

View file

@ -15,6 +15,7 @@
{{include file="field_input.tpl" field=$sitename}} {{include file="field_input.tpl" field=$sitename}}
{{include file="field_input.tpl" field=$sender_email}} {{include file="field_input.tpl" field=$sender_email}}
{{include file="field_textarea.tpl" field=$banner}} {{include file="field_textarea.tpl" field=$banner}}
{{include file="field_input.tpl" field=$email_banner}}
{{include file="field_input.tpl" field=$shortcut_icon}} {{include file="field_input.tpl" field=$shortcut_icon}}
{{include file="field_input.tpl" field=$touch_icon}} {{include file="field_input.tpl" field=$touch_icon}}
{{include file="field_textarea.tpl" field=$additional_info}} {{include file="field_textarea.tpl" field=$additional_info}}

View file

@ -0,0 +1,23 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional //EN">
<html>
<head>
<title>{{$title}}</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
</head>
<body>
<table style="border:1px solid #ccc">
<tbody>
<tr>
<td 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="{{$banner}}" alt="Friendica Banner">
<div style="padding:7px; margin-left: 5px; float:left; font-size:18px;letter-spacing:1px;">{{$product}}</div>
<div style="clear: both;"></div>
</td>
</tr>
</tbody>
</table>
<p>
{{$htmlversion nofilter}}
</p>
</body>
</html>

View file

@ -0,0 +1,5 @@
<table>
<tr><td style="padding-right:22px;">{{$htmlversion nofilter}}</td></tr>
<tr><td>{{$thanks}}</td></tr>
<tr><td>{{$site_admin}}</td></tr>
</table>

View file

@ -0,0 +1,7 @@
{{$preamble nofilter}}
{{$textversion nofilter}}
{{$thanks nofilter}}
{{$site_admin nofilter}}