Your IP : 216.73.217.142


Current Path : /var/www/consult-e-syn/public_html/plugins/ats/postemail/
Upload File :
Current File : /var/www/consult-e-syn/public_html/plugins/ats/postemail/postemail.php

<?php
/**
 * @package   ats
 * @copyright Copyright (c)2011-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

defined('_JEXEC') or die;

use Akeeba\TicketSystem\Admin\Helper\AddonEmails;
use Akeeba\TicketSystem\Admin\Helper\Email;
use Akeeba\TicketSystem\Admin\Helper\Html;
use Akeeba\TicketSystem\Admin\Helper\Permissions;
use Akeeba\TicketSystem\Admin\Helper\Subscriptions;
use Akeeba\TicketSystem\Admin\Model\Attachments;
use Akeeba\TicketSystem\Admin\Model\Posts;
use Akeeba\TicketSystem\Admin\Model\Tickets;
use Akeeba\TicketSystem\Site\Helper\Html2Text;
use FOF40\Model\DataModel\Collection as DataCollection;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Mail\MailHelper as JMailHelper;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Router\Route;
use Joomla\CMS\User\User as JUser;
use Joomla\Registry\Registry as JRegistry;

class plgAtsPostemail extends CMSPlugin
{
	/** @var \FOF40\Container\Container FOF Container */
	private $container;

	/**
	 * Public constructor
	 *
	 * @param   object  $subject  The object to observe
	 * @param   array   $config   Configuration parameters to the plugin
	 */
	public function __construct(&$subject, $config = [])
	{
		parent::__construct($subject, $config);

		if (!defined('FOF40_INCLUDED') && !@include_once(JPATH_LIBRARIES . '/fof40/include.php'))
		{
			throw new RuntimeException('This extension requires FOF 4.', 500);
		}

		$version_php = JPATH_ADMINISTRATOR . '/components/com_ats/version.php';

		if (!defined('ATS_VERSION') && @is_file($version_php))
		{
			require_once $version_php;
		}

		$this->loadLanguage();
		$this->container = FOF40\Container\Container::getInstance('com_ats');
	}

	/**
	 * This handles the onATSPost event which is triggered every time a post is
	 * created or edited.
	 *
	 * @param   array  $info  An indexed array with post info. The post key contains an AtsTablePost object.
	 *
	 * @return  void
	 */
	public function onATSPost($info)
	{
		// Only send notifications for NEW posts, or posts edited by a non-administrator
		$sendEmail = false;

		if ($info['new'])
		{
			// New post; send email
			$sendEmail = true;
		}
		else
		{
			// Modified post. Let's think...
			$modified_by = $info['post']->modified_by;

			if ($modified_by <= 0)
			{
				// Not modified / modified by invalid user - no email
				$sendEmail = false;
			}
			else
			{
				$isAdmin = Permissions::isManager($info['ticket']->catid, $modified_by);

				if (!$isAdmin)
				{
					$sendEmail = true;
				}
			}
		}

		if (!$sendEmail)
		{
			return;
		}

		// Extract the post and ticket objects
		/** @var Posts $post */
		$post = $info['post'];

		/** @var Tickets $ticket */
		$ticket = $info['ticket'];

		// Is this a new ticket?
		/** @var Posts $firstPost */
		$firstPost   = $this->getFirstPost($ticket);
		$isNewTicket = $firstPost->getId() == $post->getId();

		// Let's get the user ID's of all users who are managers of ATS or the specific category
		$managers        = $this->_getManagersForATSCategory($ticket->catid);
		$skipNotAssigned = $this->container->params->get('assigned_noemail', 0);

		if (!empty($managers))
		{
			foreach ($managers as $manager)
			{
				// Make sure we are not sending an email to the user who posted
				if ($manager == $post->created_by)
				{
					continue;
				}

				// Should I notify only the assigned manager?
				if ($skipNotAssigned)
				{
					// Ticket assigned, let's skip everyone else!
					if ($info['ticket']->assigned_to && $info['ticket']->assigned_to != $manager)
					{
						continue;
					}
				}

				// Send email to the manager
				$this->_sendEmailToUser($manager, $post, $ticket, 'manager', $isNewTicket);
			}
		}

		// Send email to the ticket owner, unless it's a reply sent by himself.
		// New ticket notifications will always be sent.
		if (($ticket->created_by != $post->created_by) || $isNewTicket)
		{
			$this->_sendEmailToUser($ticket->created_by, $post, $ticket, 'owner', $isNewTicket);
		}
	}

	/**
	 * Event fired when a ticket is assigned to someone. ATS will notify assigned user (unless they assigned
	 * a ticket to themselves
	 *
	 * @param   Tickets  $ticket  Assigned ticket
	 *
	 * @return  bool  Is everything alright?
	 */
	public function onATSassign($ticket)
	{
		// Current user is the one assigned to, it's useless to notify myself
		if ($ticket->assigned_to == Permissions::getUser()->id)
		{
			return true;
		}

		try
		{
			$mailer = Email::getMailer();
		}
		catch (Exception $e)
		{
			// JMail died on us
			return true;
		}

		[$subject, $body,] = Email::loadEmailTemplateFromDB('manager-assignedticket');

		$sitename = $this->container->platform->getConfig()->get('sitename');
		$user     = Permissions::getUser($ticket->assigned_to);

		try
		{
			$mailer->addRecipient($user->email, $user->name);
		}
		catch (Exception $e)
		{
			// JMail died on us
			return true;
		}

		// Add any add-on emails
		$addOnEmails = AddonEmails::getAddonEmails($user->id);
		$addOnEmails = AddonEmails::filterAddonEmails($addOnEmails, $user->id);

		if (is_array($addOnEmails) && !empty($addOnEmails))
		{
			foreach ($addOnEmails as $email => $name)
			{
				try
				{
					$mailer->addCc($email, $name);
				}
				catch (Exception $e)
				{
				}
			}
		}

		$category  = $ticket->joomla_category;
		$ticketURL = Route::link('site', 'index.php?option=com_ats&view=ticket&id=' . $ticket->ats_ticket_id, false, Route::TLS_IGNORE, true);

		//Grab first post, so we can use its text inside the email
		/** @var Posts $post */
		$post = $ticket->posts->first();

		$attachmentURL = '';

		if ($post->ats_attachment_id)
		{
			/** @var \Akeeba\TicketSystem\Site\Model\Attachments $attachment */
			$attachment = $this->container->factory->model('Attachments')->tmpInstance();
			$attachment->find($post->ats_attachment_id[0]);

			$attFilename = Attachments::getAttachmentsDirectory() . '/' . $attachment->mangled_filename;
			$realName    = $attachment->original_filename;

			if (@file_exists($attFilename))
			{
				$attSize = @filesize($attFilename);

				if ($attSize < 2097152)
				{
					// Attach the file
					try
					{
						$mailer->addAttachment($attFilename, $realName, 'base64', $attachment->mime_type);
					}
					catch (Exception $e)
					{
						// JMail died on us
					}
				}
				else
				{
					// Create a link to the attachment, for attachments over 2Mb
					$attachmentURL = Route::link('site', 'index.php?option=com_ats&view=attachment&task=read&format=raw&id=' . $attachment->ats_attachment_id, true, Route::TLS_IGNORE, true);
				}
			}
		}

		$mailInfo = [
			'id'         => $ticket->ats_ticket_id,
			'title'      => $ticket->title,
			'attachment' => empty($attachmentURL) ? '' : Text::sprintf('PLG_ATS_POSTEMAIL_ATTACHMENT_LINK', $attachmentURL, $realName),
			'url'        => $ticketURL,
			'catname'    => $category->title,
			'text'       => $post->content_html,
			'sitename'   => $sitename,
			'user_name'  => $user->name,
		];

		$makePlaintext = $this->params->get('makePlaintext', 1);

		try
		{
			Email::parseTemplate($body, $subject, $mailInfo, $mailer);

			// Create a plain text representation of the email body if we are asked to
			if ($makePlaintext)
			{
				$html2Text       = new Html2Text($mailer->Body);
				$mailer->AltBody = $html2Text->getText();
			}

			// Send the email
			$mailer->Send();
			unset($mailer);
		}
		catch (Exception $e)
		{
			// JMail died on us
			return true;
		}

		return true;
	}

	/**
	 * Gets all the user IDs registered as managers for a specific ATS category
	 *
	 * @param   int  $catid
	 *
	 * @return   array
	 */
	private function _getManagersForATSCategory($catid)
	{
		$managers = Permissions::getManagers($catid);

		/** @var \Akeeba\TicketSystem\Site\Model\Categories $category */
		$category = $this->container->factory->model('Categories')->tmpInstance();
		$category->load($catid);

		$params = new JRegistry($category->params);

		if (!is_object($params))
		{
			$notify  = ['all'];
			$exclude = [];
		}
		else
		{
			$notify  = $params->get('notify_managers', ['all']);
			$exclude = $params->get('exclude_managers', []);
		}

		$whitelist = [];
		$blacklist = [];

		// Only notify the managers selected by the site administrators if they are, indeed, managers of this category.
		foreach ($managers as $manager)
		{
			if (in_array('all', $notify) || in_array($manager->id, $notify))
			{
				$whitelist[] = $manager->id;
			}
		}

		// Remove managers from the whitelist array
		foreach ($managers as $manager)
		{
			if (in_array('all', $exclude) || in_array($manager->id, $exclude))
			{
				$blacklist[] = $manager->id;
			}
		}

		$ret = array_diff($whitelist, $blacklist);

		return $ret;
	}

	/**
	 * Sends a new post email notification
	 *
	 * @param   int        $user_id                                 Recipient's user ID
	 * @param   Posts      $post
	 * @param   Tickets    $ticket
	 * @param   string     $toWhom                                  One of 'manager', 'owner' or 'subscriber'.
	 *                                                              Determines the subject and post body template.
	 * @param   null|bool  $isNewTicket                             Is this a new ticket? NULL to auto-determine.
	 *
	 * @return bool  Is the email correctly sent?
	 */
	private function _sendEmailToUser($user_id, $post, $ticket, $toWhom = 'manager', $isNewTicket = null)
	{
		$isCLI = $this->container->platform->isCli();

		if ($isCLI)
		{
			$siteURL = $this->container->params->get('siteurl', 'http://www.example.com');
		}

		// Extract the useful information
		$userReceipient = Permissions::getUser($user_id);

		if ($post->created_by == -1)
		{
			$userPoster           = clone $this->container->platform->getUser();
			$userPoster->username = 'system';
			$userPoster->name     = Text::_('COM_ATS_CLI_SYSTEMUSERLABEL');
		}
		else
		{
			$userPoster = Permissions::getUser($post->created_by);
		}

		try
		{
			$mailer = Email::getMailer();
		}
		catch (Exception $e)
		{
			// JMail died on us
			return true;
		}

		// If the category specifies a category_email parameter,
		// set the Reply-To address to it.
		/** @var \Akeeba\TicketSystem\Site\Model\Categories $category */
		$category = $this->container->factory->model('Categories')->tmpInstance();
		$category->find($ticket->catid);

		$params = new JRegistry();
		$params->loadString($category->params, 'JSON');

		$category_email = $params->get('category_email', '');
		$category_email = trim($category_email);

		if (!empty($category_email))
		{
			$mailfrom = $category_email;
			$conf     = $this->container->platform->getConfig();
			$fromname = $conf->get('fromname');

			$didWeAddAReplyToAddress = false;

			try
			{
				$mailer->addReplyTo([JMailHelper::cleanLine($mailfrom), JMailHelper::cleanLine($fromname)]);
				$didWeAddAReplyToAddress = true;
			}
			catch (Exception $e)
			{
				/**
				 * It is very possible that we have Joomla! 3.5 which doesn't let us pass an array, unlike Joomla! 3.4
				 * or earlier. We have to assume that a failure here means we should retry without using an array.
				 */
			}

			if (!$didWeAddAReplyToAddress)
			{
				try
				{
					$mailer->addReplyTo(JMailHelper::cleanLine($mailfrom), JMailHelper::cleanLine($fromname));
				}
				catch (Exception $e)
				{
					// JMail died on us
					return true;
				}
			}
		}

		try
		{
			$mailer->addRecipient($userReceipient->email, $userReceipient->name);
		}
		catch (Exception $e)
		{
			// JMail died on us
			return true;
		}

		// Add any add-on emails
		$addOnEmails = AddonEmails::getAddonEmails($userReceipient->id);
		$addOnEmails = AddonEmails::filterAddonEmails($addOnEmails, $userReceipient->id);

		if (is_array($addOnEmails) && !empty($addOnEmails))
		{
			foreach ($addOnEmails as $email => $name)
			{
				try
				{
					$mailer->addCC($email, $name);
				}
				catch (Exception $e)
				{
				}
			}
		}

		if (is_null($isNewTicket))
		{
			/** @var Posts $firstPost */
			$firstPost   = $this->getFirstPost($ticket);
			$isNewTicket = $firstPost->getId() == $post->getId();
		}

		// Get default body and subject
		[$template, $subject] = $this->_getEmailTemplate($toWhom, $isNewTicket, $ticket->public);

		// I have no body and subject, this means that the webmaster doesn't want to send any emails,
		// so let's stop here
		if (!$template && !$subject)
		{
			return true;
		}

		// Set mail priority using phpmailer Priority variable and setting custom headers.
		// X-Priority header is already set by phpmailer
		// http://www.php.net/manual/en/function.mail.php#91058
		try
		{
			if ($ticket->priority == 1)
			{
				$mailer->Priority = 2;
				$head_priority    = 'High';
			}
			elseif ($ticket->priority == 5)
			{
				$mailer->Priority = 3;
				$head_priority    = 'Normal';
			}
			else
			{
				$mailer->Priority = 5;
				$head_priority    = 'Low';
			}
		}
		catch (Exception $e)
		{
			// JMail died on us
			return true;
		}

		try
		{
			/**
			 * So, the X-MSMail-Priority is required by Microsoft Outlook since it seems oblivious to the standard
			 * Importance header. However, SpamAssassin thinks that the presence of this header without X-MimeOLE means
			 * that your mail is spam. As a result we have to ignore users of Microsoft Outlook to make SpamAssassin not
			 * treat our legit mail as spam. If we don't, eventually all of the mails sent from the domain are marked as
			 * spam.
			 *
			 * As a small consolation prize, newer versions of Microsoft Outlook seem to have figured our how to honor
			 * RFC4021 in that respect. So even though we ignore users of legacy versions of Microsoft Outlook (which
			 * is broken) to please SpamAssassin (whihc is also broken) we can at least rest assured that users of a
			 * modern version of Microsoft Outlook will see the correct priority. Oh, well...
			 */
			// $mailer->addCustomHeader('X-MSMail-Priority: ' . $head_priority);
			$mailer->addCustomHeader('Importance: ' . $head_priority);
		}
		catch (Exception $e)
		{
			// JMail died on us
			return true;
		}

		// Attach files if less than 2MB (with addAttachment($attachFile)), or set up a link to them
		$attachmentURLs = [];

		if ($post->ats_attachment_id)
		{
			/** @var \Akeeba\TicketSystem\Site\Model\Attachments $attachment */
			$attachment = $this->container->factory->model('Attachments')->tmpInstance();

			foreach ($post->ats_attachment_id as $id_attachment)
			{
				// Post with no attachment (id_attachment == 0)
				if (!$id_attachment)
				{
					continue;
				}

				$attachment->find($id_attachment);

				$attFilename = Attachments::getAttachmentsDirectory() . '/' . $attachment->mangled_filename;
				$realName    = $attachment->original_filename;

				if (!@file_exists($attFilename))
				{
					continue;
				}

				$attSize = @filesize($attFilename);

				if ($attSize < 2097152 && count($post->ats_attachment_id) == 1)
				{
					// Attach the file
					try
					{
						$mailer->addAttachment($attFilename, $realName, 'base64', $attachment->mime_type);
					}
					catch (Exception $e)
					{
						// JMail died on us
					}
				}
				else
				{
					// Create a link to the attachment, for attachments over 2Mb or for multiple attachments
					$attachmentURLs[$realName] = Route::link('site', 'index.php?option=com_ats&view=attachment&task=read&format=raw&id=' . $attachment->ats_attachment_id, false, Route::TLS_IGNORE, true);
				}
			}
		}

		// Get a link to the new post
		$postURL = Route::link('site', 'index.php?option=com_ats&view=ticket&id=' . $ticket->ats_ticket_id, false, Route::TLS_IGNORE, true);
		$postURL .= '#p' . $post->ats_post_id;

		// Get the body of the message, based on overridable template files
		$sitename = $this->container->platform->getConfig()->get('sitename');

		$attachmentToken = [];

		if ($attachmentURLs)
		{
			foreach ($attachmentURLs as $realName => $attachmentURL)
			{
				$attachmentToken[] = Text::sprintf('PLG_ATS_POSTEMAIL_ATTACHMENT_LINK', $attachmentURL, $realName);
			}
		}

		$attachmentToken = implode('<br/>', $attachmentToken);

		$mailInfo = [
			'id'              => $ticket->ats_ticket_id,
			'title'           => $ticket->title,
			'url'             => $postURL,
			'attachment'      => $attachmentToken,
			'poster_username' => $userPoster->username,
			'poster_name'     => $userPoster->name,
			'user_username'   => $userReceipient->username,
			'user_name'       => $userReceipient->name,
			'text'            => $post->content_html,
			'catname'         => $category->title,
			'sitename'        => $sitename,
			'origin'          => $post->origin,
			'avatar'          => Html::getAvatarURL($userPoster, 64),
			'assigned_to'     => $ticket->assigned_to ? Permissions::getUser($ticket->assigned_to)->name : Text::_('COM_ATS_TICKETS_UNASSIGNED'),
			'modified_by'     => $ticket->modified_by ? Permissions::getUser($ticket->modified_by)->name : '',
		];

		$mailInfo['subscriptions'] = '';

		// Import the ATS plugins. Used everywhere below, basically.
		if ($this->container->platform->isCli())
		{
			$allowedPluginsInCli = $this->container->platform->isAllowPluginsInCli();
			$this->container->platform->setAllowPluginsInCli(true);
		}

		$this->container->platform->importPlugin('ats');

		if ($this->container->platform->isCli())
		{
			$this->container->platform->setAllowPluginsInCli($allowedPluginsInCli);
		}

		// Add the user's subscriptions if there is a subscriptions component integration plugin
		if (Subscriptions::hasSubscriptionsComponent())
		{
			$subs                      = Subscriptions::getSubscriptionsList($userPoster);
			$mailInfo['subscriptions'] = implode(', ', $subs->active);
		}

		// Add GMail Go-To View Action markup, see https://developers.google.com/gmail/markup/reference/go-to-action#view_action
		$gmailActions = <<< HTML
<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "EmailMessage",
  "potentialAction": {
    "@type": "ViewAction",
    "url": "$postURL",
    "name": "View ticket"
  },
  "description": "Open ticket #{$ticket->ats_ticket_id} in your browser"
}
</script>
HTML;
		/**
		 * Inject the GMail action string WITHIN the <body> tag, otherwise some email clients will complain about
		 * broken HTML in the message.
		 *
		 * If we can't find the <body> tag let's put it on top and hope for the best
		 */

		$template = preg_replace('#<body[^>]*>#i', '$0' . $gmailActions, $template, -1, $count);

		if ($count < 1)
		{
			$template = $gmailActions . $template;
		}

		/**
		 * Call other ATS plugins to modify the email.
		 *
		 * This is used, for example, to inject the reply line when the Reply by Email feature is enabled.
		 */
		$this->container->platform->runPlugins('onATSSendEmail', [
			&$subject, &$template, $mailer, &$mailInfo, $toWhom,
		]);

		try
		{
			Email::parseTemplate($template, $subject, $mailInfo, $mailer);

			// Create a plain text representation of the email body if we are asked to
			if ($this->params->get('makePlaintext', 1) == 1)
			{
				$html2Text       = new Html2Text($mailer->Body);
				$mailer->AltBody = $html2Text->getText();
			}

			// Send the email
			$ret = $mailer->Send();

			unset($mailer);

			return $ret;
		}
		catch (Exception $e)
		{
			// JMail died on us
			return true;
		}
	}

	/**
	 * Gets an email template from the database (preferred), from the overrides
	 * directory (deprecated) or from the translation keys (not encouraged).
	 *
	 * @param   string  $tpl          The template type to fetch, e.g manager or owner
	 * @param   bool    $isNewTicket  Is this a new ticket (true) or a reply (false)?
	 * @param   bool    $isPublic     Is this a public (true) or a private (false) ticket?
	 *
	 * @return array An array containing the subject and template text
	 */
	private function _getEmailTemplate($tpl = 'manager', $isNewTicket = true, $isPublic = true)
	{
		// Get status variables
		$tmplFile     = '';
		$templateText = '';
		$subject      = '';
		$loadLanguage = null;

		// Get status variables
		$newold = $isNewTicket ? 'NEW' : 'OLD';
		$status = $isPublic ? 'PUBLIC' : 'PRIVATE';
		$key    = strtolower("$tpl-$status-$newold");

		// If I'm under ATS PRO, I will only load the template from the DB
		if (ATS_PRO)
		{
			[$templateText, $subject, $loadLanguage] = Email::loadEmailTemplateFromDB($key);
		}
		// Otherwise I'll load it from the filesystem
		else
		{
			// Get default body
			$loadLanguage = null;
			$basePath     = dirname(__FILE__) . '/templates/';
			$jLang        = \Joomla\CMS\Factory::getLanguage();
			$userLang     = Permissions::getUser()->getParam('language', '');
			$languages    = [
				$userLang,
				$jLang->getTag(),
				$jLang->getDefault(),
				'en-GB',
				'*',
			];

			foreach ($languages as $lang)
			{
				if (empty($lang))
				{
					continue;
				}

				if (!empty($loadLanguage))
				{
					continue;
				}

				$filename = "$lang/$tpl.tpl";

				if (file_exists($basePath . 'overrides/' . $filename))
				{
					$loadLanguage = $lang;
					$tmplFile     = $basePath . 'overrides/' . $filename;
					break;
				}
				elseif (file_exists($basePath . 'default/' . $filename))
				{
					$loadLanguage = $lang;
					$tmplFile     = $basePath . 'default/' . $filename;
					break;
				}
			}

			// Load the template text
			if ($tmplFile && JFile::exists($tmplFile))
			{
				$templateText = @file_get_contents($tmplFile);

				if (empty($templateText))
				{
					$templateText = @file_get_contents($tmplFile);
				}
			}
		}

		return [$subject, $templateText];
	}

	private function getFirstPost(Tickets $ticket)
	{
		/** @var DataCollection|null $posts */
		$posts = $ticket->posts;

		if (!is_object($posts) || !($posts instanceof DataCollection))
		{
			/** @var Posts $postModel */
			$postModel = $this->container->factory->model('Posts')->tmpInstance();
			$postModel->ats_ticket_id($ticket->getId());
			$postModel->orderBy('ats_ticket_id', 'ASC');
			$postModel->with([]);
			$posts = $postModel->get(true);
		}

		return $posts->first();
	}
}