| Current Path : /var/www/element/data/element_backup.2025.12.07/vsl-gates.ru/assets/snippets/eform/ |
| Current File : /var/www/element/data/element_backup.2025.12.07/vsl-gates.ru/assets/snippets/eform/eform.inc.php |
<?php
# eForm 1.4.4 - Electronic Form Snippet
# Original created by: Raymond Irving 15-Dec-2004.
# Extended by: Jelle Jager (TobyL) September 2006
# -----------------------------------------------------
# local revision: $Id: eform.inc.php,v 1.3 2006/11/22 14:48:50 jelle Exp $
#
# Captcha image support - thanks to Djamoer
# Multi checkbox, radio, select support - thanks to Djamoer
# Form Parser and extended validation - by Jelle Jager
#
# see docs/eform.htm for installation and usage information
#
# VERSION HISTORY
# Version 1.4.3
# This is a bug squashing release only.
# FIXED: When using multiple forms on a page all forms are parsed and validated even
# when only one form is submitted.
# FIXED: fields array values were not cleared between forms (resulting in setting
# validation messages in all forms when one form doesn't validate)
# FIXED: numeric and date values are incorrectly validated for empty values when not required
# For a full version history see the eform_history.htm file in the docs directory
#
# Some more fixes and problems:
# FIXED: reg expression failed for select and textarea boxes which have regex special
# characters in their name attribute. eg name="multipleSelection[]"
# FIXED: validation of multiple values with #LIST & #SELECT stopped after 1st value
# Caused by repeating $v variable naming (overwriting $v array)
# e.g.
# <select name="multipleSelection[]" multiple="multiple" eform="::1::"/>
# <option value="1">1</option>
# <option value="2">2</option>
# <option value="3">3</option>
# </select>
# would only have the first selected value validated!
##
$GLOBALS['optionsName'] = "eform"; //name of pseudo attribute used for format settings
function eForm($modx,$params) {
global $_lang;
global $debugText;
global $formats,$fields;
$fields = array(); //reset fields array - needed in case of multiple forms
// define some variables used as array index
$_dfnMaxlength = 6;
extract($params,EXTR_SKIP); // extract params into variables
$fileVersion = '1.4.4';
$version = isset($version)?$version:'prior to 1.4.2';
#include default language file
include_once($snipPath."lang/english.inc.php");
#include other language file if set.
$form_language = isset($language)?$language:$modx->config['manager_language'];
if($form_language!="english" && $form_language!='') {
if(file_exists($snipPath ."lang/".$form_language.".inc.php"))
include_once $snipPath ."lang/".$form_language.".inc.php";
else
if( $isDebug ) $debugText .= "<strong>Language file '$form_language.inc.php' not found!</strong><br />"; //always in english!
}
# add debug warning - moved again...
if( $isDebug ) $debugText .= $_lang['ef_debug_warning'];
//check version differences
if( $version != $fileVersion )
return $_lang['ef_version_error'];
# check for valid form key - moved to below fetching form template to allow id coming from form template
$nomail = $noemail; //adjust variable name confusion
# activate nomail if missing $to
if (!$to) $nomail = 1;
# load templates
if($tpl==$modx->documentIdentifier) return $_lang['ef_is_own_id']."'$tpl'";
//required
if( $tmp=efLoadTemplate($tpl) ) $tpl=$tmp; else return $_lang['ef_no_doc'] . " '$tpl'";
# check for valid form key
if ($formid=="") return $_lang['ef_error_formid'];
//changed in 1.4.3 - re-introduced hidden formid field as this is essential for multiple forms on one page
// try to get formid from <form> tag id
preg_match('/<form[^>]*?id=[\'"]([^\'"]*?)[\'"]/i',$tpl,$matches);
$form_id = isset($matches[1])?$matches[1]:'';
//check for <input type='hidden name='formid'...>
if( !preg_match('/<input[^>]*?name=[\'"]formid[\'"]/i',$tpl) ){
//insert hidden formid field
$tpl = str_replace('</form>',"<input type=\"hidden\" name=\"formid\" value=\"$form_id\" /></form>",$tpl);
}
$validFormId = ($formid==$_POST['formid']);
# check if postback mode
$isPostBack = ($validFormId && count($_POST)>0)? true:false;
//moved so it can be used in onBeforeFormParse event
if($isPostBack){
$report = (($tmp=efLoadTemplate($report))!==false)?$tmp:$_lang['ef_no_doc'] . " '$report'";
if($thankyou) $thankyou = (($tmp=efLoadTemplate($thankyou))!==false )?$tmp:$_lang['ef_no_doc'] . " '$thankyou'";
if($autotext) $autotext = (($tmp=efLoadTemplate($autotext))!==false )?$tmp:$_lang['ef_no_doc'] . " '$autotext'";
}
//added eFormCSS and eFormJS parameters
//these will be added to the HEAD section of the document when the form is displayed!
if($cssStyle){
$cssStyle = ( strpos($cssStyle,',') && !strpos($cssStyle,'<style') ) ? explode(',',$cssStyle) : array($cssStyle);
foreach( $cssStyle as $tmp ) $startupSource[]= array($tmp,'css');
}
if($jScript){
$jScript = ( strpos($jScript,',') && !strpos($jScript,'<script') ) ? explode(',',$jScript) : array($jScript);
foreach( $jScript as $tmp )
$startupSource[]= array($tmp,'javascript');
}
#New in 1.4.4 - run snippet to include 'event' functions
if( strlen($runSnippet)>0 ){
$modx->runSnippet($runSnippet, array('formid'=>$formid));
//Sadly we cannot know if the snippet fails or if it exists as modx->runsnippet's return value
//is ambiguous in this case (see modx->evalSnippet where a boolean false is concatenated with an
//empty string (from output buffering) if a snippet doesn't exist
}
# invoke onBeforeFormParse event set by another script
if ($eFormOnBeforeFormParse) {
if( $isDebug && !function_exists($eFormOnBeforeFormParse))
$fields['debug'] .= "eFormOnBeforeFormParse event: Could not find the function " . $eFormOnBeforeFormParse;
else{
$templates = array('tpl'=>$tpl,'report'=>$report,'thankyou'=>$thankyou,'autotext'=>$autotext);
if( $eFormOnBeforeFormParse($fields,$templates)===false )
return "";
elseif(is_array($templates))
extract($templates); // extract back into original variables
}
}
if ($isPostBack) {
# parse form for formats and generate placeholders
$tpl = eFormParseTemplate($tpl,$isDebug);
foreach($formats as $k => $discard)
if(!isset($fields[$k])) $fields[$k] = ""; // store dummy value inside $fields
//added in 1.4 - add a disclaimer from chunk/page id - fails silently if not found
$disclaimer = (($tmp=efLoadTemplate($disclaimer))!==false )? $tmp:'';
//error message containers
$vMsg = $rMsg = $rClass = array();
# get user post back data
foreach($_POST as $name => $value){
if(is_array($value)){
//remove empty values
$value = array_flip($value);
unset($value['']);
$fields[$name] = array_flip($value);
} else
$fields[$name] = stripslashes(($allowhtml || $formats[$name][2]=='html')? $value:$modx->stripTags($value));
}
# get uploaded files
foreach($_FILES as $name => $value){
$fields[$name] = $value;
}
# check vericode
if($vericode) {
//add support for captcha code - thanks to Djamoer
$code = $_SESSION['veriword'] ? $_SESSION['veriword'] : $_SESSION['eForm.VeriCode'];
if($fields['vericode']!=$code) {
$vMsg[count($vMsg)]=$_lang['ef_failed_vericode'];
$rClass['vericode']=$invalidClass; //added in 1.4.4
}
}
# validate fields
//Added in 1.4.2 - sets $rClass array when invalid - this array is expanded into class atributes lateron
foreach($fields as $name => $value) {
$fld = $formats[$name];
if ($fld) {
$desc = $fld[1];
$datatype = $fld[2];
$isRequired = $fld[3];
// mod by JJ - Separated required test field from other validation
if ($isRequired==1 && $value=="" && $datatype!="file"){
$rMsg[count($rMsg)]="$desc";
$rClass[$name]=$requiredClass;
//mod by JJ - extended field validation - see validation functions elsewhere
}elseif( isset($fld[5]) && $value!="" && $datatype!="file" ) {
$value = validateField($value,$fld,$vMsg,$isDebug);
if($value===false) $rClass[$name]=$invalidClass;
//if returned value is not of type boolean replace value...
elseif($value!==true) $fields[$name]=$value; //replace value.
//end mod
}else{ //value check
switch ($datatype){
case "integer":
case "float":
if (strlen($value)>0 && !is_numeric($value)){
$vMsg[count($vMsg)]=$desc . $_lang["ef_invalid_number"];
$rClass[$name]=$invalidClass;
}
break;
case "date":
if(strlen($value)==0) break;
//corrected by xwisdom for php version differences
$rt = strtotime($value); //php 5.1.0+ returns false while < 5.1.0 returns -1
if ($rt===false||$rt===-1){
$vMsg[count($vMsg)]=$desc . $_lang["ef_invalid_date"];
$rClass[$name]=$invalidClass;
}
break;
case "email":
//stricter email validation - udated to allow + in local name part
if (strlen($value)>0 && !preg_match(
'/^(?:[a-z0-9+_-]+?\.)*?[a-z0-9_+-]+?@(?:[a-z0-9_-]+?\.)*?[a-z0-9_-]+?\.[a-z0-9]{2,5}$/i', $value) ){
$vMsg[count($vMsg)] = isset($formats[$name][4]) ? $formats[$name][4] : $desc . $_lang["ef_invalid_email"];
$rClass[$name]=$invalidClass;
}
break;
case "file":
if ($_FILES[$name]['error']==1 || $_FILES[$name]['error']==2){
$vMsg[count($vMsg)]=$desc . $_lang['ef_upload_exceeded'];
$rClass[$name]=$invalidClass;
}elseif ($isRequired==1 && ($_FILES[$name] && $_FILES[$name]['type']=='')){
$rMsg[count($rMsg)]=$desc;
$rClass[$name]=$requiredClass;
}elseif ($_FILES[$name]['tmp_name']){
if( substr($fld[5],0,5)!="#LIST" || validateField($_FILES[$name]['name'],$fld,$vMsg,$isDebug) )
$attachments[count($attachments)] = $_FILES[$name]['tmp_name'];
else $rClass[$name]=$invalidClass;
}
break;
case "html":
case "checkbox":
case "string":
default:
break;
}
}//end required test
}
}
// Added in 1.4.1 - eformOnValidate event - expects 3 parameters
//remember to receive all parameters referenced i.e. function myFunction(&$fields,&$vMsg,&$rMsg);
if ($eFormOnValidate) {
if( $isDebug && !function_exists($eFormOnValidate))
$fields['debug'] .= "eformOnValidate event: Could not find the function " . $eFormOnValidate;
else
if ($eFormOnValidate($fields,$vMsg,$rMsg)===false) return;
}
// MOD for 1.4.1 - Changed order for error testing so we can use the event
// for extra validation e.g. validate for a combination of fields.
if(count($vMsg)>0 || count($rMsg)>0){
//New in 1.4.2 - classes are set in labels and form elements for invalid fields
foreach($rClass as $n => $class){
$fields[$n.'_class'] = $fields[$n.'_class']?$fields[$n.'_class'].' '. $class:$class;
$fields[$n.'_vClass'] = $fields[$n.'_vClass']?$fields[$n.'_vClass'].' '. $class:$class;
}
//add debugging info to fields array
if($isDebug){
ksort($fields);
if($isDebug>1){
$debugText .= "<br /><strong>Formats array:</strong><pre>". var_export($formats,true).'</pre>';
$debugText .= "<br /><strong>Fields array:</strong><pre>". var_export($fields,true).'</pre>';
$debugText .= "<br /><strong>Classes parsed :</strong><pre>" . var_export($rClass,true) ."</pre>";
}
$debugText .= "<br /><strong>eForm configuration:</strong><pre>\n". var_export($params,true).'</pre>';
$fields['debug']=$debugText;
}
// New in 1.4.2 - wrapper elements now should be placed in the $_lang['ef_validation_message']
// and only a bare [+validationmessage+] needs to be placed in the template/document
#set validation message
$tmp = (count($rMsg)>0)?str_replace("{fields}", implode(", ",$rMsg),$_lang['ef_required_message']):"";
$tmp .= implode("<br />",$vMsg);
if(!strstr($tpl,'[+validationmessage+]'))
$modx->setPlaceholder('validationmessage',str_replace('[+ef_wrapper+]', $tmp, $_lang['ef_validation_message']));
else
$fields['validationmessage'] .= str_replace('[+ef_wrapper+]', $tmp, $_lang['ef_validation_message']);
} else {
# format report fields
foreach($fields as $name => $value) {
$fld = $formats[$name];
if ($fld) {
$datatype = $fld[2];
switch ($datatype) {
case "integer":
$value = number_format($value);
break;
case "float":
$localeInfo = localeconv();
$th_sep = empty($_lang['ef_thousands_separator'])?$localeInfo['thousands_sep']:$_lang['ef_thousands_separator'];
$dec_point= $localeInfo['decimal_point'];
$debugText .= 'Locale<pre>'.var_export($localeInfo,true).'</pre>';
$value = number_format($value, 2, $dec_point, $th_sep);
//$format_string = isset($_lang['ef_float_format']) ? $_lang['ef_float_format'] : "%f";
//$value = sprintf($format_string,$value);
break;
case "date":
$format_string = isset($_lang['ef_date_format']) ? $_lang['ef_date_format'] : '%d-%b-%Y %H:%M:%S';
$value = ($value)? strftime($format_string,strtotime($value)):"";
$value=str_replace("00:00:00","",$value);// remove trailing zero time values
break;
case "html":
// convert \n to <br>
if(!$sendAsText ) $value = preg_replace('#(\n<br[ /]*?>|<br[ /]*?>\n|\n)#i','<br />',$value);
break;
case "file":
// set file name
if($value['type']!="" && $value['type']!=""){
$value = $value["name"];
$patharray = explode(((strpos($value,"/")===false)? "\\":"/"), $value);
$value = $patharray[count($patharray)-1];
}
else {
$value = "";
}
break;
}
$fields[$name] = $value;
}
}
# set postdate
$fields['postdate'] = strftime("%d-%b-%Y %H:%M:%S",time());
//check against email injection and replace suspect content
if( hasMailHeaders($fields) ){
//send email to webmaster??
if ($reportAbuse){ //set in snippet configuration tab
$body = $_lang['ef_mail_abuse_message'];
$body .="<table>";
foreach($fields as $key => $value)
$body .= "<tr><td>$key</td><td><pre>$value</pre></td></tr>";
$body .="</table>";
include_once "manager/includes/controls/class.phpmailer.php";
# send abuse alert
$mail = new PHPMailer();
$mail->IsMail();
$mail->IsHTML(true);
$mail->From = $modx->config['emailsender'];
$mail->FromName = $modx->config['site_name'];
$mail->Subject = $_lang['ef_mail_abuse_subject'];
$mail->Body = $body;
AddAddressToMailer($mail,"to",$modx->config['emailsender']);
$mail->send(); //ignore mail errors in this case
}
//return empty form with error message
//register css and/or javascript
if( isset($startupSource) ) efRegisterStartupBlock($startupSource);
return formMerge($tpl,array('validationmessage'=> $_lang['ef_mail_abuse_error']));
}
# added in 1.4.2 - Limit the time between form submissions
if($submitLimit>0){
if( time()<$submitLimit+$_SESSION[$formid.'_limit'] ){
return formMerge($_lang['ef_submit_time_limit'],$fields);
}
else unset($_SESSION[$formid.'_limit'], $_SESSION[$formid.'_hash']); //time expired
}
/*
* mod - by JJ removed reference from function call as it's deprecated in current PHP
* Remember to treat parameter as a reference in function
*/
//added in 1.4.2 - added suggestion by Raymond - return form if $fields['validationmessage'] is set by event
# invoke OnBeforeMailSent event set by another script
if ($eFormOnBeforeMailSent) {
if( $isDebug && !function_exists($eFormOnBeforeMailSent))
$fields['debug'] .= "eFormOnBeforeMailSent event: Could not find the function " . $eFormOnBeforeMailSent;
elseif ($eFormOnBeforeMailSent($fields)===false) {
if( isset($fields['validationmessage']) && !empty($fields['validationmessage']) ){
//register css and/or javascript
if( isset($startupSource) ) efRegisterStartupBlock($startupSource);
return formMerge($tpl,$fields);
}
else
return;
}
}
# added in 1.4.2 - protect against multiple submits
if( $protectSubmit ){
$hash = '';
# create a hash of key data
if(!is_numeric($protectSubmit)){ //supplied field names
$protectSubmit = (strpos($protectSubmit,','))? explode(',',$protectSubmit):array($protectSubmit);
foreach($formats as $fld) $hash .= isset($fields[$fld]) ? $fields[$fld] : '';
}else //all required fields
foreach($formats as $fld) $hash .= ($fld[3]==1) ? $fields[$fld[0]] : '';
if($hash) $hash = md5($hash);
if( $isDebug ) $debugText .= "<strong>SESSION HASH</strong>:".$_SESSION[$formid.'_hash']."<br />"."<b>FORM HASH</b>:".$hash."<br />";
# check if already succesfully submitted with same data
if( isset($_SESSION[$formid.'_hash']) && $_SESSION[$formid.'_hash'] == $hash && $hash!='' )
return formMerge($_lang['ef_multiple_submit'],$fields);
}
$fields['disclaimer'] = ($disclaimer)? formMerge($disclaimer,$fields):"";
$subject = isset($fields['subject'])?$fields['subject']:(($subject)? formMerge($subject,$fields):$category);
$fields['subject'] = $subject; //make subject available in report & thank you page
$report = ($report)? formMerge($report,$fields):"";
$keywords = ($keywords)? formMerge($keywords,$fields):"";
$from = ($from)? formMerge($from,$fields):"";
$fromname = ($from)? formMerge($fromname,$fields):"";
$to = formMerge($to,$fields);
if(empty($to) || !strpos($to,'@')) $nomail=1;
//return '<pre>'. var_export($params,true).'</pre>';
if(!$nomail){
# check for mail selector field - select an email from to list
if ($mselector && $fields[$mselector]) {
$i = (int)$fields[$mselector];
$ar = explode(",",$to);
if ($i>0) $i--;
if ($ar[$i]) $to = $ar[$i];
else $to = $ar[0];
}
//set reply-to address
//$replyto snippet parameter must contain email or fieldname
if(!strstr($replyto,'@'))
$replyto = ( $fields[$replyto] && strstr($fields[$replyto],'@') )?$fields[$replyto]:$from;
# include PHP Mailer
include_once "manager/includes/controls/class.phpmailer.php";
# send form
//defaults to html so only test sendasText
$isHtml = ($sendAsText==1 || strstr($sendAsText,'report'))?false:true;
if(!$noemail) {
if($sendirect) $to = $fields['email'];
$mail = new PHPMailer();
$mail->IsMail();
$mail->CharSet = $modx->config['modx_charset'];
$mail->IsHTML($isHtml);
$mail->From = $from;
$mail->FromName = $fromname;
$mail->Subject = $subject;
$mail->Body = $report;
AddAddressToMailer($mail,"replyto",$replyto);
AddAddressToMailer($mail,"to",$to);
AddAddressToMailer($mail,"cc",$cc);
AddAddressToMailer($mail,"bcc",$bcc);
AttachFilesToMailer($mail,$attachments);
if(!$mail->send()) return 'Main mail: ' . $_lang['ef_mail_error'] . $mail->ErrorInfo;
}
# send user a copy of the report
if($ccsender && $fields['email']) {
$mail = new PHPMailer();
$mail->IsMail();
$mail->CharSet = $modx->config['modx_charset'];
$mail->IsHTML($isHtml);
$mail->From = $from;
$mail->FromName = $fromname;
$mail->Subject = $subject;
$mail->Body = $report;
AddAddressToMailer($mail,"to",$fields['email']);
AttachFilesToMailer($mail,$attachments);
if(!$mail->send()) return 'CCSender: ' . $_lang['ef_mail_error'] . $mail->ErrorInfo;
}
# send auto-respond email
//defaults to html so only test sendasText
$isHtml = ($sendAsText==1 || strstr($sendAsText,'autotext'))?false:true;
if ($autotext && $fields['email']!='') {
$autotext = formMerge($autotext,$fields);
$mail = new PHPMailer();
$mail->IsMail();
$mail->CharSet = $modx->config['modx_charset'];
$mail->IsHTML(true);
$mail->From = ($autosender)? $autosender:$from;
$mail->FromName = ($autoSenderName)?$autoSenderName:$fromname;
$mail->Subject = $subject;
$mail->Body = $autotext;
AddAddressToMailer($mail,"to",$fields['email']);
if(!$mail->send()) return 'AutoText: ' . $_lang['ef_mail_error'] . $mail->ErrorInfo;
}
//defaults to text - test for sendAsHtml
$isHTML = ($sendAsHTML==1 || strstr($sendAsHtml,'mobile'))?true:false;
# send mobile email
if ($mobile && $mobiletext) {
$mobiletext = formMerge($mobiletext,$fields);
$mail = new PHPMailer();
$mail->IsMail();
$mail->CharSet = $modx->config['modx_charset'];
$mail->IsHTML($isHtml);
$mail->From = $from;
$mail->FromName = $fromname;
$mail->Subject = $subject;
$mail->Body = $mobiletext;
AddAddressToMailer($mail,"to",$mobile);
$mail->send();
}
}//end test nomail
# added in 1.4.2 - Protection against multiple submit with same form data
if($protectSubmit) $_SESSION[$formid.'_hash'] = $hash; //hash is set earlier
# added in 1.4.2 - Limit the time between form submissions
if($submitLimit>0) $_SESSION[$formid.'_limit'] = time();
/*
* mod - by JJ removed reference from function call as it's deprecated in current PHP
* Remember to treat parameter as a reference in function!!
*/
# invoke OnMailSent event set by another script
if ($eFormOnMailSent) {
if( $isDebug && !function_exists($eFormOnMailSent) )
$fields['debug'] .= "eFormOnMailSent event: Could not find the function" . $eFormOnMailSent;
else
if ($eFormOnMailSent($fields)===false) return;
}
if($isDebug){
$debugText .="<strong>Mail Headers:</strong><br>From: $from ($fromname)<br/>Reply-to:$replyto<br />To: $to<br/>Subject: $subject<br />CC: $cc<br /> BCC:$bcc<br />";
if($isDebug>1){
$debugText .= "<br /><strong>Formats array:</strong><pre>". var_export($formats,true).'</pre>';
$debugText .= "<br /><strong>Fields array:</strong><pre>". var_export($fields,true).'</pre>';
}
$fields['debug'] = $debugText;
}
# show or redirect to thank you page
if ($gid==$modx->documentIdentifier){
//mod by JJ - added thank you chunk output
if(!empty($thankyou) ){
if($isDebug && !strstr($thankyou,'[+debug+]')) $thankyou .= '[+debug+]';
if( isset($startupSource) ) efRegisterStartupBlock($startupSource,true); //skip scripts
if( $sendAsText ){
foreach($formats as $key => $fmt)
if($fmt[2]=='html') $fields[$key] = str_replace("\n",'<br />',$fields[$key]);
}
$thankyou = formMerge($thankyou,$fields);
return $thankyou;
}else{
return $_lang['ef_thankyou_message'];
}
}
else {
$url = $modx->makeURL($gid);
$modx->sendRedirect($url);
}
return; // stop here
}
}else{ //not postback
//add debugging info to fields array
if($isDebug){
$debugText .= "<br /><strong>eForm configuration:</strong><pre>". var_export($params,true).'</pre>';
$fields['debug']=$debugText;
}
//strip the eform attribute
$regExpr = "#eform=([\"'])[^\\1]*?\\1#si";
$tpl = preg_replace($regExpr,'',$tpl);
}
// set vericode
if($vericode) {
$_SESSION['eForm.VeriCode'] = $fields['vericode'] = substr(uniqid(''),-5);
$fields['verimageurl'] = $modx->config['base_url'].'manager/includes/veriword.php?rand='.rand();
}
# get SESSION data - thanks to sottwell
#should this be replaced by event?
if($sessionVars){
$sessionVars = (strpos($sessionVars,',',0))?explode(',',$sessionVars):array($sessionVars);
foreach( $sessionVars as $varName ){
if( empty($varName) ) continue;
$varName = trim($varName);
if( isset($_SESSION[$varName]) && !empty($_SESSION[$varName]) )
$fields[$varName] = ( isset($fields[$varName]) && $postOverides )?$fields[$varName]:$_SESSION[$varName];
}
}
#Added in 1.4.1
# invoke OnBeforeFormMerge event set by another script
# Remember to treat $fields parameter as a reference in function
if ($eFormOnBeforeFormMerge) {
if( $isDebug && !function_exists($eFormOnBeforeFormMerge))
$fields['debug'] .= "eFormOnBeforeFormMerge event: Could not find the function " . $eFormOnBeforeFormMerge;
else
if ($eFormOnBeforeFormMerge($fields)===false) return;
}
# build form
if($isDebug && !$fields['debug']) $fields['debug'] = $debugText;
if($isDebug && !strstr($tpl,'[+debug+]')) $tpl .= '[+debug+]';
//register css and/or javascript
if( isset($startupSource) ) efRegisterStartupBlock($startupSource);
return formMerge($tpl,$fields);
}
# Form Merge
function formMerge($docText, $docFields, $vClasses='') {
global $formats;
$lastitems;
if(!$docText) return '';
preg_match_all('~\[\+(.*?)\+\]~', $docText, $matches);
for($i=0;$i<count($matches[1]);$i++) {
$name = $matches[1][$i];
//mod - splitting name:value to get proper docFields key
list($listName,$listValue) = explode(":",$name);
$value = isset($docFields[$listName])? $docFields[$listName]:"";
//end mod
// support for multi checkbox, radio and select - Djamoer
if(is_array($value)) $value=implode(', ', $value);
$fld = $formats[$name];
if (!isset($fld)){
// listbox, checkbox, radio select
$colonPost = strpos($name, ':');
$listName = substr($name, 0, $colonPost);
$listValue = substr($name, $colonPost+1);
$datatype = $formats[$listName][2];
if(is_array($docFields[$listName])) {
if($datatype=="listbox" && in_array($listValue, $docFields[$listName])) $docText = str_replace("[+$listName:$listValue+]","selected='selected'",$docText);
if(($datatype=="checkbox"||$datatype=="radio") && in_array($listValue, $docFields[$listName])) $docText = str_replace("[+$listName:$listValue+]","checked='checked'",$docText);
}
else {
if($datatype=="listbox" && $listValue==$docFields[$listName]) $docText = str_replace("[+$listName:$listValue+]","selected='selected'",$docText);
if(($datatype=="checkbox"||$datatype=="radio") && $listValue==$docFields[$listName]) $docText = str_replace("[+$listName:$listValue+]","checked='checked'",$docText);
}
}
if(strpos($name,":")===false) $docText = str_replace("[+$name+]",$value,$docText);
else {
// this might be a listbox item.
// we'll remove this field later
$lastitems[count($lastitems)]="[+$name+]";
}
}
$lastitems[count($lastitems)] = "class=\"\""; //removal off empty class attributes
$docText = str_replace($lastitems,"",$docText);
return $docText;
}
# Adds Addresses to Mailer
function AddAddressToMailer(&$mail,$type,$addr){
$a = explode(",",$addr);
for($i=0;$i<count($a);$i++){
if(!empty($a[$i])) {
if ($type=="to") $mail->AddAddress($a[$i]);
elseif ($type=="cc") $mail->AddCC($a[$i]);
elseif ($type=="bcc") $mail->AddBCC($a[$i]);
elseif ($type=="replyto") $mail->AddReplyTo($a[$i]);
}
}
}
# Attach Files to Mailer
function AttachFilesToMailer(&$mail,&$attachFiles) {
if(count($attachFiles)>0){
foreach($attachFiles as $attachFile){
if(!file_exists($attachFile)) continue;
$FileName = $attachFile;
$contentType = "application/octetstream";
if (is_uploaded_file($attachFile)){
foreach($_FILES as $n => $v){
if($_FILES[$n]['tmp_name']==$attachFile) {
$FileName = $_FILES[$n]['name'];
$contentType = $_FILES[$n]['type'];
}
}
}
$patharray = explode(((strpos($FileName,"/")===false)? "\\":"/"), $FileName);
$FileName = $patharray[count($patharray)-1];
$mail->AddAttachment($attachFile,$FileName,"base64",$contentType);
}
}
}
/*--- Form Parser stuff----------------------*/
function eFormParseTemplate($tpl, $isDebug=false ){
global $formats,$optionsName,$_lang,$debugText,$fields;
$formats =""; //clear formats so values don't persist through multiple snippet calls
$labels = "";
# check if postback mode
$isPostBack = (count($_POST)>0)? 1:0;
//Replaced in 1.4.2 - get class values as well
$regExpr = "#(<label[^>]*?>)(.*?)</label>#si";;
preg_match_all($regExpr,$tpl,$matches);
foreach($matches[1] as $key => $fld){
$attr = attr2array($fld);
if(isset($attr['for'])){
$name = substr($attr['for'],1,-1);
//add class to fields array
$fields[$name."_vClass"] = isset($attr['class'])?substr($attr['class'],1,-1):'';
$labels[$name] = strip_tags($matches[2][$key]);
//create placeholder for class
$attr['class'] = '"[+'.$name.'_vClass+]"';
$newTag = buildTagPlaceholder('label',$attr,$name);
$tpl = str_replace($fld,$newTag,$tpl);
}
}
//retrieve all the form fields
$regExpr = "#(<(input|select|textarea)[^>]*?>)#si";
preg_match_all($regExpr,$tpl,$matches);
$fieldTypes = $matches[2];
$fieldTags = $matches[1];
for($i=0;$i<count($fieldTypes);$i++){
$type = $fieldTypes[$i];
//get array of html attributes
$tagAttributes = attr2array($fieldTags[$i]);
//attribute values are stored including quotes
//strip quotes as well as any brackets to get the raw name
$name = str_replace(array("'",'"','[',']'),'',$tagAttributes['name']);
#skip vericode field - updated in 1.4.4
#special case. We need to set the class placeholder but forget about the rest
if($name=="vericode"){
if(isset($tagAttributes['class'])){
$fields[$name.'_class'] = substr($tagAttributes['class'],1,-1);
}
$tagAttributes['class'] = '"[+'.$name.'_class+]"';
$tagAttributes['value'] = '';
$newTag = buildTagPlaceholder('input',$tagAttributes,$name);
$tpl = str_replace($fieldTags[$i],$newTag,$tpl);
continue;
}
//store the field options
if (isset($tagAttributes[$optionsName])){
//split to max of 5 so validation rule can contain ':'
$formats[$name] = explode(":",stripTagQuotes($tagAttributes[$optionsName]),5) ;
array_unshift($formats[$name],$name);
}else{
if(!isset($formats[$name])) $formats[$name]=array($name,'','',0);
}
//added for 1.4 - use label if it is defined
if(empty($formats[$name][1]))
$formats[$name][1]=(isset($labels[$name])) ? $labels[$name] : $name;
unset($tagAttributes[$optionsName]);
//added in 1.4.2 - add placeholder to class attribute
if(isset($tagAttributes['class'])){
$fields[$name.'_class'] = substr($tagAttributes['class'],1,-1);
}
$tagAttributes['class'] = '"[+'.$name.'_class+]"';
switch($type){
case "select":
//replace with 'cleaned' tag and added placeholder
$newTag = buildTagPlaceholder('select',$tagAttributes,$name);
$tpl = str_replace($fieldTags[$i],$newTag,$tpl);
if($formats[$name]) $formats[$name][2]='listbox';
//Get the whole select block with option tags
//escape any regex characters!
$regExp = "#<select [^><]*?name=".preg_quote($tagAttributes['name'],'#')."[^>]*?".">(.*?)</select>#si";
preg_match($regExp,$tpl,$matches);
$optionTags = $matches[1];
$select = $newSelect = $matches[0];
//get separate option tags and split them up
preg_match_all("#(<option [^>]*?>)#si",$optionTags,$matches);
$validValues = array();
foreach($matches[1] as $option){
$attr = attr2array($option);
$value = substr($attr['value'],1,-1); //strip outer quotes
if( trim($value)!='' ) $validValues[] = $value;
$newTag = buildTagPlaceholder('option',$attr,$name);
$newSelect = str_replace($option,$newTag,$newSelect);
}
//replace complete select block
$tpl = str_replace($select,$newSelect,$tpl);
//add valid values to formats... (extension to $formats)
if($formats[$name] && !$formats[$name][5])
$formats[$name][4] = $_lang['ef_failed_default'];
//convert commas in values to something else !
$formats[$name][5]= "#LIST " . implode(",",str_replace(',',',',$validValues));
break;
case "textarea":
// add support for maxlength attribute for textarea
// attribute get's stripped form form //
if( $tagAttributes['maxlength'] ){
$formats[$name][$_dfnMaxlength] == $tagAttributes['maxlength'];
unset($tagAttributes['maxlength']);
}
$newTag = buildTagPlaceholder($type,$tagAttributes,$name);
$regExp = "#<textarea [^>]*?name=" . $tagAttributes["name"] . "[^>]*?" . ">(.*?)</textarea>#si";
preg_match($regExp,$tpl,$matches);
//if nothing Posted retain the content between start/end tags
$placeholderValue = ($isPostBack)?"[+$name+]":$matches[1];
$tpl = str_replace($matches[0],$newTag.$placeholderValue."</textarea>",$tpl);
break;
default: //all the rest, ie. "input"
$newTag = buildTagPlaceholder($type,$tagAttributes,$name);
$fieldType = stripTagQuotes($tagAttributes['type']);
//validate on maxlength...
if( $fieldType=='text' && $tagAttributes['maxlength'] ){
$formats[$name][$_dfnMaxlength] == $tagAttributes['maxlength'];
}
$debugText .= $name.'<pre>'.var_export($formats[$name],true).'</pre>';
if($formats[$name] && !$formats[$name][2]) $formats[$name][2]=($fieldType=='text')?"string":$fieldType;
//populate automatic validation values for hidden, checbox and radio fields
if($fieldType=='hidden'){
if(!$isDebug) $formats[$name][1] = "[undefined]"; //do not want to disclose hidden field names
if(!isset($formats[$name][4])) $formats[$name][4]= $_lang['ef_tamper_attempt'];
if(!isset($formats[$name][5])) $formats[$name][5]= "#VALUE ". stripTagQuotes($tagAttributes['value']);
}elseif($fieldType=='checkbox' || $fieldType=='radio'){
$formats[$name][4]= $_lang['ef_failed_default'];
$formats[$name][5] .= isset($formats[$name][5])?",":"#LIST ";
//convert embedded comma's in values!
$formats[$name][5] .= str_replace(',',',',stripTagQuotes($tagAttributes['value']));
}
/* //not tested yet!! do all browsers include mime-type when uploading files?
elseif($fieldType=="file"){
if( isset($tagAttributes["accept"]) )
$formats[$name][5] = isset($formats[$name][5])?",":"#LIST ";
$formats[$name][5] .= stripTagQuotes($tagAttributes['accept']);
$formats[$name][4]= $_lang['ef_failed_filetype'];
}
* //*/
$tpl = str_replace($fieldTags[$i],$newTag,$tpl);
break;
}
}
if($isDebug>2) $debugText .= "<strong>Parsed template</strong><p style=\"border:1px solid black;padding:2px;\">" . str_replace("\n",'<br />',str_replace('+','+',htmlspecialchars($tpl)))."</p><hr>";
return $tpl;
}
function stripTagQuotes($value){
return substr($value,1,-1);
}
function buildTagPlaceholder($tag,$attributes,$name){
$type = stripTagQuotes($attributes["type"]);
$quotedValue = $attributes['value'];
$val = stripTagQuotes($quotedValue);
foreach ($attributes as $k => $v)
$t .= ($k!='value' && $k!='checked' && $k!='selected')?" $k=$v":"";
switch($tag){
case "select":
return "<$tag$t>"; //only the start tag mind you
case "option":
return "<$tag$t value=".$quotedValue." [+$name:$val+]>";
case "input":
switch($type){
case 'radio':
case 'checkbox':
return "<input$t value=".$quotedValue." [+$name:$val+] />";
case 'text':
if($name=='vericode') return "<input$t value=\"\" />";
//else pass on to next
case 'password':
return "<input$t value=\"[+$name+]\" />";
default: //leave as is - no placeholder
return "<input$t value=".$quotedValue." />";
}
case "file": //no placeholder!
case "textarea": //placeholder needs to be added in calling code
return "<$tag$t>";
case "label":
return "<$tag$t>";
default:
return "<input$t value=\"[+$name+]\" />";
} // switch
return ""; //if we've arrived here we're in trouble
}
function attr2array($tag){
$expr = "#([a-z0-9_]*?)=(([\"'])[^\\3]*?\\3)#si";
preg_match_all($expr,$tag,$matches);
foreach($matches[1] as $i => $key)
$rt[$key]= $matches[2][$i];
return $rt;
}
function validateField($value,$fld,&$vMsg,$isDebug=false){
global $modx,$_lang, $debugText,$fields;
$output = true;
$desc = $fld[1];
$fldMsg = trim($fld[4]);
if(empty($fld[5])) return $output; //if no rule value validates
list($cmd,$param) = explode(" ",trim($fld[5]),2);
$cmd = strtoupper(trim($cmd));
if (substr($cmd,0,1)!='#'){
$vMsg[count($vMsg)] = "$desc »" . $_lang['ef_error_validation_rule'];
return false;
}
$valList = (is_array($value))?$value:array($value);
//init vars
$errMsg='';
unset($vlist);
for($i=0;$i<count($valList);$i++){
$value = $valList[$i]; //re-using $value, destroying original!!
switch ($cmd) {
//really only used internally for hidden fields
case "#VALUE":
if($value!=$param) $errMsg = $_lang['ef_failed_default'];
break;
case "#RANGE":
if(!isset($vlist)) $vlist = explode(',',strtolower(trim($param))); //cached
//the crude way first - will have to refine this
foreach($vlist as $p){
if( strpos($p,'~')>0)
$range = explode('~',$p);
else
$range = array($p,$p); //yes,.. I know - cheating :)
if($isDebug && (!is_numeric($range[0]) || !is_numeric($range[1])) )
$modx->messageQuit('Error in validating form field!', '',$false,E_USER_WARNING,__FILE__,'','#RANGE rule contains non-numeric values: '.$fld[5],__LINE__);
sort($range);
if( $value>=$range[0] && $value<=$range[1] ) break 2; //valid
}
$errMsg = $_lang['ef_failed_range'];
break;
case "#LIST": // List of comma separated values (not case sensitive)
//added in 1.4 - file upload filetype test
if($fld[2]=='file')$value = substr($value,strrpos($value,'.')+1); //file extension
if(!isset($vlist)){
$vlist = explode(',',strtolower(trim($param))); //cached
foreach($vlist as $k =>$v ) $vlist[$k]=str_replace(',',',',$v);
} //changes to make sure embedded commma's in values are recognized
if( $isDebug && count($vlist)==1 && empty($vlist[0]) ){
//if debugging bail out big time
$modx->messageQuit('Error in validating form field!', '',$false,E_USER_WARNING,__FILE__,'','#LIST rule declared but no list values supplied: '.$fld[5],__LINE__);
}elseif(!in_array(strtolower($value),$vlist))
$errMsg = ($fld[2]=='file')? $_lang["ef_failed_upload"]: $_lang['ef_failed_list'];
break;
case "#SELECT": //validates against a list of values from the cms database
#cache all this
if( !isset($vlist) ) {
$rt = array();
$param = str_replace('{DBASE}',$modx->db->config['dbase'],$param);
$param = str_replace('{PREFIX}',$modx->db->config['table_prefix'],$param);
//added in 1.4
if( preg_match("/{([^}]*?)\}/",$param,$matches) ){
$tmp = $modx->db->escape(formMerge('[+'.$matches[1].'+]',$fields));
$param = str_replace('{'.$matches[1].'}',$tmp,$param);
}
$rs = $modx->db->query("SELECT $param;");
//select value from 1st field in records only (not case sensitive)
while( $v = $modx->db->getValue($rs) ) $vlist[]=strtolower($v);
}
if(!is_array($vlist)){
//WARNING! if not debugging (and query fails) error is ignored, and value will validate!!
//version 1.4 - replaced fatal error with friendly debug message when debugging
$debugText .= ($isDebug)? "'<strong>$fld[1]</strong>' ".$_lang['ef_sql_no_result'].'<br />':'';
}elseif(!in_array(strtolower($value),$vlist)){
$errMsg = $_lang['ef_failed_list'];
}
break;
case "#EVAL": // php code should return true or false
$tmp = $cmd; //just in case eval destroys cmd
if( eval($param)===false )
$errMsg = $_lang['ef_failed_eval'];
if($isDebug) $debugText .= "<strong>$fld[1]</strong>: ".$_lang['ef_eval_deprecated']." $param";
$cmd = $tmp;
break;
//added in 1.4
case "#FUNCTION":
$tmp = $cmd; //just in case function destroys cmd
if( function_exists($param) )
if( !@$param($value) ) $errMsg = $_lang['ef_failed_eval'];
else
if($isDebug) $debugText .= "<strong>$fld[1]</strong>: ".$_lang['ef_no_function']." $param";
$cmd = $tmp;
break;
case "#REGEX":
if( !$tmp=preg_match($param,$value) )
$errMsg = $_lang['ef_failed_ereg'];
if($isDebug && $tmp===false ) $debugText .= "<strong>$fld[1]</strong>: ".$_lang['ef_regex_error']." $param";
break;
case "#FILTER":
$valList[$i] = filterEformValue($value,$param);
break;
default:
$errMsg = $_lang['ef_error_validation_rule'];
}//end switch
if($isDebug) {
$debugText .="'<strong>$fld[1]</strong>' ";
$debugText .= (empty($errMsg))?'passed':'<span style="color:red;">Failed</span>';
$debugText .= " using rule: $cmd ".$param.', (input='.htmlspecialchars($valList[$i]).")<br />\n";
}
if(!empty($errMsg)){
$errMsg = ($fldMsg)?"$desc » $fldMsg":"$desc » $errMsg";
$vMsg[count($vMsg)] = $errMsg;
$output=false;
break; //quit testing more values
}
}//end for
//make sure we have correct return value in case of #filter
$valList = (!is_array($value))?implode('',$valList):$valList;
return ($cmd=="#FILTER")?$valList:$output;
}//end validateField
function filterEformValue($value,$param){
list($cmd,$param) = explode(" ",trim($param),2);
$cmd = trim($cmd);
switch(strtoupper($cmd)){
case "#REGEX":
list($src,$dst) = explode("||",$param,2);
$value = ( $v = preg_replace($src,$dst,$value) )?$v:$value;
break;
case "#LIST":
$param = explode("||",$param,2);
$src = strpos($param[0],',')?explode(',',$param[0]):$param[0];
$dst = strpos($param[1],',')?explode(',',$param[1]):$param[1];
$value = str_replace($src,$dst,$value);
break;
case "#EVAL":
$value = ($v = @eval($param))?$v:$value;
break;
}
return $value;
}
function efLoadTemplate($key){
global $modx;
if( strlen($key)>50 ) return $key;
$tpl = false;
if( is_numeric($key) ) { //get from document id
//try unpublished docs first
$tpl = ( $doc=$modx->getDocument($key,'content',0) )? $doc['content'] :false;
if(!$tpl )
$tpl = ( $doc=$modx->getDocument($key,'content',1) )? $doc['content'] : false;
}elseif( $key ){
$tpl = ( $doc=$modx->getChunk($key) )? $doc : false;
//try snippet if chunk is not found
if(!$tpl) $tpl = ( $doc=$modx->runSnippet($key) )? $doc : false;
}
return $tpl;
}
# registers css and/or javascript to modx class
function efRegisterStartupBlock($src_array,$noScript=false){
global $modx;
if(!array($src_array)) return;
foreach($src_array as $item){
list($src,$type) = $item;
//skip scripts if told to do so
if($type=='javascript' && $noScript) continue;
//try to load from document or chunk
if( $tmp = efLoadtemplate($src) )
$src = $tmp;
$src=trim($src);
if ( $type=='css' )
$modx->regClientCSS($src);
elseif ( $type == 'javascript' )
$modx->regClientStartupScript($src);
else
$modx->regClientStartupHTMLBlock($src);
}//end foreach
}
/**
* adapted from http://php.mirrors.ilisys.com.au/manual/en/ref.mail.php
* If any field contains newline followed by email-header specific string it is replaced by a harmless substitute
* (we hope) If any subsitutiosn are made the function returns true
* Currently tests for
* Content-Transfer-Encoding:
* MIME-Version:
* content-type:
* to:
* cc:
* bcc:
*/
function hasMailHeaders( &$fields ){
$injectionAttempt = false;
$re = "/(%0A|%0D|\n+|\r+)(Content-Transfer-Encoding:|MIME-Version:|content-type:|to:|cc:|bcc:)/i";
foreach($fields as $fld => $text){
if( ($match = preg_replace($re,'\\[\2]\\', $text))!=$text ){
$injectionAttempt = true;
$fields[$fld]=$match; //replace with 'disabled' version
}
}
return ($injectionAttempt)?true:false;
}
?>