2 /************************************************************************
3 * Mailer v0.2.1-FINAL Start: 04/12/2009 *
4 * =================== Last change: 04/12/2009 *
6 * -------------------------------------------------------------------- *
7 * File : expression-functions.php *
8 * -------------------------------------------------------------------- *
9 * Short description : Expression callback functions *
10 * -------------------------------------------------------------------- *
11 * Kurzbeschreibung : Expression-Callback-Funktionen *
12 * -------------------------------------------------------------------- *
15 * $Tag:: 0.2.1-FINAL $ *
17 * -------------------------------------------------------------------- *
18 * Copyright (c) 2003 - 2009 by Roland Haeder *
19 * Copyright (c) 2009 - 2012 by Mailer Developer Team *
20 * For more information visit: http://mxchange.org *
22 * This program is free software; you can redistribute it and/or modify *
23 * it under the terms of the GNU General Public License as published by *
24 * the Free Software Foundation; either version 2 of the License, or *
25 * (at your option) any later version. *
27 * This program is distributed in the hope that it will be useful, *
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
30 * GNU General Public License for more details. *
32 * You should have received a copy of the GNU General Public License *
33 * along with this program; if not, write to the Free Software *
34 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, *
36 ************************************************************************/
38 // Some security stuff...
39 if (!defined('__SECURITY')) {
43 // Private function to replace the code
44 function replaceExpressionCode ($data, $replacer) {
46 // @TODO is escapeQuotes() enough for strings with single/double quotes?
47 return str_replace($data['matches'][0][$data['key']], $replacer, escapeQuotes($data['code']));
50 // Private function to determine whether we have a special expression function avaible
51 // (mostly located in wrapper-functions.php)
52 function isExpressionFunctionAvaiable ($data) {
53 // Get the enty we need and trim it
54 $entry = trim($data['matches'][4][$data['key']]);
57 if (!isset($GLOBALS['expression_function_available'][$entry])) {
59 $functionName = 'get';
61 // Explode it in an array
62 foreach (explode('_', $entry) as $piece) {
63 // Add non-empty parts
66 $functionName .= capitalizeUnderscoreString($piece);
70 // Is that function there?
71 if (function_exists($functionName)) {
73 $GLOBALS['expression_function_name'][$entry] = $functionName;
74 $GLOBALS['expression_function_available'][$entry] = TRUE;
77 logDebugMessage(__FUNCTION__, __LINE__, 'Expression function ' . $functionName . ' not found. Please consider adding it to improve execution speed.');
80 $GLOBALS['expression_function_available'][$entry] = FALSE;
82 } elseif (($GLOBALS['expression_function_available'][$entry] == FALSE) && (isDebugModeEnabled())) {
83 // Debug message in debug mode
84 logDebugMessage(__FUNCTION__, __LINE__, 'Expression function for entry ' . $entry . ' requested but does not exist.');
88 return $GLOBALS['expression_function_available'][$entry];
91 // Getter for above expression function
92 function getExpressionFunction ($data) {
93 // Get the enty we need
94 $entry = trim($data['matches'][4][$data['key']]);
97 return $GLOBALS['expression_function_name'][$entry];
100 // Expression call-back function for getCode() calls
101 function doExpressionCode ($data) {
103 $code = str_replace($data['matches'][0][$data['key']], "{DQUOTE} . getCode('" . $data['matches'][4][$data['key']] . "') . {DQUOTE}", $data['code']);
105 // Return replaced code
109 // Expression call-back function for URLs (example: {%url=foo.php?bar=bar%})
110 function doExpressionUrl ($data) {
112 if ($data['callback'] == 'js') {
114 $data['output_mode'] = '1';
117 // Handle an URL here
118 $replacer = "{DQUOTE} . encodeUrl('" . $data['matches'][4][$data['key']] . "', " . $data['output_mode'] . ') . {DQUOTE}';
121 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'replacer=' . $replacer . ',callback=' . $data['callback'] . ',output_mode=' . $data['output_mode']);
124 $code = replaceExpressionCode($data, $replacer);
126 // Return replaced code
130 // Expression call-back function for reading data from $_SERVER
131 function doExpressionServer ($data) {
132 // This will make 'foo_bar' to detectFooBar()
133 $functionName = "'detect' . implode('', array_map('firstCharUpperCase', explode('_', '" . $data['callback'] . "')))";
136 $replacer = '{DQUOTE} . call_user_func(' . $functionName . ') . {DQUOTE}';
139 $code = replaceExpressionCode($data, $replacer);
141 // Return replaced code
145 // Expression call-back function for getting extension data
146 function doExpressionExt ($data) {
147 // Not installed is default
150 // Is the extension installed?
151 if (isExtensionInstalled($data['matches'][4][$data['key']])) {
152 // Construct call-back function name
153 $functionName = 'getExtension' . capitalizeUnderscoreString($data['callback']);
155 // Construct call of the function
156 $replacer = "{DQUOTE} . call_user_func_array('" . $functionName . "', array('" . $data['matches'][4][$data['key']] . "', TRUE)) . {DQUOTE}";
160 $replacer = sprintf("&ext=%s&ver=%s&rev={?CURRENT_REPOSITORY_REVISION?}", $data['matches'][4][$data['key']], $replacer);
162 // Replace it and insert parameter for GET request
163 $code = replaceExpressionCode($data, $replacer);
166 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'code=' . $code . ',replacer=' . $replacer . ',key=' . $data['key'] . ',callback=' . $data['callback']);
168 // Return replaced code
172 // Expression call-back function for getting configuration data
173 // @TODO FILTER_COMPILE_CONFIG does not handle call-back functions so we handle it here again
174 function doExpressionConfig ($data) {
175 // Is there a special expression function for it?
176 if (isExpressionFunctionAvaiable($data)) {
178 $replacer = '{DQUOTE} . ' . $data['callback'] . '(' . getExpressionFunction($data) . '()) . {DQUOTE}';
179 } elseif (!isConfigEntrySet($data['matches'][4][$data['key']])) {
180 // Config entry is not set
181 $replacer = '{DQUOTE} . ' . $data['callback'] . '(NULL) . {DQUOTE}';
183 // Default replacer is the config value itself
184 $replacer = '{DQUOTE} . ' . $data['callback'] . '(getConfig(' . chr(39) . $data['matches'][4][$data['key']] . chr(39) . ')) . {DQUOTE}';
187 // Replace the config entry
188 $code = replaceExpressionCode($data, $replacer);
190 // Return replaced code
194 // Expression call-back function for piping data through functions
195 function doExpressionPipe ($data) {
196 // We need callback and extra_func: callback is really the call-back function, extra_func is our value
197 $replacer = $data['extra_func'];
199 // Is there a call-back? Should always be there!
200 if (!empty($data['callback'])) {
201 //* DEBUG: */ if ($data['callback'] == 'getMemberId') die('<pre>'.encodeEntities(print_r($data, TRUE)).'</pre>');
202 // If the value is empty, we don't add it
203 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'value[' . gettype($data['value']) . ']=' . $data['value']);
204 if ((empty($data['value'])) && ($data['value'] != '0')) {
206 $replacer = '{DQUOTE} . ' . $data['extra_func2'] . '(' . $data['extra_func'] . '(' . $data['callback'] . '())) . {DQUOTE}';
207 } elseif (isXmlTypeBool($data['value'])) {
208 // Boolean value detected
209 $replacer = '{DQUOTE} . ' . $data['extra_func2'] . '(' . $data['extra_func'] . '(' . $data['callback'] . '(' . $data['value'] . '))) . {DQUOTE}';
211 // Some string/integer value is set
212 $replacer = '{DQUOTE} . ' . $data['extra_func2'] . '(' . $data['extra_func'] . '(' . $data['callback'] . "('" . $data['value'] . "'))) . {DQUOTE}";
216 // Replace the config entry
217 //* NOISY-DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'replacer=' . $replacer);
218 $code = replaceExpressionCode($data, $replacer);
220 // Return replaced code
224 // Expression call-back function for calling filters
225 function doExpressionFilter ($data) {
226 // Construct replacement
227 $replacer = "{DQUOTE} . runFilterChain('" . $data['matches'][4][$data['key']] . "') . {DQUOTE}";
229 // Run the filter and insert result
230 $code = replaceExpressionCode($data, $replacer);
232 // Return replaced code
236 // Expression call-back function for validator links
237 function doExpressionValidatorLinks ($data) {
238 // Default is nothing
241 // Get the code from data array for replacement/pipe-through
242 $code = $data['code'];
244 // Should we generally include validator links?
245 if ((isExtensionInstalled('validator')) && (getConfig('enable_validator') == 'Y') && (!in_array(getModule(), array('admin', 'login')))) {
246 // Load the validator template
247 $replacer = escapeQuotes(loadTemplate('validator_links', TRUE));
251 $code = replaceExpressionCode($data, $replacer);
253 // Return the (maybe) replaced code
257 // Expression call-back for dynamic messages
258 function doExpressionMessage ($data) {
260 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'callback=' . $data['callback'] . ',extra_func=' . $data['extra_func'] . ',value=' . $data['value']);
262 // Message string replacement depends on if message is masked
263 if ((isMessageMasked($data['callback'], FALSE)) && ((!empty($data['extra_func'])) || ($data['extra_func'] == '0'))) {
264 // Message should be masked
265 $replacer = "{DQUOTE} . getMaskedMessage('" . $data['callback'] . "', '" . $data['extra_func'] . "') . {DQUOTE}";
266 } elseif (!empty($data['value'])) {
267 // value is set, so it is masked message
268 $replacer = "{DQUOTE} . getMaskedMessage('" . $data['callback'] . "', '" . $data['value'] . "') . {DQUOTE}";
271 $replacer = "{DQUOTE} . getMessage('" . $data['callback'] . "') . {DQUOTE}";
275 $code = replaceExpressionCode($data, $replacer);
277 // Return the (maybe) replaced code
281 // Expression call-back for template functions
282 function doExpressionTemplate ($data) {
283 // Construct call-back function name
284 $callbackFunction = 'doTemplate' . $data['callback'];
287 $replacer = '<!-- [' . __FUNCTION__ . ':' . __LINE__.'] Call-back function ' . $callbackFunction . ' does not exist. //-->';
289 // Is the function there?
290 if (!isset($GLOBALS['current_template'])) {
291 // This is very bad and needs fixing
292 reportBug(__FUNCTION__, __LINE__, 'current_template in GLOBALS not set, callbackFunction=' . $callbackFunction . ',function_exists()=' . intval(function_exists($callbackFunction)));
293 } elseif (!function_exists($callbackFunction)) {
294 // Log missing function only in debug mode
295 if (isDebugModeEnabled()) {
297 logDebugMessage(__FUNCTION__, __LINE__, 'Call-back function ' . $callbackFunction . ' does not exist.');
300 // Do the replacement
301 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'template='.$GLOBALS['current_template']);
302 $replacer = '{DQUOTE} . ' . $callbackFunction . '(' . chr(39) . $GLOBALS['current_template'] . chr(39) . ', TRUE';
305 if (!empty($data['value'])) {
306 // Then include it as well
307 $replacer .= ', ' . chr(39) . $data['value'] . chr(39);
311 $replacer .= ') . {DQUOTE}';
315 $code = replaceExpressionCode($data, $replacer);
317 // Return the (maybe) replaced code
321 // Expression call-back for math functions
322 function doExpressionMath ($data) {
323 // Do the replacement
324 //* DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'template='.$GLOBALS['current_template']);
325 $replacer = '{DQUOTE} . doCalculate' . $data['callback'] . '(' . $data['value'] . ') . {DQUOTE}';
328 $code = replaceExpressionCode($data, $replacer);
331 loadIncludeOnce('inc/math-functions.php');
333 // Return the (maybe) replaced code
337 // Expression call-back for GET request
338 function doExpressionGet ($data) {
339 // Construct the replacer:
340 // - GET request element
341 $replacer = '{%pipe,getRequestElement';
343 // Add more call-back functions?
344 if (!empty($data['callback'])) {
346 $replacer .= ',' . $data['callback'];
349 // - Finalize replacer
350 $replacer .= '=' . $data['value'] . '%}';
353 $code = replaceExpressionCode($data, $replacer);
355 // Return the (maybe) replaced code
359 // Expression call-back for POST request
360 function doExpressionPost ($data) {
361 // Construct the replacer:
362 // - POST request element
363 $replacer = '{%pipe,postRequestElement';
365 // Add more call-back functions?
366 if (!empty($data['callback'])) {
368 $replacer .= ',' . $data['callback'];
371 // - Finalize replacer
372 $replacer .= '=' . $data['value'] . '%}';
375 $code = replaceExpressionCode($data, $replacer);
377 // Return the (maybe) replaced code
381 // Expression call-back for session data
382 function doExpressionSession ($data) {
383 // Construct the replacer:
385 $replacer = '{%pipe,getSession';
387 // Add more call-back functions?
388 if (!empty($data['callback'])) {
390 $replacer .= ',' . $data['callback'];
393 // - Finalize replacer
394 $replacer .= '=' . $data['value'] . '%}';
397 //* NOISY-DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'value=' . $data['value'] . ',replacer=' . $replacer);
400 $code = replaceExpressionCode($data, $replacer);
402 // Return the (maybe) replaced code
407 * Expression call-back for session piplining, this means:
409 * 1) Read session data
410 * 2) Wrap the raw data into {%pipe,fooFunction=$rawValue%}
412 function doExpressionSessionPipe ($data) {
413 // Get the session data
414 $rawValue = getSession($data['value']);
416 // Construct the replacer
417 $replacer = '{%pipe,' . $data['callback'] . '=' . $rawValue . '%}';
420 //* NOISY-DEBUG: */ logDebugMessage(__FUNCTION__, __LINE__, 'value=' . $data['value'] . ',rawValue=' . $rawValue . ',replacer=' . $replacer);
423 $code = replaceExpressionCode($data, $replacer);
425 // Return the (maybe) replaced code
429 // Expression call-back for formulars
430 function doExpressionForm ($data) {
431 // Default method is GET, target is _self
432 $data['__form_method'] = 'get';
433 $data['__form_target'] = '_self';
434 $data['__form_name'] = 'form';
435 $data['__form_id'] = 'form';
436 $data['__server'] = '';
438 // Check which method/target is set
439 foreach (array('callback', 'extra_func', 'extra_func2') as $key) {
441 $value = strtolower($data[$key]);
443 // Is formMethodPost set?
444 if ($value == 'formmethodpost') {
446 $data['__form_method'] = 'post';
447 } elseif (($value == 'formmethodpost') && (!isSpider()) && (!isSessionValid())) {
448 // Then expand 'value' with session id
449 if (strpos($data['value'], '?') !== FALSE) {
451 $data['value'] .= '&';
454 $data['value'] .= '?';
457 // Append session's name and id
458 $data['value'] .= session_name() . '=' . session_id();
459 } elseif (substr($value, 0, 10) == 'formtarget') {
460 // Form target is found
461 $data['__form_target'] = substr($value, 10);
462 } elseif (substr($value, 0, 8) == 'formname') {
463 // Form name is found
464 $data['__form_name'] = substr($value, 8);
465 } elseif (substr($value, 0, 6) == 'formid') {
467 $data['__form_id'] = substr($value, 6);
468 } elseif (substr($value, 0, 6) == 'server') {
469 // {%server,foo%} found
470 $data['__server'] = '{%server=' . substr($value, 6) . '%}';
474 // Generate the replacement code which is the opening form tag
475 $data['__replacer'] = '<form accept-charset=\"UTF-8\"';
476 if (!empty($data['value'])) {
477 $data['__replacer'] .= ' action=\"{%url=' . $data['value'];
478 if (!empty($data['__server'])) {
479 $data['__replacer'] .= $data['__server'];
481 $data['__replacer'] .= '%}\"';
485 foreach (array('method', 'target', 'name', 'id') as $key) {
486 $data['__replacer'] .= ' ' . $key . '=\"' . $data['__form_' . $key] . '\"';
489 // Close the tag here (don't move it below the next filter)
490 $data['__replacer'] .= '>' . PHP_EOL;
493 * Call a filter chain to allow more hidden fields being added. You should
494 * not remove the > char from above line to add onsubmit="" or so. Instead
495 * you should better use jquery to accomplish the same.
497 $data = runFilterChain('open_form_fields', $data);
500 $code = replaceExpressionCode($data, $data['__replacer']);
502 // Return the (maybe) replaced code
506 // Expression call-back to close form tags
507 function doExpressionFormClose ($data) {
508 // Initial replacer is really easy ...
509 $data['__replacer'] = '</form>' . PHP_EOL;
512 * Call a filter chain to allow more hidden fields being added at the end
515 $data = runFilterChain('close_form_fields', $data);
518 $code = replaceExpressionCode($data, $data['__replacer']);
520 // Return the (maybe) replaced code
524 // Expression call-back to handle jquery inclusion
525 function doExpressionJquery ($data) {
526 // Default is compressed
527 $jquery = 'jquery.js';
529 // Is debug mode enabled?
530 if ((isGetRequestElementSet('jquery')) || (isSessionVariableSet('jquery'))) {
531 // Then use uncompressed
532 $jquery = 'jquery-uncompressed.js';
534 // Remember it in session
535 setSession('jquery', '1');
538 // Add {%url%} around it
539 $replacer = '{%url=js/' . $jquery . '?dummy=1%}';
542 $code = replaceExpressionCode($data, $replacer);
544 // Return the (maybe) replaced code