Server : Apache System : Linux indy02.toastserver.com 3.10.0-962.3.2.lve1.5.85.el7.x86_64 #1 SMP Thu Apr 18 15:18:36 UTC 2024 x86_64 User : palandch ( 1163) PHP Version : 7.1.33 Disable Function : NONE Directory : /home/palandch/public_html/core/components/formit/model/formit/ |
<?php /** * FormIt * * Copyright 2009-2012 by Shaun McCormick <shaun@modx.com> * * FormIt is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) any * later version. * * FormIt is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * FormIt; if not, write to the Free Software Foundation, Inc., 59 Temple Place, * Suite 330, Boston, MA 02111-1307 USA * * @package formit */ /** * Base Hooks handling class. Hooks can be used to run scripts prior to loading the form, or after a form has been * submitted. * * Hooks can be either a predefined list by FormIt, or custom MODX Snippets. They can also be chained to allow for * order-of-execution processing. Returning false from a Hook will end the chain. Returning false in a postHook will * prevent the form from being further submitted. * * @package formit */ class fiHooks { /** * A collection of all the processed errors so far. * @var array $errors * @access public */ public $errors = array(); /** * A collection of all the processed hooks so far. * @var array $hooks * @access public */ public $hooks = array(); /** * A reference to the modX instance. * @var modX $modx * @access public */ public $modx = null; /** * A reference to the FormIt instance. * @var FormIt $formit * @access public */ public $formit = null; /** * If a hook redirects, it needs to set this var to use proper order of execution on redirects/stores * @var string * @access public */ public $redirectUrl = null; /** * The current stored and parsed fields for the FormIt call. * @var array $fields */ public $fields = array(); /** * The type of Hook request that this represents * @var string $type */ public $type; /** * The constructor for the fiHooks class * * @param FormIt &$formit A reference to the FormIt class instance. * @param array $config Optional. An array of configuration parameters. * @param string $type The type of hooks this service class is loading * @return fiHooks */ function __construct(FormIt &$formit,array $config = array(),$type = '') { $this->formit =& $formit; $this->modx =& $formit->modx; $this->config = array_merge(array( 'placeholderPrefix' => 'fi.', 'errTpl' => '<span class="error">[[+error]]</span>', 'mathField' => 'math', 'mathOp1Field' => 'op1', 'mathOp2Field' => 'op2', 'mathOperatorField' => 'operator', ),$config); $this->type = $type; } /** * Loads an array of hooks. If one fails, will not proceed. * * @access public * @param array $hooks The hooks to run. * @param array $fields The fields and values of the form * @param array $customProperties An array of extra properties to send to the hook * @return array An array of field name => value pairs. */ public function loadMultiple($hooks,array $fields = array(),array $customProperties = array()) { if (empty($hooks)) return array(); if (is_string($hooks)) $hooks = explode(',',$hooks); $this->hooks = array(); $this->fields =& $fields; foreach ($hooks as $hook) { $hook = trim($hook); $success = $this->load($hook,$this->fields,$customProperties); if (!$success) return $this->hooks; /* dont proceed if hook fails */ } return $this->hooks; } /** * Load a hook. Stores any errors for the hook to $this->errors. * * @access public * @param string $hookName The name of the hook. May be a Snippet name. * @param array $fields The fields and values of the form. * @param array $customProperties Any other custom properties to load into a custom hook. * @return boolean True if hook was successful. */ public function load($hookName,array $fields = array(),array $customProperties = array()) { $success = false; if (!empty($fields)) $this->fields =& $fields; $this->hooks[] = $hookName; $reserved = array('load','_process','__construct','getErrorMessage'); if (method_exists($this,$hookName) && !in_array($hookName,$reserved)) { /* built-in hooks */ $success = $this->$hookName($this->fields); /** @var modSnippet $snippet */ } else if ($snippet = $this->modx->getObject('modSnippet',array('name' => $hookName))) { /* custom snippet hook */ $properties = array_merge($this->formit->config,$customProperties); $properties['formit'] =& $this->formit; $properties['hook'] =& $this; $properties['fields'] = $this->fields; $properties['errors'] =& $this->errors; $success = $snippet->process($properties); } else { /* search for a file-based hook */ $this->modx->parser->processElementTags('',$hookName,true,true); if (file_exists($hookName)) { $success = $this->_loadFileBasedHook($hookName,$customProperties); } else { /* no hook found */ $this->modx->log(modX::LOG_LEVEL_ERROR,'[FormIt] Could not find hook "'.$hookName.'".'); $success = true; } } if (is_array($success) && !empty($success)) { $this->errors = array_merge($this->errors,$success); $success = false; } else if ($success != true) { if (!isset($this->errors[$hookName])) $this->errors[$hookName] = ''; $this->errors[$hookName] .= ' '.$success; $success = false; } return $success; } /** * Attempt to load a file-based hook given a name * @param string $path The absolute path of the hook file * @param array $customProperties An array of custom properties to run with the hook * @return boolean True if the hook succeeded */ private function _loadFileBasedHook($path,array $customProperties = array()) { $scriptProperties = array_merge($this->formit->config,$customProperties); $formit =& $this->formit; $hook =& $this; $fields = $this->fields; $errors =& $this->errors; $modx =& $this->modx; $success = false; try { $success = include $path; } catch (Exception $e) { $this->modx->log(modX::LOG_LEVEL_ERROR,'[FormIt] '.$e->getMessage()); } return $success; } /** * Gets the error messages compiled into a single string. * * @access public * @param string $delim The delimiter between each message. * @return string The concatenated error message */ public function getErrorMessage($delim = "\n") { return implode($delim,$this->errors); } /** * Adds an error to the stack. * * @access private * @param string $key The field to add the error to. * @param string $value The error message. * @return string The added error message with the error wrapper. */ public function addError($key,$value) { if (!isset($this->errors[$key])) $this->errors[$key] = ''; $this->errors[$key] .= $value; return $this->errors[$key]; } /** * See if there are any errors in the stack. * * @return boolean */ public function hasErrors() { return !empty($this->errors); } /** * Get all errors for this current request * * @return array */ public function getErrors() { return $this->errors; } /** * Sets the value of a field. * * @param string $key The field name to set. * @param mixed $value The value to set to the field. * @return mixed The set value. */ public function setValue($key,$value) { $this->fields[$key] = $value; return $this->fields[$key]; } /** * Sets an associative array of field name and values. * * @param array $values A key/name pair of fields and values to set. */ public function setValues(array $values = array()) { foreach ($values as $key => $value) { $this->setValue($key,$value); } } /** * Gets the value of a field. * * @param string $key The field name to get. * @return mixed The value of the key, or null if non-existent. */ public function getValue($key) { if (array_key_exists($key,$this->fields)) { return $this->fields[$key]; } return null; } /** * Gets an associative array of field name and values. * * @return array $values A key/name pair of fields and values. */ public function getValues() { return $this->fields; } /** * Redirect to a specified URL. * * Properties needed: * - redirectTo - the ID of the Resource to redirect to. * * @param array $fields An array of cleaned POST fields * @return boolean False if unsuccessful. */ public function redirect(array $fields = array()) { if (empty($this->formit->config['redirectTo'])) return false; $redirectParams = !empty($this->formit->config['redirectParams']) ? $this->formit->config['redirectParams'] : ''; if (!empty($redirectParams)) { $prefix = $this->modx->getOption('placeholderPrefix',$this->formit->config,'fi.'); $this->modx->setPlaceholders($fields,$prefix); $this->modx->parser->processElementTags('',$redirectParams,true,true); $redirectParams = $this->modx->fromJSON($redirectParams); if (empty($redirectParams)) $redirectParams = ''; } $contextKey = $this->modx->context->get('key'); $resource = $this->modx->getObject('modResource',$this->formit->config['redirectTo']); if ($resource) { $contextKey = $resource->get('context_key'); } $url = $this->modx->makeUrl($this->formit->config['redirectTo'],$contextKey,$redirectParams,'full'); $this->setRedirectUrl($url); return true; } /** * Send an email of the form. * * Properties: * - emailTpl - The chunk name of the chunk that will be the email template. * This will send the values of the form as placeholders. * - emailTo - A comma separated list of email addresses to send to * - emailToName - A comma separated list of names to pair with addresses. * - emailFrom - The From: email address. Defaults to either the email * field or the emailsender setting. * - emailFromName - The name of the From: user. * - emailSubject - The subject of the email. * - emailHtml - Boolean, if true, email will be in HTML mode. * * @access public * @param array $fields An array of cleaned POST fields * @return boolean True if email was successfully sent. */ public function email(array $fields = array()) { $tpl = $this->modx->getOption('emailTpl',$this->formit->config,''); $emailHtml = (boolean)$this->modx->getOption('emailHtml',$this->formit->config,true); $emailConvertNewlines = (boolean)$this->modx->getOption('emailConvertNewlines',$this->formit->config,false); /* get from name */ $emailFrom = $this->modx->getOption('emailFrom',$this->formit->config,''); if (empty($emailFrom)) { $emailFrom = !empty($fields['email']) ? $fields['email'] : $this->modx->getOption('emailsender'); } $emailFrom = $this->_process($emailFrom,$fields); $emailFromName = $this->modx->getOption('emailFromName',$this->formit->config,$emailFrom); $emailFromName = $this->_process($emailFromName,$fields); /* get subject */ $useEmailFieldForSubject = $this->modx->getOption('emailUseFieldForSubject',$this->formit->config,true); if (!empty($fields['subject']) && $useEmailFieldForSubject) { $subject = $fields['subject']; } else { $subject = $this->modx->getOption('emailSubject',$this->formit->config,''); } $subject = $this->_process($subject,$fields); /* check email to */ $emailTo = $this->modx->getOption('emailTo',$this->formit->config,''); $emailToName = $this->modx->getOption('emailToName',$this->formit->config,$emailTo); if (empty($emailTo)) { $this->errors['emailTo'] = $this->modx->lexicon('formit.email_no_recipient'); $this->modx->log(modX::LOG_LEVEL_ERROR,'[FormIt] '.$this->modx->lexicon('formit.email_no_recipient')); return false; } /* compile message */ $origFields = $fields; if (empty($tpl)) { $tpl = 'email'; $f = ''; $multiSeparator = $this->modx->getOption('emailMultiSeparator',$this->formit->config,"\n"); $multiWrapper = $this->modx->getOption('emailMultiWrapper',$this->formit->config,"[[+value]]"); foreach ($fields as $k => $v) { if ($k == 'nospam') continue; if (is_array($v) && !empty($v['name']) && isset($v['error']) && $v['error'] == UPLOAD_ERR_OK) { $v = $v['name']; $f[$k] = '<strong>'.$k.'</strong>: '.$v.'<br />'; } else if (is_array($v)) { $vOpts = array(); foreach ($v as $vKey => $vValue) { if (is_string($vKey) && !empty($vKey)) { $vKey = $k.'.'.$vKey; $f[$vKey] = '<strong>'.$vKey.'</strong>: '.$vValue.'<br />'; } else { $vOpts[] = str_replace('[[+value]]',$vValue,$multiWrapper); } } $newValue = implode($multiSeparator,$vOpts); if (!empty($vOpts)) { $f[$k] = '<strong>'.$k.'</strong>:'.$newValue; } } else { $f[$k] = '<strong>'.$k.'</strong>: '.$v.'<br />'; } } $fields['fields'] = implode("\n",$f); } else { /* handle file/checkboxes in email tpl */ $multiSeparator = $this->modx->getOption('emailMultiSeparator',$this->formit->config,"\n"); if (empty($multiSeparator)) $multiSeparator = "\n"; if ($multiSeparator == '\n') $multiSeparator = "\n"; /* allow for inputted newlines */ $multiWrapper = $this->modx->getOption('emailMultiWrapper',$this->formit->config,"[[+value]]"); if (empty($multiWrapper)) $multiWrapper = '[[+value]]'; foreach ($fields as $k => &$v) { if (is_array($v) && !empty($v['name']) && isset($v['error']) && $v['error'] == UPLOAD_ERR_OK) { $v = $v['name']; } else if (is_array($v)) { $vOpts = array(); foreach ($v as $vKey => $vValue) { if (is_string($vKey) && !empty($vKey)) { $vKey = $k.'.'.$vKey; $fields[$vKey] = $vValue; unset($fields[$k]); } else { $vOpts[] = str_replace('[[+value]]',$vValue,$multiWrapper); } } $v = implode($multiSeparator,$vOpts); } } } $message = $this->formit->getChunk($tpl,$fields); $message = $this->_process($message,$this->config); /* load mail service */ $this->modx->getService('mail', 'mail.modPHPMailer'); /* set HTML */ $this->modx->mail->setHTML($emailHtml); /* set email main properties */ $this->modx->mail->set(modMail::MAIL_BODY,$emailHtml && $emailConvertNewlines ? nl2br($message) : $message); $this->modx->mail->set(modMail::MAIL_FROM, $emailFrom); $this->modx->mail->set(modMail::MAIL_FROM_NAME, $emailFromName); $this->modx->mail->set(modMail::MAIL_SENDER, $emailFrom); $this->modx->mail->set(modMail::MAIL_SUBJECT, $subject); /* handle file fields */ foreach ($origFields as $k => $v) { $attachmentIndex = 0; if (is_array($v) && !empty($v['tmp_name']) && isset($v['error']) && $v['error'] == UPLOAD_ERR_OK) { if (empty($v['name'])) { $v['name'] = 'attachment'.$attachmentIndex; } $this->modx->mail->mailer->AddAttachment($v['tmp_name'],$v['name'],'base64',!empty($v['type']) ? $v['type'] : 'application/octet-stream'); $attachmentIndex++; } } /* add to: with support for multiple addresses */ $emailTo = explode(',',$emailTo); $emailToName = explode(',',$emailToName); $numAddresses = count($emailTo); for ($i=0;$i<$numAddresses;$i++) { $etn = !empty($emailToName[$i]) ? $emailToName[$i] : ''; if (!empty($etn)) $etn = $this->_process($etn,$fields); $emailTo[$i] = $this->_process($emailTo[$i],$fields); if (!empty($emailTo[$i])) { $this->modx->mail->address('to',$emailTo[$i],$etn); } } /* reply to */ $emailReplyTo = $this->modx->getOption('emailReplyTo',$this->formit->config,$emailFrom); $emailReplyTo = $this->_process($emailReplyTo,$fields); $emailReplyToName = $this->modx->getOption('emailReplyToName',$this->formit->config,$emailFromName); $emailReplyToName = $this->_process($emailReplyToName,$fields); if (!empty($emailReplyTo)) { $this->modx->mail->address('reply-to',$emailReplyTo,$emailReplyToName); } /* cc */ $emailCC = $this->modx->getOption('emailCC',$this->formit->config,''); if (!empty($emailCC)) { $emailCCName = $this->modx->getOption('emailCCName',$this->formit->config,''); $emailCC = explode(',',$emailCC); $emailCCName = explode(',',$emailCCName); $numAddresses = count($emailCC); for ($i=0;$i<$numAddresses;$i++) { $etn = !empty($emailCCName[$i]) ? $emailCCName[$i] : ''; if (!empty($etn)) $etn = $this->_process($etn,$fields); $emailCC[$i] = $this->_process($emailCC[$i],$fields); if (!empty($emailCC[$i])) { $this->modx->mail->address('cc',$emailCC[$i],$etn); } } } /* bcc */ $emailBCC = $this->modx->getOption('emailBCC',$this->formit->config,''); if (!empty($emailBCC)) { $emailBCCName = $this->modx->getOption('emailBCCName',$this->formit->config,''); $emailBCC = explode(',',$emailBCC); $emailBCCName = explode(',',$emailBCCName); $numAddresses = count($emailBCC); for ($i=0;$i<$numAddresses;$i++) { $etn = !empty($emailBCCName[$i]) ? $emailBCCName[$i] : ''; if (!empty($etn)) $etn = $this->_process($etn,$fields); $emailBCC[$i] = $this->_process($emailBCC[$i],$fields); if (!empty($emailBCC[$i])) { $this->modx->mail->address('bcc',$emailBCC[$i],$etn); } } } /* send email */ if (!$this->formit->inTestMode) { $sent = $this->modx->mail->send(); } else { $sent = true; } $this->modx->mail->reset(array( modMail::MAIL_CHARSET => $this->modx->getOption('mail_charset',null,'UTF-8'), modMail::MAIL_ENCODING => $this->modx->getOption('mail_encoding',null,'8bit'), )); if (!$sent) { $this->errors[] = $this->modx->lexicon('formit.email_not_sent').' '.print_r($this->modx->mail->mailer->ErrorInfo,true); $this->modx->log(modX::LOG_LEVEL_ERROR,'[FormIt] '.$this->modx->lexicon('formit.email_not_sent').' '.print_r($this->modx->mail->mailer->ErrorInfo,true)); } return $sent; } /** * Processes string and sets placeholders * * @param string $str The string to process * @param array $placeholders An array of placeholders to replace with values * @return string The parsed string */ public function _process($str,array $placeholders = array()) { if (is_string($str)) { foreach ($placeholders as $k => $v) { if (is_scalar($k) && is_scalar($v)) { $str = str_replace('[[+'.$k.']]',$v,$str); } } } $this->modx->parser->processElementTags('',$str,true,false); return $str; } /** * Ensure the a field passes a spam filter. * * Properties: * - spamEmailFields - The email fields to check. A comma-delimited list. * * @access public * @param array $fields An array of cleaned POST fields * @return boolean True if email was successfully sent. */ public function spam(array $fields = array()) { $passed = true; $spamEmailFields = $this->modx->getOption('spamEmailFields',$this->formit->config,'email'); $emails = explode(',',$spamEmailFields); if ($this->modx->loadClass('stopforumspam.StopForumSpam',$this->formit->config['modelPath'],true,true)) { $sfspam = new StopForumSpam($this->modx); $checkIp = $this->modx->getOption('spamCheckIp',$this->formit->config,false); $ip = $checkIp ? $_SERVER['REMOTE_ADDR'] : ''; foreach ($emails as $email) { $spamResult = $sfspam->check($ip,$fields[$email]); if (!empty($spamResult)) { $spamFields = implode($this->modx->lexicon('formit.spam_marked')."\n<br />",$spamResult); $this->addError($email,$this->modx->lexicon('formit.spam_blocked',array( 'fields' => $spamFields, ))); $passed = false; } } } else { $this->modx->log(modX::LOG_LEVEL_ERROR,'[FormIt] Couldnt load StopForumSpam class.'); } return $passed; } /** * Adds in reCaptcha support to FormIt * * @access public * @param array $fields An array of cleaned POST fields * @return boolean True if email was successfully sent. */ public function recaptcha(array $fields = array()) { $passed = false; /** @var FormItReCaptcha $reCaptcha */ $reCaptcha = $this->formit->request->loadReCaptcha(); if (empty($reCaptcha->config[FormItReCaptcha::OPT_PRIVATE_KEY])) return false; $response = $reCaptcha->checkAnswer($_SERVER['REMOTE_ADDR'],$_POST['recaptcha_challenge_field'],$_POST['recaptcha_response_field']); if (!$response->is_valid) { $this->addError('recaptcha',$this->modx->lexicon('recaptcha.incorrect',array( 'error' => $response->error != 'incorrect-captcha-sol' ? $response->error : '', ))); } else { $passed = true; } return $passed; } /** * Set a URL to redirect to after all hooks run successfully. * * @param string $url The URL to redirect to after all hooks execute */ public function setRedirectUrl($url) { $this->redirectUrl = $url; } /** * Get the specified redirection url * * @return null|string */ public function getRedirectUrl() { return $this->redirectUrl; } /** * Math field hook for anti-spam math input field. * * @access public * @param array $fields An array of cleaned POST fields * @return boolean True if email was successfully sent. */ public function math(array $fields = array()) { $mathField = $this->modx->getOption('mathField',$this->config,'math'); if (!isset($fields[$mathField])) { $this->errors[$mathField] = $this->modx->lexicon('formit.math_field_nf',array('field' => $mathField)); return false; } if (empty($fields[$mathField])) { $this->errors[$mathField] = $this->modx->lexicon('formit.field_required',array('field' => $mathField)); return false; } $op1Field = $this->modx->getOption('mathOp1Field',$this->config,'op1'); if (empty($fields[$op1Field])) { $this->errors[$mathField] = $this->modx->lexicon('formit.math_field_nf',array('field' => $op1Field)); return false; } $op2Field = $this->modx->getOption('mathOp2Field',$this->config,'op2'); if (empty($fields[$op2Field])) { $this->errors[$mathField] = $this->modx->lexicon('formit.math_field_nf',array('field' => $op2Field)); return false; } $operatorField = $this->modx->getOption('mathOperatorField',$this->config,'operator'); if (empty($fields[$operatorField])) { $this->errors[$mathField] = $this->modx->lexicon('formit.math_field_nf',array('field' => $operatorField)); return false; } $answer = false; $op1 = (int)$fields[$op1Field]; $op2 = (int)$fields[$op2Field]; switch ($fields[$operatorField]) { case '+': $answer = $op1 + $op2; break; case '-': $answer = $op1 - $op2; break; case '*': $answer = $op1 * $op2; break; } $guess = (int)$fields[$mathField]; $passed = (boolean)($guess == $answer); if (!$passed) { $this->addError($mathField,$this->modx->lexicon('formit.math_incorrect')); } return $passed; } /** * Process any errors returned by the hooks and set them to placeholders * @return void */ public function processErrors() { $errors = array(); $placeholderErrors = $this->getErrors(); foreach ($placeholderErrors as $key => $error) { $errors[$key] = str_replace('[[+error]]',$error,$this->config['errTpl']); } $this->modx->toPlaceholders($errors,$this->config['placeholderPrefix'].'error'); $errorMsg = $this->getErrorMessage(); if (!empty($errorMsg)) { $this->modx->setPlaceholder($this->config['placeholderPrefix'].'error_message',$errorMsg); } } /** * Gather fields and set them into placeholders for pre-fetching * @return array */ public function gatherFields() { if (empty($this->fields)) return array(); $fs = $this->getValues(); /* better handling of checkbox values when input name is an array[] */ foreach ($fs as $f => $v) { if (is_array($v)) { $v = implode(',',$v); } $fs[$f] = $v; } $this->modx->toPlaceholders($fs,$this->config['placeholderPrefix'],''); return $this->getValues(); } }