Spade
Mini Shell
| Directory:~$ /home/lmsyaran/public_html/joomla5/plugins/fabrik_form/email/ |
| [Home] [System Details] [Kill Me] |
<?php
/**
* @package Joomla.Plugin
* @subpackage Fabrik.form.email
* @copyright Copyright (C) 2005-2020 Media A-Team, Inc. - All rights
reserved.
* @license GNU/GPL http://www.gnu.org/copyleft/gpl.html
*/
// No direct access
defined('_JEXEC') or die('Restricted access');
use Joomla\CMS\Language\Text;
use Joomla\CMS\Document\Document;
use Joomla\CMS\Factory;
use Joomla\CMS\Filesystem\File;
use Joomla\CMS\Filesystem\Path;
use Fabrik\Helpers\ArrayHelper;
use Fabrik\Helpers\Html;
use Fabrik\Helpers\Pdf;
use Fabrik\Helpers\StringHelper;
use Fabrik\Helpers\Worker;
use Joomla\CMS\Profiler\Profiler;
use Joomla\String\Normalise;
use Fabrik\Helpers\Php;
// Require the abstract plugin class
require_once COM_FABRIK_FRONTEND . '/models/plugin-form.php';
/**
* Send email upon form submission
*
* @package Joomla.Plugin
* @subpackage Fabrik.form.email
* @since 3.0
*/
class PlgFabrik_FormEmail extends PlgFabrik_Form
{
/**
* Attachment files
*
* @var array
*/
protected $attachments = array();
/**
* Attachment files to delete after use
*
* @var array
*/
protected $deleteAttachments = array();
/**
* Posted form keys that we don't want to include in the message
* This is basically the fileupload elements
*
* @var array
*/
protected $dontEmailKeys = null;
/**
* MOVED TO PLUGIN.PHP SHOULDPROCESS()
* determines if a condition has been set and decides if condition is
matched
*
* @param object $params
*
* @return bool true if you should send the email, false stops sending of
email
*/
/*function shouldSend(&$params)
{
}*/
/**
* Run right at the end of the form processing
* form needs to be set to record in database for this to hook to be
called
*
* @return bool
*/
public function onAfterProcess()
{
$profiler = Profiler::getInstance('Application');
JDEBUG ? $profiler->mark("email: start: onAfterProcess") :
null;
$params = $this->getParams();
$input = $this->app->input;
jimport('joomla.mail.helper');
$w = new Worker;
//Reset attachments in case of multiple email plugins
$this->attachments = [];
/** @var \FabrikFEModelForm $formModel */
$formModel = $this->getModel();
$emailTemplate = Path::clean(JPATH_SITE .
'/plugins/fabrik_form/email/tmpl/' .
$params->get('email_template', ''));
$this->data = $this->getProcessData();
/* $$$ hugh - moved this to here from above the previous line, 'cos
it needs $this->data
* check if condition exists and is met
*/
if ($this->alreadySent() ||
!$this->shouldProcess('email_conditon', null, $params))
{
return true;
}
// set up some useful placeholders for links to form
$this->data['fabrik_editurl'] = COM_FABRIK_LIVESITE .
'index.php?option=com_' . $this->package .
'&view=form&formid=' .
$formModel->get('id') . '&rowid='
. $input->get('rowid', '', 'string');
$this->data['fabrik_viewurl'] = COM_FABRIK_LIVESITE .
'index.php?option=com_' . $this->package .
'&view=details&formid=' .
$formModel->get('id') . '&rowid='
. $input->get('rowid', '', 'string');
$this->data['fabrik_editlink'] = '<a
href="' . $this->data['fabrik_editurl'] .
'">' . Text::_('COM_FABRIK_EDIT') .
'</a>';
$this->data['fabrik_viewlink'] = '<a
href="' . $this->data['fabrik_viewurl'] .
'">' . Text::_('COM_FABRIK_VIEW') .
'</a>';
/**
* Added option to run content plugins on message text. Note that rather
than run it one time at the
* end of the following code, after we have assembled all the various
options in to a single $message,
* it needs to be run separately on each type of content. This is
because we do placeholder replacement
* in various places, which will strip all {text} which doesn't
match element names.
*/
$runContentPlugins =
$params->get('email_run_content_plugins', '0') ===
'1';
$contentTemplate = $params->get('email_template_content');
$content = $contentTemplate != '' ?
Html::getContentTemplate($contentTemplate, 'both',
$runContentPlugins) : '';
// Always send as html as even text email can contain html from wysiwyg
editors
$htmlEmail = true;
$messageTemplate = '';
if (File::exists($emailTemplate))
{
$messageTemplate = File::getExt($emailTemplate) == 'php' ?
$this->_getPHPTemplateEmail($emailTemplate)
: $this->_getTemplateEmail($emailTemplate);
// $$$ hugh - added ability for PHP template to return false to abort,
same as if 'condition' was was false
if ($messageTemplate === false)
{
return true;
}
if ($runContentPlugins === true)
{
Html::runContentPlugins($messageTemplate, false);
}
$messageTemplate = str_replace('{content}', $content,
$messageTemplate);
}
$messageText = $params->get('email_message_text',
'');
if (!empty($messageText))
{
if ($runContentPlugins === true)
{
Html::runContentPlugins($messageText, false);
}
$messageText = str_replace('{content}', $content,
$messageText);
$messageText = str_replace('{template}', $messageTemplate,
$messageText);
$messageText = $w->parseMessageForPlaceholder($messageText,
$this->data, false);
}
if (!empty($messageText))
{
$message = $messageText;
}
elseif (!empty($messageTemplate))
{
$message = $messageTemplate;
}
elseif (!empty($content))
{
$message = $content;
}
else
{
$message = $this->_getTextEmail();
}
$this->addAttachments();
$cc = null;
$bcc = null;
// $$$ hugh - test stripslashes(), should be safe enough.
$message = stripslashes($message);
Pdf::fullPaths($message);
// $$$ rob if email_to is not a valid email address check the raw value
to see if that is
$emailTo = explode(',',
$params->get('email_to')??'');
foreach ($emailTo as &$emailKey)
{
$emailKey = $w->parseMessageForPlaceholder($emailKey, $this->data,
false);
// Can be in repeat group in which case returns
"email1,email2"
$emailKey = explode(',', $emailKey);
foreach ($emailKey as &$key)
{
// $$$ rob added strstr test as no point trying to add raw suffix if
not placeholder in $emailKey
if (!Worker::isEmail($key) && trim($key) !== ''
&& strstr($key, '}'))
{
$key = explode('}', $key);
if (substr($key[0], -4) !== '_raw')
{
$key = $key[0] . '_raw}';
}
else
{
$key = $key[0] . '}';
}
$key = $w->parseMessageForPlaceholder($key, $this->data, false);
}
}
}
// Reduce back down to single dimension array
foreach ($emailTo as $i => $a)
{
foreach ($a as $v)
{
$emailTo[] = $v;
}
unset($emailTo[$i]);
}
$emailToEval = $params->get('email_to_eval', '');
if (!empty($emailToEval))
{
$emailToEval = $w->parseMessageForPlaceholder($emailToEval,
$this->data, false);
Worker::clearEval();
$emailToEval = Php::Eval(['code' => $emailToEval,
'vars' => ['formModel'=>$formModel],
'thisVars' => ['data' =>
$this->data]]);
Worker::logEval($emailToEval, 'Caught exception on eval in email
emailto : %s');
$emailToEval = explode(',', $emailToEval);
$emailTo = array_merge($emailTo, $emailToEval);
}
@list($emailFrom, $emailFromName) = explode(":",
$w->parseMessageForPlaceholder($params->get('email_from'),
$this->data, false), 2);
if (empty($emailFrom))
{
$emailFrom = $this->config->get('mailfrom');
}
if (empty($emailFromName))
{
$emailFromName = $this->config->get('fromname',
$emailFrom);
}
// Changes by JFQ
@list($returnPath, $returnPathName) = explode(":",
$w->parseMessageForPlaceholder($params->get('return_path'),
$this->data, false), 2);
if (empty($returnPath))
{
$returnPath = null;
}
if (empty($returnPathName))
{
$returnPathName = null;
}
// End changes
$subject = Text::_($params->get('email_subject'));
if ($subject == '')
{
$subject = $this->config->get('sitename') . " ::
Email";
}
$subject = preg_replace_callback('/&#([0-9a-fx]+);/mi',
array($this, 'replace_num_entity'), $subject);
$attachType = $params->get('email_attach_type',
'');
$attachFileName = $this->config->get('tmp_path') .
'/' . uniqid() . '.' . $attachType;
$query = $this->_db->getQuery(true);
$emailTo = array_map('trim', $emailTo);
// Add any assigned groups to the to list
$sendTo = (array) $params->get('to_group');
$groupEmails = (array) $this->getUsersInGroups($sendTo, $field =
'email');
$emailTo = array_merge($emailTo, $groupEmails);
$emailTo = array_unique($emailTo);
// Remove blank email addresses
$emailTo = array_filter($emailTo);
$dbEmailTo = array_map(array($this->_db, 'quote'),
$emailTo);
// Get an array of user ids from the email to array
if (!empty($dbEmailTo))
{
$query->select('id,
email')->from('#__users')->where('email IN
(' . implode(',', $dbEmailTo) . ')');
$this->_db->setQuery($query);
$userIds = $this->_db->loadObjectList('email');
}
else
{
$userIds = array();
}
$customHeadersEval = $params->get('email_headers_eval',
'');
$customHeaders = array();
if (!empty($customHeadersEval))
{
$customHeadersEval =
$w->parseMessageForPlaceholder($customHeadersEval, $this->data,
false);
$customHeaders = Php::Eval(['code' => $customHeadersEval]);
Worker::logEval($customHeaders, 'Caught exception on eval in email
custom headers : %s');
}
// Send email
foreach ($emailTo as $email)
{
$email = strip_tags($email);
if (Worker::isEmail($email))
{
$thisAttachments = $this->attachments;
$this->data['emailto'] = $email;
$userId = array_key_exists($email, $userIds) ?
$userIds[$email]->id : 0;
$thisUser = Factory::getUser($userId);
$thisMessage = $w->parseMessageForPlaceholder($message,
$this->data, true, false, $thisUser);
$thisSubject = strip_tags($w->parseMessageForPlaceholder($subject,
$this->data, true, false, $thisUser));
if (!empty($attachType))
{
if (File::write($attachFileName, $thisMessage))
{
$thisAttachments[] = $attachFileName;
}
else
{
$attachFileName = '';
}
}
try
{
$this->pdfAttachment($thisAttachments);
}
catch (Exception $e)
{
$this->app->enqueueMessage($e->getMessage(),
'error');
}
/*
* Sanity check for attachment files existing. Could have base folder
paths for things
* like file upload elements with no file. As of J! 3.5.1, the J!
mailer tosses an exception
* if files don't exist. We catch that in the sendMail helper,
but remove non-files here anyway
*/
foreach ($thisAttachments as $aKey => $attachFile)
{
if (!File::exists($attachFile))
{
unset($thisAttachments[$aKey]);
}
}
JDEBUG ? $profiler->mark("email: sendMail start: " .
$email) : null;
$res = Worker::sendMail(
$emailFrom,
$emailFromName,
$email,
$thisSubject,
$thisMessage,
$htmlEmail,
$cc,
$bcc,
$thisAttachments,
$returnPath,
$returnPathName,
$customHeaders
);
JDEBUG ? $profiler->mark("email: sendMail end: " . $email)
: null;
/*
* $$$ hugh - added some error reporting, but not sure if 'invalid
address' is the appropriate message,
* may need to add a generic "there was an error sending the
email" message
*/
if ($res !== true)
{
$this->app->enqueueMessage(Text::sprintf('PLG_FORM_EMAIL_DID_NOT_SEND_EMAIL',
$email), 'notice');
// Log the info
$opts = new stdClass;
$opts->listid =
$formModel->getListModel()->getId();
$opts->formid = $formModel->getId();
$opts->rowid = $this->data['rowid'];
$opts->userid = $userId;
$opts->emailFrom = $emailFrom;
$opts->emailFromName = $emailFromName;
$opts->emailReturn = $returnPath;
$opts->emailSubject = $thisSubject;
$opts->emailAttachments = $thisAttachments;
$opts->emailTo = $email;
$opts->emailCc = $cc;
$opts->emailBcc = $bcc;
$opts->emailMsg = $thisMessage;
$msgType = 'fabrik.form.email.error';
$msg = new stdClass;
$msg->opts = $opts;
$msg->data = $this->data;
$msg = json_encode($msg);
$this->doLog($msgType, $msg);
}
if (File::exists($attachFileName))
{
File::delete($attachFileName);
}
}
else
{
$this->app->enqueueMessage(Text::sprintf('PLG_FORM_EMAIL_DID_NOT_SEND_EMAIL_INVALID_ADDRESS',
$email), 'notice');
}
}
foreach ($this->deleteAttachments as $attachment)
{
if (File::exists($attachment))
{
File::delete($attachment);
}
}
$this->updateRow();
JDEBUG ? $profiler->mark("email: end: onAfterProcess") :
null;
return true;
}
/**
* Check to see if there is an "update field" specified, and if
it is already non-zero
*
* @return bool
*/
protected function alreadySent()
{
$params = $this->getParams();
$updateField = $params->get('email_update_field');
if (!empty($updateField))
{
$updateField .= '_raw';
$updateEl = StringHelper::safeColNameToArrayKey($updateField);
$updateVal = ArrayHelper::getValue($this->data, $updateEl,
'');
$updateVal = is_array($updateVal) ? $updateVal[0] : $updateVal;
return !empty($updateVal);
}
return false;
}
/**
* Attach the details view as a PDF to the email
*
* @param array &$thisAttachments Attachments
*
* @throws RuntimeException
*
* @return void
*/
protected function pdfAttachment(&$thisAttachments)
{
$params = $this->getParams();
if ($params->get('attach_pdf', 0) == 0)
{
return;
}
/** @var FabrikFEModelForm $model */
$model = $this->getModel();
$model->setRowId($this->data['rowid']);
/**
* Create a PDF document and assign it to the factory, so subsequent
document checks (like getTmpl) in the
* display get the right data.
*/
// Set up document properties.
$lang = Factory::getApplication()->getLanguage();
$attributes = array(
'charset' => 'utf-8',
'lineend' => 'unix',
'tab' => ' ',
'language' => $lang->getTag(),
'direction' => $lang->isRtl() ? 'rtl' :
'ltr'
);
// Get a PDF document and set the title.
$document = Factory::getDocument();
$pdfDoc = Document::getInstance('pdf', $attributes);
$pdfDoc->setTitle($document->getTitle());
// assign it to the factory
Factory::$document = $pdfDoc;
$input = $this->app->input;
/*
* * unset the template, to make sure view display picks up the PDF one
*/
$model->tmpl = null;
$orig['view'] = $input->get('view');
$orig['format'] = $input->get('format');
$input->set('view', 'details');
$input->set('format', 'pdf');
// set editable false so things like getFormCss() pick up the detail, not
form, CSS
$model->setEditable(false);
// Ensure the package is set to fabrik
$prevUserState =
$this->app->getUserState('com_fabrik.package');
$this->app->setUserState('com_fabrik.package',
'fabrik');
try
{
// Require files and set up DOM pdf
require_once JPATH_SITE .
'/components/com_fabrik/controllers/details.php';
// if selected PDF engine isn't installed, this will throw an
exception which we should catch
Worker::canPdf(true);
$size = strtoupper($params->get('pdf_size',
'A4'));
$orientation = $params->get('pdf_orientation',
'portrait');
/**
* Unfortunately, we need to reload the data, so it's in the right
format. Can't use the
* submitted data. "One of these days" we need to have a
serious look at normalizing the data formats,
* so submitted data is in the same format (once processed) as data read
from the database.
*/
$controller = new FabrikControllerDetails;
$model->data = null;
$controller->_model = $model;
$controller->_model->data = $model->getData();
$controller->_model->tmpl = null;
/*
* Allows us to bypass "view records" ACL settings for
creating the details view
*/
$model->getListModel()->setLocalPdf();
/*
* get the CSS in a kinda hacky way
* (moved to after setting up the model and controller, so things like
tmpl have been reset)
*/
$model->getFormCss();
$pdfbootstrap = $params->get('pdf_include_bootstrap',1);
if ($pdfbootstrap) FabrikhelperHTML::loadBootstrapCSS(true);
foreach ($pdfDoc->_styleSheets as $url => $ss)
{
if (!strstr($url, COM_FABRIK_LIVESITE))
{
$url = COM_FABRIK_LIVESITE_ROOT . $url;
}
$url = htmlspecialchars_decode($url);
$formCss[] = file_get_contents($url);
}
// Store in output buffer
ob_start();
$controller->display();
$html = ob_get_contents();
ob_end_clean();
if (!empty($formCss))
{
$html = "<style>\n" . implode("\n", $formCss)
. "</style>\n" . $html;
}
// Store the file in the tmp folder so it can be attached
$layout =
$model->getLayout('form.fabrik-pdf-title');
$displayData = new stdClass;
$displayData->doc = $document;
$displayData->model = $model;
$fileName = $layout->render($displayData);
$file = $this->config->get('tmp_path') .
'/' . Normalise::toDashSeparated($fileName) . '.pdf';
$pdf = Pdf::renderPdf($html, $size, $orientation);
if (File::write($file, $pdf))
{
$thisAttachments[] = $file;
}
else
{
throw new RuntimeException('Could not write PDF file to tmp
folder');
}
}
catch (Exception $e)
{
$this->app->enqueueMessage($e->getMessage(),
'error');
}
// set back to editable
$model->setEditable(true);
// Set the package back to what it was before rendering the module
$this->app->setUserState('com_fabrik.package',
$prevUserState);
// Reset input
foreach ($orig as $key => $val)
{
$input->set($key, $val);
}
// Swap the documents back.
Factory::$document = $document;
}
/**
* Use a php template for advanced email templates, particularly for forms
with repeat group data
*
* @param string $tmpl Path to template
*
* @return string email message
*/
protected function _getPHPTemplateEmail($tmpl)
{
$emailData = $this->data;
$formModel = $this->getModel();
// Start capturing output into a buffer
ob_start();
$result = require $tmpl;
$message = ob_get_contents();
ob_end_clean();
if ($result === false)
{
return false;
}
return $message;
}
/**
* Add attachments to the email
*
* @return void
*/
protected function addAttachments()
{
$params = $this->getParams();
$data = $this->getProcessData();
/** @var FabrikFEModelForm $formModel */
$formModel = $this->getModel();
$groups = $formModel->getGroupsHiarachy();
foreach ($groups as $groupModel)
{
$elementModels = $groupModel->getPublishedElements();
foreach ($elementModels as $elementModel)
{
$elName = $elementModel->getFullName(true, false);
if (array_key_exists($elName, $this->data))
{
if (method_exists($elementModel, 'addEmailAttachement'))
{
if (array_key_exists($elName . '_raw', $data))
{
$val = $data[$elName . '_raw'];
}
else
{
$val = $data[$elName];
}
if (is_array($val))
{
if (is_array(current($val)))
{
// Can't implode multi dimensional arrays
$val = json_encode($val);
$val = Worker::JSONtoData($val, true);
}
}
else
{
$val = array($val);
}
foreach ($val as $v)
{
$file = $elementModel->addEmailAttachement($v);
if ($file !== false)
{
$this->attachments[] = $file;
if ($elementModel->shouldDeleteEmailAttachment($v))
{
$this->deleteAttachments[] = $file;
}
}
}
}
}
}
}
// $$$ hugh - added an optional eval for adding attachments.
// Eval'd code should just return an array of file paths which we
merge with $this->attachments[]
$w = new Worker;
$emailAttachEval =
$w->parseMessageForPlaceholder($params->get('email_attach_eval',
''), $this->data, false);
if (!empty($emailAttachEval))
{
Worker::clearEval();
$email_attach_array = Php::Eval(['code' =>
$emailAttachEval, 'vars' =>
['formModel'=>$formModel],
'thisVars' => ['data' =>
$this->data]]);
Worker::logEval($email_attach_array, 'Caught exception on eval in
email email_attach_eval : %s');
if (!empty($email_attach_array))
{
$this->attachments = array_merge($this->attachments,
$email_attach_array);
}
}
}
/**
* Get an array of keys we don't want to email to the user
*
* @return array
*/
protected function getDontEmailKeys()
{
if (is_null($this->dontEmailKeys))
{
$this->dontEmailKeys = array();
foreach ($_FILES as $key => $file)
{
$this->dontEmailKeys[] = $key;
}
}
return $this->dontEmailKeys;
}
/**
* Template email handling routine, called if email template specified
*
* @param string $emailTemplate path to template
*
* @return string email message
*/
protected function _getTemplateEmail($emailTemplate)
{
return file_get_contents($emailTemplate);
}
/**
* Default email handling routine, called if no email template specified
*
* @return string email message
*/
protected function _getTextEmail()
{
$data = $this->getProcessData();
$ignore = $this->getDontEmailKeys();
$message = '';
/** @var FabrikFEModelForm $formModel */
$formModel = $this->getModel();
$groupModels = $formModel->getGroupsHiarachy();
foreach ($groupModels as &$groupModel)
{
$elementModels = $groupModel->getPublishedElements();
foreach ($elementModels as &$elementModel)
{
$element = $elementModel->getElement();
// @TODO - how about adding a 'renderEmail()' method to
element model, so specific element types can render themselves?
$key = (!array_key_exists($element->name, $data)) ?
$elementModel->getFullName(true, false) : $element->name;
if (!in_array($key, $ignore))
{
$val = '';
if (is_array(ArrayHelper::getValue($data, $key)))
{
// Repeat group data
foreach ($data[$key] as $k => $v)
{
if (is_array($v))
{
$val = implode(", ", $v);
}
$val .= count($data[$key]) == 1 ? ": $v<br />" :
($k++) . ": $v<br />";
}
}
else
{
$val = ArrayHelper::getValue($data, $key);
}
$val = StringHelper::rtrimword($val, "<br />");
$val = stripslashes($val);
// Set $val to default value if empty
if ($val == '')
{
$val = " - ";
}
// Don't add a second ":"
$label = trim(strip_tags($element->label));
$message .= $label;
if (
strlen($label) != 0
&&
\Joomla\String\StringHelper::strpos(
$label, ':',
\Joomla\String\StringHelper::strlen($label) - 1
) === false
)
{
$message .= ':';
}
$message .= "<br />" . $val . "<br /><br
/>";
}
}
}
$message = Text::_('Email from') . ' ' .
$this->config->get('sitename') . '<br />' .
Text::_('Message') . ':'
. "<br />===================================<br
/>" . "<br />" . stripslashes($message);
return $message;
}
/**
* Update row
*/
private function updateRow()
{
$params = $this->getParams();
$updateField = $params->get('email_update_field');
$rowid = $this->data['rowid'];
if (!empty($updateField) && !empty($rowid))
{
$this->getModel()->getListModel()->updateRow($rowid,
$updateField, '1');
}
}
}