Files
conservatorio-tomadini/plugins/system/nrframework/NRFramework/Email.php
2024-12-31 11:07:09 +01:00

304 lines
8.0 KiB
PHP

<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework;
defined('_JEXEC') or die('Restricted access');
use NRFramework\Functions;
use NRFramework\URLHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Uri\Uri;
use Joomla\Filesystem\Path;
use Joomla\CMS\Language\Text;
/**
* Framework Emailer
*/
class Email
{
/**
* Indicates the last error
*
* @var string
*/
public $error;
/**
* Email Object
*
* @var email data to be sent
*/
private $email;
/**
* Required elements for a valid email object
*
* @var array
*/
private $requiredKeys = [
'from_email',
'from_name',
'recipient',
'subject',
'body'
];
/**
* Class constructor
*/
public function __construct($email)
{
$this->email = $email;
}
/**
* Validates Email Object
*
* @param array $email The email object
*
* @return boolean Returns true if the email object is valid
*/
public function validate()
{
// Validate email object
if (!$this->email || !is_array($this->email) || !count($this->email))
{
$this->setError('Invalid email object.');
return;
}
// Check for missing properties
foreach ($this->requiredKeys as $key)
{
if (!isset($this->email[$key]) || empty($this->email[$key]))
{
$this->setError("The $key field is either missing or invalid.");
return;
}
}
// Validate recipient email addresses.
$this->email['recipient'] = Functions::makeArray($this->email['recipient']);
foreach ($this->email['recipient'] as $recipient)
{
if (!$this->validateEmailAddress($recipient))
{
$this->setError("Invalid recipient email address: $recipient");
return;
}
}
// Validate sender email address
if (!$this->validateEmailAddress($this->email['from_email']))
{
$this->setError('Invalid sender email address: ' . $this->email['from_email']);
return;
}
$this->email['bcc'] = isset($this->email['bcc']) ? Functions::makeArray($this->email['bcc']) : [];
$this->email['cc'] = isset($this->email['cc']) ? Functions::makeArray($this->email['cc']) : [];
// Convert special HTML entities back to characters on non text-only properties.
// For instance, the subject line of an email is not parsed as HTML, it's just pure text.
// Because of this an HTML entity like &amp; it will be displayed as encoded.
// To prevent this from happening we need decode the values.
$this->email['subject'] = htmlspecialchars_decode($this->email['subject']);
$this->email['from_name'] = htmlspecialchars_decode($this->email['from_name']);
$this->email['reply_to_name'] = htmlspecialchars_decode($this->email['reply_to_name']);
return true;
}
/**
* Sending emails
*
* @param array $email The mail objecta
*
* @return mixed Returns true on success. Throws exeption on fail.
*/
public function send()
{
// Proceed only if Mail Sending is enabled.
if (!Factory::getConfig()->get('mailonline'))
{
$this->error = Text::_('NR_ERROR_EMAIL_IS_DISABLED');
return;
}
// Validate first the email object
if (!$this->validate($this->email))
{
return;
}
$email = $this->email;
$mailer = Factory::getMailer();
$mailer->CharSet = 'UTF-8';
$mailer->Encoding = 'quoted-printable';
// Email Sender
$mailer->setSender([
$email['from_email'],
$email['from_name']
]);
// Reply-to
if (isset($email['reply_to']) && !empty($email['reply_to']))
{
$name = (isset($email['reply_to_name']) && !empty($email['reply_to_name'])) ? $email['reply_to_name'] : '';
$reply_to_addresses = array_filter(array_map('trim', explode(',', $email['reply_to'])));
foreach($reply_to_addresses as $reply_to_address)
{
$mailer->addReplyTo($reply_to_address, $name);
}
}
// Convert all relative paths found in <a> and <img> elements to absolute URLs
$email['body'] = URLHelper::relativePathsToAbsoluteURLs($email['body']);
// Fix space characters displayed as ???? in old email clients like SquirrelMail.
// Ticket reference: https://smilemotive.teamwork.com/desk/tickets/96313487/messages
$specialSpace = [
"\xC2\xA0",
"\xE1\xA0\x8E",
"\xE2\x80\x80",
"\xE2\x80\x81",
"\xE2\x80\x82",
"\xE2\x80\x83",
"\xE2\x80\x84",
"\xE2\x80\x85",
"\xE2\x80\x86",
"\xE2\x80\x87",
"\xE2\x80\x88",
"\xE2\x80\x89",
"\xE2\x80\x8A",
"\xE2\x80\x8B",
"\xE2\x80\xAF",
"\xE2\x81\x9F",
"\xEF\xBB\xBF",
];
$email['body'] = str_replace($specialSpace, " ", $email['body']);
$mailer
->addRecipient($email['recipient'])
->isHTML(true)
->setSubject($email['subject'])
->setBody($email['body']);
$mailer->AltBody = strip_tags(str_ireplace(['<br />', '<br>', '<br/>'], "\r\n", $email['body']));
// Add CC
if (!empty($email['cc']))
{
$mailer->addCc($email['cc']);
}
// Add BCC
if (!empty($email['bcc']))
{
$mailer->addBcc($email['bcc']);
}
// Attachments
$attachments = $email['attachments'];
if (!empty($attachments))
{
if (!is_array($attachments))
{
$attachments = explode(',', $attachments);
}
// Validate Attachments
$attachments = array_filter(array_map('trim', $attachments));
foreach ($attachments as $attachment)
{
$file_path = $this->toRelativePath($attachment);
if (!is_file($file_path))
{
continue;
}
$mailer->addAttachment($file_path);
}
}
// Send mail
$send = $mailer->Send();
if ($send !== true)
{
$this->setError($send->__toString());
return;
}
return true;
}
/**
* Set Class Error
*
* @param string $error The error message
*/
private function setError($error)
{
$this->error = 'Error sending email: ' . $error;
}
/**
* Removes all illegal characters and validates an email address
*
* @param string $email Email address string
*
* @return bool
*/
private function validateEmailAddress($email)
{
// If the email address contains an ampersand, throw an error
if (strpos($email, '&') !== false)
{
return false;
}
$email = filter_var($email, FILTER_SANITIZE_EMAIL);
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
/**
* Attempts to transform an absolute URL to path relative to the site's root.
*
* @param string $url
*
* @return string
*/
private function toRelativePath($url)
{
$needles = [
Uri::root(),
JPATH_SITE,
JPATH_ROOT
];
$path = str_replace($needles, '', $url);
$path = Path::clean($path);
// Relative paths should not start with a slash.
$path = ltrim($path, '/');
return $path;
}
}