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"];
} else {
$nickname = '';
} }
$nickname = $user["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("
@ -261,17 +261,15 @@ class Update
This needs to be fixed soon and I can't do it alone. Please contact a 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.", friendica developer if you can not help me on your own. My database might be invalid.",
$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}}