<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_users
 *
 * @copyright   (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

namespace Joomla\Component\Users\Administrator\Model;

\defined('_JEXEC') or die;

use Joomla\CMS\Access\Access;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Filter\InputFilter;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Log\Log;
use Joomla\CMS\Mail\Exception\MailDisabledException;
use Joomla\CMS\Mail\MailTemplate;
use Joomla\CMS\MVC\Model\AdminModel;
use Joomla\Database\ParameterType;
use PHPMailer\PHPMailer\Exception as phpMailerException;

/**
 * Users mail model.
 *
 * @since  1.6
 */
class MailModel extends AdminModel
{
	/**
	 * Method to get the row form.
	 *
	 * @param   array    $data      An optional array of data for the form to interrogate.
	 * @param   boolean  $loadData  True if the form is to load its own data (default case), false if not.
	 *
	 * @return  Form	A Form object on success, false on failure
	 *
	 * @since   1.6
	 */
	public function getForm($data = array(), $loadData = true)
	{
		// Get the form.
		$form = $this->loadForm('com_users.mail', 'mail', array('control' => 'jform', 'load_data' => $loadData));

		if (empty($form))
		{
			return false;
		}

		return $form;
	}

	/**
	 * Method to get the data that should be injected in the form.
	 *
	 * @return  mixed  The data for the form.
	 *
	 * @since   1.6
	 * @throws  \Exception
	 */
	protected function loadFormData()
	{
		// Check the session for previously entered form data.
		$data = Factory::getApplication()->getUserState('com_users.display.mail.data', array());

		$this->preprocessData('com_users.mail', $data);

		return $data;
	}

	/**
	 * Method to preprocess the form
	 *
	 * @param   Form    $form   A form object.
	 * @param   mixed   $data   The data expected for the form.
	 * @param   string  $group  The name of the plugin group to import (defaults to "content").
	 *
	 * @return  void
	 *
	 * @since   1.6
	 * @throws  \Exception if there is an error loading the form.
	 */
	protected function preprocessForm(Form $form, $data, $group = 'user')
	{
		parent::preprocessForm($form, $data, $group);
	}

	/**
	 * Send the email
	 *
	 * @return  boolean
	 *
	 * @throws  \Exception
	 */
	public function send()
	{
		$app      = Factory::getApplication();
		$data     = $app->input->post->get('jform', array(), 'array');
		$user     = Factory::getUser();
		$access   = new Access;
		$db       = $this->getDbo();
		$language = Factory::getLanguage();

		$mode         = array_key_exists('mode', $data) ? (int) $data['mode'] : 0;
		$subject      = array_key_exists('subject', $data) ? $data['subject'] : '';
		$grp          = array_key_exists('group', $data) ? (int) $data['group'] : 0;
		$recurse      = array_key_exists('recurse', $data) ? (int) $data['recurse'] : 0;
		$bcc          = array_key_exists('bcc', $data) ? (int) $data['bcc'] : 0;
		$disabled     = array_key_exists('disabled', $data) ? (int) $data['disabled'] : 0;
		$message_body = array_key_exists('message', $data) ? $data['message'] : '';

		// Automatically removes html formatting
		if (!$mode)
		{
			$message_body = InputFilter::getInstance()->clean($message_body, 'string');
		}

		// Check for a message body and subject
		if (!$message_body || !$subject)
		{
			$app->setUserState('com_users.display.mail.data', $data);
			$this->setError(Text::_('COM_USERS_MAIL_PLEASE_FILL_IN_THE_FORM_CORRECTLY'));

			return false;
		}

		// Get users in the group out of the ACL, if group is provided.
		$to = $grp !== 0 ? $access->getUsersByGroup($grp, $recurse) : array();

		// When group is provided but no users are found in the group.
		if ($grp !== 0 && !$to)
		{
			$rows = array();
		}
		else
		{
			// Get all users email and group except for senders
			$uid = (int) $user->id;
			$query = $db->getQuery(true)
				->select(
					[
						$db->quoteName('email'),
						$db->quoteName('name'),
					]
				)
				->from($db->quoteName('#__users'))
				->where($db->quoteName('id') . ' != :id')
				->bind(':id', $uid, ParameterType::INTEGER);

			if ($grp !== 0)
			{
				$query->whereIn($db->quoteName('id'), $to);
			}

			if ($disabled === 0)
			{
				$query->where($db->quoteName('block') . ' = 0');
			}

			$db->setQuery($query);
			$rows = $db->loadObjectList();
		}

		// Check to see if there are any users in this group before we continue
		if (!$rows)
		{
			$app->setUserState('com_users.display.mail.data', $data);

			if (in_array($user->id, $to))
			{
				$this->setError(Text::_('COM_USERS_MAIL_ONLY_YOU_COULD_BE_FOUND_IN_THIS_GROUP'));
			}
			else
			{
				$this->setError(Text::_('COM_USERS_MAIL_NO_USERS_COULD_BE_FOUND_IN_THIS_GROUP'));
			}

			return false;
		}

		// Get the Mailer
		$mailer = new MailTemplate('com_users.massmail.mail', $language->getTag());
		$params = ComponentHelper::getParams('com_users');

		try
		{
			// Build email message format.
			$data = [
				'subject' => stripslashes($subject),
				'body' => $message_body,
				'subjectprefix' => $params->get('mailSubjectPrefix', ''),
				'bodysuffix' => $params->get('mailBodySuffix', '')
			];
			$mailer->addTemplateData($data);

			$recipientType = $bcc ? 'bcc' : 'to';

			// Add recipients
			foreach ($rows as $row)
			{
				$mailer->addRecipient($row->email, $row->name, $recipientType);
			}

			if ($bcc)
			{
				$mailer->addRecipient($app->get('mailfrom'), $app->get('fromname'));
			}

			// Send the Mail
			$rs = $mailer->send();
		}
		catch (MailDisabledException | phpMailerException $exception)
		{
			try
			{
				Log::add(Text::_($exception->getMessage()), Log::WARNING, 'jerror');

				$rs = false;
			}
			catch (\RuntimeException $exception)
			{
				Factory::getApplication()->enqueueMessage(Text::_($exception->errorMessage()), 'warning');

				$rs = false;
			}
		}

		// Check for an error
		if ($rs !== true)
		{
			$app->setUserState('com_users.display.mail.data', $data);
			$this->setError($mailer->ErrorInfo);

			return false;
		}
		elseif (empty($rs))
		{
			$app->setUserState('com_users.display.mail.data', $data);
			$this->setError(Text::_('COM_USERS_MAIL_THE_MAIL_COULD_NOT_BE_SENT'));

			return false;
		}
		else
		{
			/**
			 * Fill the data (specially for the 'mode', 'group' and 'bcc': they could not exist in the array
			 * when the box is not checked and in this case, the default value would be used instead of the '0'
			 * one)
			 */
			$data['mode']    = $mode;
			$data['subject'] = $subject;
			$data['group']   = $grp;
			$data['recurse'] = $recurse;
			$data['bcc']     = $bcc;
			$data['message'] = $message_body;
			$app->setUserState('com_users.display.mail.data', array());
			$app->enqueueMessage(Text::plural('COM_USERS_MAIL_EMAIL_SENT_TO_N_USERS', count($rows)), 'message');

			return true;
		}
	}
}
