<?php if (!defined('PmWiki')) exit(); /* +----------------------------------------------------------------------+ | Copyright 2015 Hans Bracker. http://www.softflow.co.uk | This program is free software; you can redistribute it and/or modify | it under the terms of the GNU General Public License, Version 2, as | published by the Free Software Foundation. | http://www.gnu.org/copyleft/gpl.html | This program 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. +----------------------------------------------------------------------+ | fox.php for PmWiki | A form processing script for PmWiki 2.2.76 and above, and PHP 5.4 and above | Instructions on how to use it see http://softflow.co.uk/design/FoxDocumentation/ +----------------------------------------------------------------------+ | Other contributors: | * Nils Knappmeier (nk@knappi.org): original script adddeleteline2.php | * John Rankin: AccessCode code from commentbox.php | * Petko Yotov (2006): original deletelink directive | * Patrick R. Michaud: permission check, input validation and other ideas | * Feral: FoxFilter hook and other improvements | * Stirling Westrup (2007): code overhaul with a rewrite of TemplateEngine | * and associated functions and many function improvements +----------------------------------------------------------------------+ */ $RecipeInfo['Fox']['Version'] = '2023-01-30'; $FmtPV['$FoxVersion'] = "'{$RecipeInfo["Fox"]["Version"]}'"; // switch on echos for debugging, can be set in config.php SDV($FoxDebug, 0); //echo process: 1 - basic; 2 - +input, 3 - +targets, 4 - early target arrays, // 5 - +template, +new text, +ptvs, 6 - +full text, 7 - +marks //in case page name is not resolved $pagename = ResolvePageName($pagename); // auth permission level for adding and deleting posts. // 'edit': needs edit permission. // 'read': needs read permission (can post to edit protected pages) // 'ALWAYS':no permission needed, can also post to 'read' protected pages. // $FoxPagePermissions must also be set to include page(s) as permitted targets SDV($FoxAuth, 'edit'); SDV($FoxConfigPageFmt, "$SiteAdminGroup.FoxConfig"); //default config page for page permission patterns SDV($FoxFormsFmt, "$SiteGroup.FoxForms"); //default page + section for Fox form templates SDV($FoxTemplatePageFmt, "$SiteGroup.FoxTemplates#null"); SDV($EnableFoxUrlInput, false); //set to true to allow input via url parameters (php $_GET array) SDV($EnablePostDirectives, false); //set to true to allow posting of directives of form (: :) SDV($EnableAccessCode, false); //set to true to enable accesscode check (needs access code form fields as well) SDV($EnableFoxDefaultMsg, true); //to stop fatal php timeout errors set two seconds less than php.ini: max_execution_time 30sec default SDV($FoxProcessTimeMax, 28); //processing time limit in seconds for UpdatePages process. SDV($FoxClearPTVFmt, 'NULL'); //string used as input to clear a PTV. SDV($EnableFoxPTVDelete, false); //set to true to allow PTVs to be deleted by changing value for a PTV to $FoxPTVDeleteKey SDV($FoxPTVDeleteKey, 'ERASEPTV'); //string to use for erasing (deleting) PTVs, if $EnableFoxPTVDelete = true; SDV($FoxCheckErrorMsg, '$[Please enter valid input!]'); SDV($FoxEnablePTVRefresh, 1); //unsets page cache for PTVs, so PTVs will refresh without need for full page refresh SDV($FoxPTVArraySeparator, " "); //separates posted elements of a PTV array. ##security check for FoxConfig page location if ($SiteGroup != $SiteAdminGroup) if (PageExists('$SiteGroup.FoxConfig') && !PageExists('$SiteAdminGroup.FoxConfig')) echo "Security update: Fox detected page <a href='$ScriptUrl?n=$SiteGroup.FoxConfig'>$SiteGroup.FoxConfig</a><br /> Please move the page to <a href='$ScriptUrl?n=$SiteAdminGroup.FoxConfig?action=edit'>$SiteAdminGroup.FoxConfig</a>! "; // Search path for fox.css files, if any. if (!isset($FarmPubDirUrl)) $FarmPubDirUrl = $PubDirUrl; SDV($FoxPubListFmt, array ( "pub/fox/fox.css" => "$PubDirUrl/fox/fox.css", "$FarmD/pub/fox/fox.css" => "$FarmPubDirUrl/fox/fox.css" )); # load special styles for edit/delete button and links and for message classes foreach((array)$FoxPubListFmt as $k=>$v) { if (file_exists(FmtPageName($k,$pagename))) { $HTMLHeaderFmt['fox'][] = "<link rel='stylesheet' type='text/css' href='$v' media='screen' />\n"; break; } } //}}} //conditionals for edit forms $Conditions['foxpreview'] = '(boolean)$_POST["preview"]'; $Conditions['foxcheck'] = $Conditions['foxerror'] = 'FoxCheckErrorCond($condparm) == 1'; function FoxCheckErrorCond($arg) { global $FoxCheckError; if (!$arg) return (boolean)$FoxCheckError; $arg = ParseArgs($arg); if ($arg[''][0] != $_POST["foxname"]) return ''; if (in_array($arg[''][1], $FoxCheckError)) return 1; } //}}} ## (:foxform #section :) or (:foxform FormPage#section :) for loading Fox forms ## #section is retrieved from page(s) given with $FoxFormsFmt. ## $FoxFormsFmt can be array of page names. Markup('foxform', '>if', '/\\(:foxform\\s+(\\S.*?):\\)/i', "FoxLoadForm"); function FoxLoadForm($m) { global $FoxFormsFmt; extract($GLOBALS['MarkupToHTML']); $list = (is_array($FoxFormsFmt)) ? $FoxFormsFmt : array($FoxFormsFmt); $text = RetrieveAuthSection($pagename, $m[1], $list, 'read'); if (!$text) return "%red%Fox form [[$args]] is missing"; return PRR(PVSE($text)); } //}}} //(:fox formname ....:) main form markup, starts a fox form Markup('fox','directives','/\(:fox ([\\w]+)\\s?(.*?):\)/i',"FoxMarkup"); ## Creates the HTML code for the (:fox name [placement] [parameters]:) directive function FoxMarkup($m) { extract($GLOBALS['MarkupToHTML']); $PageUrl = PageVar($pagename, '$PageUrl'); static $cnt = 0; $cnt++; $defaults = array(); $name = $m[1]; $args = ParseArgs($m[2]); if (!isset($args['put'])) $args['put'] = (isset($args[''][0])) ? $args[''][0] : 'bottom'; $opt = array_merge($defaults, $args); if (isset($opt['redirect'])) { $opt['redir'] = $opt['redirect']; unset($opt['redirect']);} $javacheck = (isset($opt['formcheck']) ? FoxJSFormCheck($opt['formcheck']) : ''); $out = $javacheck; $out.= "<form name='$name' action='{$PageUrl}' method='post' ". (isset($opt['newwin']) ? "target='_blank'" : ""). (isset($opt['upload'])||isset($opt['uptarget']) ? "enctype='multipart/form-data'" : ""). (isset($opt['formcheck']) ? "onsubmit=\"return checkform(this);\">" : ">"). "<input type='hidden' name='foxpage' value='$pagename' />". "<input type='hidden' name='action' value='foxpost' />". "<input type='hidden' name='foxname' value='$name' />"; foreach ($opt as $key => $val) { if(!is_array($val)) $out.= "<input type='hidden' name='".$key."' value='".$val."' />"; } return Keep($out); } //}}} //(:foxtemplate "...":) (:foxpreviewtemplate "...":) Markup('foxtemplate','<input','/\\(:fox(display|preview|)template\\s+"(.*?)\\s*"\\s*:\\)/', "FoxTemplateMarkup"); # Creates the HTML code for (:foxtemplate "templatestring":) as hidden input form field # $m[2] is the template string function FoxTemplateMarkup($m) { $t = ($m[1]=='display' || $m[1]=='preview') ? 'preview' : ''; return Keep('<input type="hidden" name="fox'.$t.'template" value="'.htmlspecialchars($m[2]).'"/>'); } //}}} //(:fox-post ...:) (:fox-add ...:) (:fox-copy ...:) (:fox-replace ...:) (:fox-ptv ...:) //(:foxpost ...:) (:foxadd ...:) (:foxcopy ...:) (:foxreplace ...:) (:foxptv ...:) # markup for multi-page processing, can be used multipe times in form Markup('foxpost','directives','/\(:fox-?(post|add|copy|replace|ptv|mail)\\s+(.*?):\)/i',"FoxPostMarkup"); # create hidden HTML input tags for template and target pages, for foxpost and foxcopy markup function FoxPostMarkup($m) { $act = $m[1]; if ($act=='post') $act = 'add'; //'post' deprecated in favour of 'add' $defaults = array('if'=>'', 'put'=>'','mark'=>'', 'endmark'=>'', 'ptvfields'=>'', 'ptvfmt'=>'','ptvclear'=>'','foxtemplate'=>''); //placeholders $put_names = array('top','bottom','prepend','append','below','above','belowform','aboveform','string','marktomark'); $args = ParseArgs($m[2],'(?>([-\\w]+(?:\.[-\\w]+)?(?:\\w#[-.\\w]*)?(?:\\#[-.\\w]*)?)(?:=>|[=:]))'); $opt = array_merge($defaults, $args); $opt[''] = (array)@$opt['']; $opt['foxaction'] = $act; foreach ($opt[''] as $i => $p) { if ($p =='') continue; if (in_array($p, $put_names) && !isset($args['put'])) $opt['put'] = $p; else $param[] = $p; } if (isset($param)) $opt['foxparam'] = implode(',',$param); if ($act=='ptv') { if (isset($opt['foxparam']) && !(isset($opt['target']) OR isset($opt['ptvtarget']))) $opt['target'] = $opt['foxparam']; if (isset($opt['ptvtarget'])) $opt['target'] = $opt['ptvtarget']; } unset($opt[''],$opt['#']); $keys = $opt; foreach ($keys as $k => $v) if (preg_match('/(^[A-Z0-9].*)|(^\\w+?\\..+)/',$k)) unset($keys[$k]); unset($keys['target'], $keys['template']); $out = ''; foreach ($opt as $key => $val) { //template=>target parameters. template in key>=val must start Upper case or numeral or contain a dot if (preg_match('/(^[A-Z0-9].*)|(^\\w+?\\..+)/',$key)) { $out .= '<input type="hidden" name=":template[]" value="'.PVSE($key).'"/>'; $out .= '<input type="hidden" name=":target[]" value="'.PVSE($val).'"/>'; foreach($keys as $d => $val) $out .= '<input type="hidden" name=":'.$d.'[]" value="'.PVSE($val).'"/>'; } //target= parameter elseif ($key=='target') { $targets = explode(",", $val); //can be comma separated list $templates = array(); if(isset($opt['template'])) { $templates = explode(",", $opt['template']); //can be comma separated list //use last template for any missing templates while( count($templates)<count($targets) ) $templates[] = @end($templates); } //make :target[] :template[] array elements, add other keys to get proper index mapping foreach($targets as $i => $tgt) { if (!isset($templates[$i])) $templates[$i] = ''; $out .= '<input type="hidden" name=":target[]" value="'.$tgt.'"/>'; $out .= '<input type="hidden" name=":template[]" value="'.$templates[$i].'"/>'; foreach($keys as $d => $v) $out .= '<input type="hidden" name=":'.$d.'[]" value="'.PVSE($v).'"/>'; } } } return Keep($out); } //}}} // (:foxcheck name [match='wikiwildcardpattern'] [regex='regexpattern'] [if='condition'] [msg='error message'] :) Markup('foxcheck','directives','/\(:fox-?(check)\\s+(.*?):\)/i',"FoxCheckMarkup"); function FoxCheckMarkup($m) { static $idx = 0; $opt = ParseArgs($m[2]); #show($opt,'opt'); $opt[''] = (array)$opt['']; $opt['name'] = isset($opt['name']) ? $opt['name'] : array_shift($opt['']); if (!isset($opt['match']) && $opt['']) $opt['match'] = array_shift($opt['']); unset($opt['#'], $opt['']); $out = ''; foreach ($opt as $key => $val) { if ($val=='') continue; $out .= '<input type="hidden" name="chk_'.$key.'['.$idx.']" value="'.PHSC($val).'"/>'; } $idx++; return Keep($out); } //}}} // (:foxmessage [form] [name]:) (:foxdisplay [form] [name]:) Markup('foxdisplaymessage','directives','/\\(:fox-?(message|display|preview)s?\\s*(.*?)\\s*:\\)/', "FoxDisplayMarkup"); function FoxDisplayMarkup($m) { global $FoxMsgFmt, $FoxDisplayFmt; extract($GLOBALS['MarkupToHTML']); if (!$FoxMsgFmt && !$FoxDisplayFmt) return ''; $opt = ParseArgs($m[2]); $opt[''] = (array)@$opt['']; $form = (isset($opt['form'])) ? $opt['form'] : array_shift($opt['']); $name = (isset($opt['name'])) ? $opt['name'] : array_shift($opt['']); $msg = ''; if($FoxMsgFmt) { if (!$form && !$name) $msg = implode("\\\\\n", $FoxMsgFmt); //show all messages elseif ($form==@$_REQUEST['foxname']) { if (isset($name) ) $msg = @$FoxMsgFmt[$name]; //show error message from check 'name' elseif (@$opt['list']=='nocheck') //show non-name messages foreach($FoxMsgFmt as $k => $v) { if (is_int($k)) $msg .= $v; } elseif (@$opt['list']=='check') //show name messages foreach($FoxMsgFmt as $k => $v) { if (is_int($k)) continue; $msg .= $v; } else $msg = implode("\\\\\n", $FoxMsgFmt); //show all messages } } if ($m[1]=='display' || $m[1]=='preview' || @$opt['list']=='display') if (!$form || $form==@$_REQUEST['foxname']) if(!is_array($FoxDisplayFmt)) $msg = $FoxDisplayFmt; else if ($form==@$_REQUEST['foxname'] && isset($name)) $msg = @$FoxDisplayFmt[$name]; $out = MarkupToHTML($pagename, $msg); //strip p tags from beginning and end, trim end space $out = rtrim(preg_replace("/^<p>(.*?)<\\/p>$/s","$1", $out)); return Keep($out); } //}}} # (:foxend name:) Markup('foxendform','directives','/\(:foxend(\\s[\\w]+):\)/', "</form>"); # (:foxprepend:) and (:foxappend:) just vanish because they are only used later in FoxInsertText Markup('foxaprepend','directives','/\(:fox(ap|pre)pend\\s*(.*?)\\s*:\)/',''); # (:foxallow:) for permission check: if present will grant page permission Markup('foxallow','directives','/\(:foxallow\\s*(.*?)\\s*:\)/',''); # #foxbegin# and #foxend# invisible markers used by foxdelete links and buttons Markup('foxentry','<fulltext','/#fox(begin|end)( [-:\\w]+)?#/',''); # add FoxEditTemplate to $EditFunctions for FoxHandlePost (foxaction newedit) if($action=='edit' && @$_REQUEST['foxtemptext']==1) array_unshift($EditFunctions, 'FoxEditTemplate'); ## provide page text for ?action=edit&foxtemptext=1 function FoxEditTemplate($pagename, &$page, &$new) { if (@$new['text'] > '') return ''; if (@$_REQUEST['foxtemptext']) { if ($_SESSION["FoxTempPageText"] > '') $new['text'] = $_SESSION["FoxTempPageText"]; return ''; } } //}}} # add action foxpost $HandleActions['foxpost'] = 'FoxHandlePost'; ## Main function called with action=foxpost function FoxHandlePost($pagename, $auth) { global $InputValues, $EnableFoxUrlInput, $EnableFoxDefaultMsg, $EnablePostDirectives, $IsPagePosted, $FoxDebug, $FmtV, $FoxMsgFmt; FoxTimer($pagename, 'FoxHandlePost: begin'); //get arguments from POST and GET if ($EnableFoxUrlInput==true) $fx = RequestArgs(); else $fx = RequestArgs($_POST); //get arguments from FILES foreach($_FILES as $n => $upfile) { if ($upfile['name']=='') continue; foreach($upfile as $k => $v) if (!$fx[$n.'_'.$k]) $fx[$n.'_'.$k] = $v; //save file extension $fx[$n.'_ext'] = end(explode(".", $upfile['name'])); } //store current values to redisplay, in case we abort. foreach ($fx as $k=>$v) { if (is_array($v)) foreach ($v as $kk=>$vv) $InputValues[$k][$kk] = htmlspecialchars($vv,ENT_NOQUOTES); else $InputValues[$k] = htmlspecialchars($v,ENT_NOQUOTES); } //abort if non-permitted input, i.e. if GET input is not allowed if ($fx['action']!='foxpost') FoxAbort($pagename, "$[Error: input not permitted]"); //use foxpage as abort target. if(isset($fx['foxpage'])) $pagename = $fx['foxpage']; //initialise $redirname = $pagename; //preprocess fields with FoxFilter, which calls external filter functions if (isset($fx['foxfilter'])) FoxFilter($pagename, $fx); //initialising preview if (isset($fx['preview']) || isset($fx['foxdisplay'])) { //see if we got a preview template if ($fx['foxpreviewtemplate']) { $fx['foxtemplate'] = $fx['foxpreviewtemplate']; } else if ($fx['previewtemplate']) { $fx['template'] = $fx['previewtemplate']; } //for cases not called by a foxedit form if (!isset($_SESSION['foxedit'][$pagename])) { $fx['foxaction'] = 'display'; $fx['target'] = $pagename; unset($fx['redir']); unset($fx[':target']); unset($fx['ptvtarget']); } } //DEBUG// if($FoxDebug==2) show($fx,'$fx early'); //make foxgroup input suitable for group name, add foxgrouptitle to preserve original input if (isset($fx['foxgroup'])) { $fx['foxgrouptitle'] = $fx['foxgroup']; $fx['foxgroup'] = FoxWikiWord($fx['foxgroup']); } //sanitize posted directives and markup expressions if ($EnablePostDirectives==false) FoxDefuseMarkup($pagename, $fx); //do {$$var} input field replacements and process {$$(expre ...)} markup expressions FoxInputVarReplace($pagename, $fx); //DEBUG// if($FoxDebug>2) show($fx,'$fx after Input Var Replace'); //make ptv array from ptv_ fields FoxPTVFields($pagename, $fx); //check form input as set by (:foxcheck ..:) markup FoxInputCheck($pagename, $fx); //build list of targets as array with associated parameters $to = FoxTargetList($pagename, $fx); $targets = array_keys($to); //check for foxnotify input to do notifications (needs FoxNotify installed) if (isset($fx['foxnotify'])) { global $FoxNotifyLists, $FoxNotifyListsGroup; $FoxNotifyLists = array(); if (is_array($fx['foxnotify'])) { foreach($fx['foxnotify'] as $n) $FoxNotifyLists[] = $FoxNotifyListsGroup.".".$n; } else $FoxNotifyLists[] = $FoxNotifyListsGroup.".".$fx['foxnotify']; } //set $redirectname for redirect, but inhibit redirect when debugging, to see the debug echos if (isset($fx['redir']) AND $FoxDebug==0) $urlfmt = FoxRedirectFmt($pagename, $targets, $fx); if (isset($fx['urlfmt'])) $urlfmt = $fx['urlfmt']; //cancel if (isset($fx['cancel'])) { $urlfmt = (isset($fx['cancelredirect']))? FoxRedirectFmt($pagename, $targets, $fx) : ''; Redirect($redirname, $urlfmt); exit; } //check various genaral security restrictions (page permissions are checked later) //check for and possibly defuse posted markup FoxSecurityCheck($pagename, $targets, $fx); //main page processing $counter = FoxProcessTargets($pagename, $fx, $to); //upload files if (isset($fx['uptarget'])) FoxPostUpload($pagename, $fx); $xtime = FoxTimer($pagename,'FoxHandlePost: end'); if( $EnableFoxDefaultMsg==1 && $counter>1 ) $FoxMsgFmt[] = "$counter pages processed in $xtime seconds"; if(!isset($urlfmt)) { FoxFinish($redirname, $fx, ''); } else Redirect($redirname, $urlfmt); } //}}} # create url for redirections function FoxRedirectFmt ($pagename, $targets, $fx) { global $EnablePathInfo, $ScriptUrl; $arg = ''; $anch = ''; if (isset($fx['redir'])) $redir = $fx['redir']; elseif (isset($fx['cancelredir'])) $redir = $fx['cancelredirect']; else $redir = $pagename; if ($redir==0) $redir = $pagename; if (substr($redir,0,4)=="http") $urlfmt = $redir; else { if($fx['redir']==='1') { $pname = end($targets); } else { $rr = explode("?", $redir); $aa = explode("#", $rr[0]); if ($aa[1]) $anch = "#".$aa[1]; $pname = FoxGroupName($pagename, $fx, $rr[0]); if ($rr[1]) $arg = "?".$rr[1]; } $pname = str_replace(".","/",$pname); //set urlfmt for redirect $urlfmt = (IsEnabled($EnablePathInfo, 0) ? $ScriptUrl."/".$pname.$anch.$arg : $ScriptUrl."?n=".$pname.$anch.$arg ); } return $urlfmt; } //}}} ## create arrays from special fields & build target array function FoxTargetList ($pagename, &$fx) { global $FoxDebug; if($FoxDebug>3) echo "<br /> FoxTargetList>"; //DEBUG// //assign current page as target if no target is specified, but template is given. //exclude this for dangerous actions to prevent overwriting form page if (!isset($fx[':target']) && !isset($fx['target']) && !isset($fx['newedit'])) if ((isset($fx['template']) && $fx['template']!=0) || isset($fx['foxtemplate'])) if (!isset($fx['foxaction']) || (isset($fx['foxaction']) && $fx['foxaction']!='copy' && !($fx['foxaction']=='replace' && $fx['put']=='overwrite'))) $fx['target'] = $pagename; //'display' by default has current page as target if (isset($foxaction) && $fx['foxaction']=='display' && !isset($fx['target'])) $fx['target'] = array($pagename); //no template, foxtemplate or foxpreviewtemplate parameter -> unset target if (isset($fx['target']) && !isset($fx['template']) && !isset($fx['foxtemplate']) && !isset($fx['foxpreviewtemplate'])) { unset($fx['target']); if (!isset($fx[':target']) && !isset($fx['newedit'])) FoxAbort($pagename, "$[Error: no template specified!]"); } if (isset($fx['foxcopy']) && $fx['foxcopy']===1) $fx['foxaction'] = 'copy'; //create upload target if (isset($fx['uptarget'])) $fx['uptarget'] = FoxGroupName($pagename, $fx, $fx['uptarget']); elseif (isset($fx['upload']) && $fx['upload']==1) { if (isset($fx['target'])) { $tgts = explode(',',$fx['target']); $fx['uptarget'] = FoxGroupName($pagename, $fx, $tgts[0]); } else $fx['uptarget'] = $pagename; } //create newedit target, will be used as last element in target array $new_to = array(); if (isset($fx['newedit'])) { $new_to[0]['target'] = $new_to[0]['fulltarget'] = FoxGroupName($pagename, $fx, $fx['newedit']); $new_to[0]['foxaction'] = 'newedit'; if ($fx['template']) { $tempArr = explode(',',$fx['template']); $new_to[0]['template'] = end($tempArr); } elseif ($fx['foxtemplate']) $new_to[0]['template'] = 'foxtemplate'; else $new_to[0]['template'] = 0; } //make arrays from keys fields which could be arrays or lists for making arrays //assign values to targets, csv lists: if no more value use previous one $csv_to = array(); $keys = array('foxaction','target','template','put','mark','endmark', 'foxsuccess','foxfailure','foxtemplate'); if (isset($fx['target']) && $fx['target']!='') { foreach ($keys as $n) { if(isset($fx[$n]) && $fx[$n]!='') { $kp[$n] = array(); if(is_array($fx[$n])) $kp[$n] = $fx[$n]; elseif ($n=='target' || $n=='template' || $n=='put') $kp[$n] = explode(",", $fx[$n]); else $kp[$n] = array($fx[$n]); } } unset($keys['target']); foreach($keys as $k) { if (isset($kp['target'])) { foreach($kp['target'] as $i => $tg) { $csv_to[$i]['target'] = $tg; if (isset($kp[$k][$i])) $csv_to[$i][$k] = $kp[$k][$i]; elseif (isset($kp[$k][$i-1])) $csv_to[$i][$k] = $kp[$k][$i] = $kp[$k][$i-1]; } } } } if($FoxDebug==4) show($csv_to, 'csv_to'); if (isset($fx['ptvupdate']) && $fx['ptvupdate']==1 && !isset($fx['ptvtarget']) && isset($fx['target'])) $fx['ptvtarget'] = $fx['target']; //create target array from ptvtarget $ptv_to = array(); if (isset($fx['ptvtarget'])) { $fx['ptvtarget'] = explode(',',$fx['ptvtarget']); foreach($fx['ptvtarget'] as $i => $t) { $ptv_to[$i]['target'] = $t; $ptv_to[$i]['foxaction'] = 'ptv'; foreach(array('ptvfields','ptvclear','ptvfmt') as $n) if (isset($fx[$n])) $ptv_to[$i][$n] = $fx[$n]; } } if($FoxDebug==4) show($ptv_to,'ptv_to'); //create target array from fx[': '] fields $ext_to = array(); foreach($fx as $k => $ar) { if ($k[0]!=':') continue; foreach($ar as $i => $v) { $n = substr($k,1); // remove leading : if ($v!='') $ext_to[$i][$n] = $v; // set only non-empty values if ($n=='if' && !CondText($pagename, 'if '.$v, 'yes')) $ext_to[$i]['target'] = ''; } } //remove entries with missing target silently foreach ($ext_to as $i => $tg) if ($tg['target']=='') unset($ext_to[$i]); if($FoxDebug==4) show($ext_to,'ext_to'); //merge input targets. Process 1. extended markup targets, 2. std/csv markup targets, 3. ptv targets $to = array_merge($ext_to, $csv_to, $ptv_to); if($FoxDebug==4) show($to,'$to first'); //set any default parameters FoxTargetDefaults($fx, $to); if($FoxDebug==4) show($to,'$to second'); //make target names foreach($to as $i => $ar) { $to[$i]['target'] = FoxGroupName($pagename, $fx, $ar['target']); $to[$i]['fulltarget'] = $to[$i]['target'].strstr($ar['target'], '#'); if($FoxDebug>2) echo " target=".$to[$i]['target']; if (isset($ar['ptvtarget'])) { $to[$i]['ptvtarget'] = FoxGroupName($pagename, $fx, $ar['ptvtarget']); if($FoxDebug>2) echo " ptvtarget=".$to[$i]['target']; if ($fx['ptvupdate']==1 && PageExists($to[$i]['target'])) $to[$i]['foxaction'] = 'ptv'; } } //sort by target name and foxaction usort($to, "FoxTargetSort"); $to = array_merge($to, $new_to); //add newedit target to end FoxCombineTargets($to); //DEBUG// if($FoxDebug>2) show($to,'$to target'); return $to; } //}}} ## sort $to array by target, then secondary by foxaction function FoxTargetSort($a ,$b) { $order = array('copy','add','replace','ptv','display'); //first sort by target name if ($a['target'] < $b['target']) { return -1; } elseif ($a['target'] > $b['target']) { return 1; } else { //same target, now sort by foxaction return ( array_search($a['foxaction'], $order) > array_search($b['foxaction'], $order)); } } //}}} ## reorganise $to array function FoxCombineTargets(&$to) { $newto = array(); foreach($to as $i => $v) $newto[$v['target']][] = $to[$i]; $to = $newto; unset($newto); } //}}} ## set defaults for target array function FoxTargetDefaults( $fx, &$to ) { global $FoxDebug; if ($FoxDebug==4) echo "FoxTargetDefaults>"; foreach($to as $i => $tg) { //set 'template' if (isset($fx['foxtemplate'])) if (!isset($tg['template']) && !isset($tg['foxtemplate']) && $to[$i]['foxaction']!='ptv') $to[$i]['template'] = 'foxtemplate'; //used by FoxLoadTemplate //set 'foxaction' if (!isset($tg['foxaction']) ) $to[$i]['foxaction'] = 'add'; //set 'put' if (!isset($tg['put']) && $to[$i]['foxaction']=='add') $to[$i]['put'] = 'bottom'; elseif (!isset($tg['put']) && $to[$i]['foxaction']=='replace') $to[$i]['put'] = 'string'; } } //}}} ## check 'foxgroup' and set targetname, function FoxGroupName($pagename, $fx, $name) { global $FoxDebug; if($FoxDebug) echo "<br /> FoxGroupName> ".$name; //DEBUG// if ($name[0]=='#') $name = PageVar($pagename,'$Name'); if (!isset($fx['foxgroup'])) { $pname = MakePageName($pagename, $name); if (substr($pname, -1)==".") return ''; return $pname; } else $group = $fx['foxgroup']; // exception: for 'escaped' target name ignore foxgroup if (substr($name,0)=="\\") { $name = str_replace("\\","",$name); return MakePageName($pagename, $name); } else { $name = FoxWikiWord($name); return $group.'.'.$name; } } //}}} ## processing of target pages for all foxactions function FoxProcessTargets($pagename, $fx, $to) { global $FoxDebug; if($FoxDebug) echo "<br/><b> FoxProcessTargets></b> "; //DEBUG// global $FoxAuth, $EnableBlocklist, $FoxMsgFmt, $EnableFoxDefaultMsg, $ScriptUrl, $FmtV, $IsPagePosted, $Now, $ChangeSummary, $EditFunctions, $FoxExcludeEditFunctions, $EnablePost, $FoxDisplayFmt, $InputValues; $counter = 0; $tcount = count($to); if($tcount==0) FoxAbort($pagename, "$[Error: no target specified!]"); //process all target pages ($to target array built by function FoxTargetList) // key $tn is targetname, value $tacts is array of action parameter sets foreach ($to as $tn => $tacts) { if ($tn=='') FoxAbort($pagename, "$[Error: no target specified or target not found!]"); StopWatch('FoxProcessTargets: begin $tn'); //echo $counter; //process fox action sets for each target page // tg is array of parameters for target action, idx is index number of action in process foreach ($tacts as $idx => $tg) { //init $tg['t_count'] = $tcount; $tg['t_idx'] = $counter; //set a target page index, used by FoxValue to get $act = $tg['foxaction']; //DEBUG if($FoxDebug>1) echo "<br/>$tn ACTION $idx>".$act." "; if($FoxDebug==4) show($tg, 'targ'); //check target page permission for the fox action and set $permit flag $permit = FoxPagePermission($pagename, $act, $tn, $fx); //email notify only, no text save to target. Needs foxnotify.php installed. //target is page with list of email addresses. template is page with email template. //email list target needs to be in FoxNotifyListsGroup if ($act == 'mail') { if(function_exists('FoxNotifyUpdate')) { global $FoxNotifyLists, $FoxNotifyListsGroup, $FoxNotifyTemplatePageFmt; if (PageVar($tn, '$Group') != $FoxNotifyListsGroup) $FoxMsgFmt[] = "Error: Target list is not in the group for FoxNotifyLists"; else { $IsPagePosted = 1; $FoxNotifyTemplatePageFmt = MakePageName($pagename, $tg['template']); $FoxNotifyLists[] = $FoxNotifyListsGroup.".".PageVar($tn, '$Name'); array_splice($EditFunctions, array_search('FoxPostNotify', $EditFunctions ), 1); //remove from EditFunctions, otherwise possible multiple mailings #FoxNotifyUpdate($pagename, getcwd(), $fx, $tg); //use register_shutdown_function, to do mailing at end $counter++; if (isset($tg['foxsuccess'])) $FoxMsgFmt[] = $tg['foxsuccess']; elseif (isset($fx['foxsuccess'])) $FoxMsgFmt[] = $fx['foxsuccess']; elseif ($EnableFoxDefaultMsg==1) $FoxMsgFmt[] = "$[Successful post to] [[$tn(?action=browse)]]"; register_shutdown_function('FoxNotifyUpdate', $pagename, getcwd(), $fx, $tg); } } else $FoxMsgFmt[] = "Error: Could not send mail. FoxNotify is not installed!"; continue 2; //next target page } //get template, skip for 'ptv' if ($act!='ptv') { $template = FoxLoadTemplate($pagename, $fx, $tg); if($FoxDebug>4) echo "<pre><b>TEMPL=</b><br/>".$template."</pre>";//DEBUG// //do var replacements on template, skip for 'copy' if ($template && $act!='copy') $template = FoxTemplateEngine($tn, $template, $fx, $tg, '','FoxProcessTargets'); } //display only, no text save to target. Needs (:foxdisplay...:) as page location for output if ($act == 'display') { $FoxDisplayFmt = $template; } //preview for foxedit if (isset($fx['preview']) && isset($_SESSION['foxedit'][$pagename])) { $FoxDisplayFmt = ''; if($template && ($act=='add' || $act=='replace')) { $InputValues['text'] = $template; $FoxDisplayFmt = $template; } if(function_exists(FoxHandleEdit)) FoxHandleEdit($pagename); continue 2; //next target page } //newedit if ($act == 'newedit') { FoxNewEdit($pagename, $template, $fx, $tg); //ends process } //add, replace, ptv, copy: load page file for saving if ($act=='add' || $act=='replace' || $act=='ptv' || $act=='copy') { //open page for first of foxactions if ($idx==0) { Lock(2); $page = RetrieveAuthPage($tn, $FoxAuth, true); //DEBUG if($FoxDebug) echo "<i><b>LOAD PAGE</b></i> $tn <br/>"; if (!$page) Abort("?cannot read $pagename"); $pagetext = $text = isset($page['text']) ? $page['text'] : ''; $pagetext = trim($pagetext); } //end open page $toptxt = $bottxt = ''; //extract anchored sections if (strstr($tg['fulltarget'],'#')) { $alltext = $text; $section = FoxTextSection($text, $tg['fulltarget']); //break off this page process if specified target section is not found if (!empty($section)) { $text = $section['text']; $tpos = $section['pos']; $toptxt = substr($alltext, 0, $tpos); $bottxt = substr($alltext, $tpos + strlen($text)); } else { $FoxMsgFmt[] = "$[Error: could not find target section on] $tn"; $permit = false; //do not allow text modification, as we have no target section } } $text = trim($text); //modify the text if ($permit == true) switch ($act) { case 'add' : $text = FoxAddText( $pagename, $text, $template, $fx, $tg ); break; case 'replace': $text = FoxReplaceText( $pagename, $text, $template, $fx, $tg ); break; case 'ptv': $text = FoxPTVAddUpdate($pagename, $text, $fx, $tg ); break; case 'copy' : $text = "\n".$template."\n"; break; } $text = trim($text); //recombine text sections if (!$toptxt=='') $text = trim($toptxt)."\n".$text; if (!$bottxt=='') $text .= "\n".trim($bottxt); } //end add, replace, ptv, copy //save target page after last of foxactions if ($idx==count($tacts)-1) { //DEBUG if($FoxDebug) echo "<br/><i><b>SAVE PAGE</b></i> $tn <br/>"; //if we got changes, save page if ($text!=$pagetext) { $new = $page; $new['text'] = rtrim($text); //reduce $EditFunctions if ($act == 'copy') $EditFunctions = array('SaveAttributes','PostPage','PostRecentChanges'); else { SDVA($FoxExcludeEditFunctions, array('MergeSimulEdits','EditTemplate','RestorePage', 'AutoCreateTargets','PreviewPage','RequireAuthor')); foreach($EditFunctions as $k => $fn) if (in_array($fn, $FoxExcludeEditFunctions)) unset($EditFunctions[$k]); } //abort process if $FoxProcessTimeMax is exceeded, to avoid php fatal error on timeout. FoxTimer($pagename, "FoxProcessTargets: $tn"); $IsPagePosted = 0; $new['csum'] = $ChangeSummary; if ($ChangeSummary) $new["csum:$Now"] = $ChangeSummary; if (@$fx['foxnosave']!=1 && @$fx['foxnosave']!='on') $IsPagePosted = UpdatePage($tn, $page, $new, $EditFunctions); } Lock(0); if ($IsPagePosted==1) { $counter++; if (isset($tg['foxsuccess'])) $FoxMsgFmt[] = $tg['foxsuccess']; elseif (isset($fx['foxsuccess'])) $FoxMsgFmt[] = $fx['foxsuccess']; elseif ($EnableFoxDefaultMsg==1) $FoxMsgFmt[] = "$[Successful post to] [[$tn(?action=browse)]]"; } else { if (isset($tg['foxfailure'])) $FoxMsgFmt[] = $tg['foxfailure']; elseif (isset($fx['foxfailure'])) $FoxMsgFmt[] = $fx['foxfailure']; elseif($EnableFoxDefaultMsg==1) $FoxMsgFmt[] = "$[Nothing posted to] $tn "; } unset($IsPagePosted); } //end save target page after last foxaction } //end of foxaction process loop (for one page) } //end of page process loop return $counter; } //}}} ## get template from template page, #section, or (:foxtemplate 'string':) function FoxLoadTemplate($pagename, $fx, $tg) { $tplname = (isset($tg['template']))? $tg['template'] : ''; global $FoxDebug; if($FoxDebug) echo " FoxLoadTemplate> ".$tplname."<br>"; //DEBUG// switch($tplname) { //first check if no template is wanted case '0' : return ''; case 'foxtemplate' : if ($fx['foxtemplate']=='' || $fx['foxtemplate']=='NULL' ) return ''; else return Fox_htmlspecialchars_decode($fx['foxtemplate']); default : //check if foxtemplate is set as array if (isset($tg['foxtemplate']) && $tg['foxtemplate']!='') return Fox_htmlspecialchars_decode($tg['foxtemplate']); //if tplname starts with # assume template is section on current page if ($tplname[0]=='#') $tplname = $pagename.$tplname; $tplpage = MakePageName($pagename, $tplname); if ($tplpage) $page = ReadPage($tplpage, READPAGE_CURRENT); //TextSection will process any section passed, or the whole page if (isset($page['text'])) $template = trim(TextSection($page['text'], $tplname),"\r\n"); } if (!isset($template) && $tplpage) FoxAbort($pagename, "$[Error: Template page] $tplname $[is missing!]"); if (!isset($template)) FoxAbort($pagename, "$[Error: Template is missing!]"); return $template; } //}}} ## add processed template text at position defined with put, foxmark or mark function FoxAddText( $pn, $text, $template, $fx, $tg ) { global $FoxMsgFmt, $FoxDebug; if($FoxDebug) echo " FoxAddText>"; //DEBUG// //get array with mark & form positions $ms = FoxSetMarks($pn, $text, $fx, $tg); $mark = (isset($ms[0]['mark']) ? $ms[0]['mark'] : ''); $pre = $aft = ''; $err=0; //calculate section position and length switch ($ms['put']) { case '#top' : //legacy, next case 'top' : $pos = 0; $aft = "\n"; break; case '#bottom' : //legacy, next case 'bottom': $pos = strlen($text); $pre = "\n"; break; case 'above' : if (!isset($ms['Mpos'])) { $FoxMsgFmt[] = "$[Error: Found no mark to add above!]"; return $text; } else { $pos = $ms['Mpos']; $aft = "\n"; break; } case 'below' : if (!isset($ms['Mpos'])) { $FoxMsgFmt[] = "$[Error: Found no mark to add below!]"; return $text; } else { $pos = $ms['Mpos'] + $ms['Mlen']; $pre = "\n"; break; } case '#append' : //legacy, next case 'aboveform': if (!isset($ms['Fpos'])) { $FoxMsgFmt[] = "$[Error: Found no form to add above!]"; return $text; } else { $pos = $ms['Fpos']; $aft = "\n"; break; } case '#prepend' : //legacy, next case 'belowform': if (!isset($ms['Fpos'])) { $FoxMsgFmt[] = "$[Error: Found no form to add below!]"; return $text; } else { $pos = $ms['Fpos'] + $ms['Flen']; $pre = "\n"; break; } case 'insert' : if (!isset($ms['Mpos'])) { $FoxMsgFmt[] = "$[Error: Found no mark to insert after!]"; return $text; } else { $pos = $ms['Mpos'] + $ms['Mlen']; break; } case 'insertbefore' : if (!isset($ms['Mpos'])) { $FoxMsgFmt[] = "$[Error: Found no mark to insert before!]"; return $text; } else { $pos = $ms['Mpos']; break; } default: $FoxMsgFmt[] = "$[Error:] '{$ms['put']}' $[is not a valid option with 'add'!] "; return $text; } //DEBUG// if($FoxDebug>4) echo "<pre><b>ADD NEW TEXT=</b><br/>".$template."</pre>";//DEBUG// //add line breaks as needed $temp = $pre.$template.$aft; // do string insert or repacement $text = substr_replace($text, $temp, $pos, 0); //DEBUG// if($FoxDebug>5) echo "<pre><b>FULL TEXT=</b><br/>".$text."</pre>";//DEBUG// return $text; } //}}} ## replace text with processed template text at position defined by put, or mark and endmark function FoxReplaceText( $pn, $text, $template, $fx, $tg ) { global $FoxDebug, $FoxMsgFmt; if($FoxDebug) echo " FoxReplaceText>"; //DEBUG// //get array with mark & form positions $ms = FoxSetMarks($pn, $text, $fx, $tg); $mark = (isset($ms[0]['mark']) ? $ms[0]['mark'] : ''); switch ($ms['put']) { case 'string' : if (!isset($ms['Mpos'])) { $FoxMsgFmt[]="$[Error: No string to find!]"; break; } else $text = substr_replace($text, $template, $ms['Mpos'], $ms['Mlen']); break; case 'all': $tlen = strlen($template); $i = 0; $icnt = count($ms[0]['pos']); while($i < $icnt) { $ipos = $ms[0]['pos'][$i] + $i*($tlen-$ms[0]['len']); $text = substr_replace($text, $template, $ipos, $ms[0]['len']); $i++; } break; case 'allplus' : $text = str_replace($mark, $template, $text); break; case 'regex' : $text = preg_replace("/$mark/", $template, $text); break; case 'marktomark': if ( $ms['Npos'] < $ms['Mpos'] ) {$FoxMsgFmt[]="$[Error: could not find endmark!]"; break;} else { $ipos = $ms['Mpos'] + $ms['Mlen']; $ilen = $ms['Npos'] - $ipos; $text = substr_replace($text, $template, $ipos, $ilen); break; } case 'overwrite' : $text = $template; break; default: $FoxMsgFmt[] = "$[Error:] '{$ms['put']}' $[is not a valid option with 'replace'!] "; } //DEBUG// if($FoxDebug>5) echo "<pre>FULL TEXT=".$text."</pre>";//DEBUG// return $text; } //}}} ## claculate and set mark positions, excluding positions in fox forms and overlappings function FoxSetMarks($pn, $text, $fx, $tg) { global $FoxDebug; if($FoxDebug>6) echo "<br>FoxSetMarks>"; //DEBUG// $ms = array(); $mk = array(); $ms['put'] = $tg['put']; $mk[0] = isset($tg['mark']) ? $tg['mark'] : ''; $mk[1] = isset($tg['endmark']) ? $tg['endmark'] : ''; $formname = $fx['foxname']; //set foxmark if (isset($fx['foxmark'])) $foxmark = " ".$fx['foxmark']; elseif (isset($fx['foxplace'])) $foxmark = " ".$fx['foxplace']; //legacy keyword else $foxmark = ''; //check for foxmarks, it overrides any other put setting $foxmarks = array( "(:foxappend {$formname}{$foxmark}:)" => 'above', "(:foxprepend {$formname}{$foxmark}:)" => 'below'); foreach($foxmarks as $pat=>$v) if (strpos($text, $pat)) { $ms['put'] = $v; $mk[0] = $pat; break; } //DEBUG// if($FoxDebug>6) echo " MARK=".$mk[0]." PLACE=".$ms['put'];//DEBUG// #if ($mk[0]=='') return $ms; //calculate any mark positions $marks = array(); foreach($mk as $i => $m) { $ms[$i]['mark'] = $m; $ms[$i]['len'] = strlen($m); if ($mk[$i]=='') continue; $mv = array(); $pat = preg_quote( $mk[$i],'/'); if(preg_match_all("/$pat/", $text, $match, PREG_OFFSET_CAPTURE)) { foreach($match[0] as $k => $mark) { $mv[$k] = array( $mark[1], $mark[1]+strlen($mark[0]) ); } } $marks[$i] = $mv; } //DEBUG// if($FoxDebug>6) { echo "<pre>\$fx mv "; print_r($mv); echo "\$marks "; print_r($marks); echo "</pre>"; } //get any form positions and exclude marks found in any forms if (preg_match_all("/(\\(:fox\\s+([\\w]+)(?: *\\n)?)(.*?)(\\(:foxend \\2:\\))/s", $text, $matches, PREG_OFFSET_CAPTURE)) { $forms = array(); foreach((array)$matches[0] as $i => $frm ) { //build forms array: [0] = form start pos, [1] = form end pos $forms[$i][0] = $frm[1]; $forms[$i][1] = $frm[1] + strlen($frm[0]); //calculate form position of calling form if ($formname == $matches[2][$i][0]) { $ms['Fpos'] = $frm[1]; $ms['Flen'] = strlen($frm[0]); } } // add end-of-text dummy to help calculate positions near end of text $txe = strlen($text)+1; $forms[] = array( $txe, $txe); } //calculate all positions outside forms if(!isset($forms)) $forms = ''; foreach($marks as $m=>$mm) { $mk = FoxExcludeFormPos($mm, $forms); foreach ($mk as $i=>$item) { $ms[$m]['pos'][$i] = $item[0]; } } //exclude formoverlapping mark to endmark positions if(isset($ms[1]['pos'])) { foreach($ms[0]['pos'] as $m=>$mm) { foreach($ms[1]['pos'] as $n=>$nn) { if ($mm>$nn) continue; $nms[] = array($mm,$nn); } } $mk2 = FoxExcludeFormPos($nms, $forms); foreach ($mk2 as $i=>$item) { $ms[0]['pos'][$i] = $item[0]; $ms[1]['pos'][$i] = $item[1]; } } //set mark position if (isset($ms[0]['pos'][0])) { $ms['Mpos'] = $ms[0]['pos'][0]; $ms['Mlen'] = $ms[0]['len']; } //set endmark position if (isset($ms[1]['pos'])) { foreach($ms[1]['pos'] as $i => $v) { $ms['Npos'] = $v; break; } $ms['Nlen'] = $ms[1]['len']; } $ms['Tlen'] = strlen($text); //DEBUG// if($FoxDebug>6) { echo "<pre>\$ms "; print_r($ms); echo "</pre>"; } return $ms; } //}}} ## exclude mark positions inside fox forms function FoxExcludeFormPos($marks, $forms) { $mark = array(); foreach($marks as $k=>$m) { if ($forms=='') { $mark[$k] = array($m[0],$m[1]); continue; } foreach($forms as $d=>$f) { if ($f[0]>$m[0] && $f[0]<$m[1]) continue 2; if ($m[1]<$f[0]) { $mark[] = array($m[0],$m[1]); continue 2; } if ($m[1]<$f[1]) continue 2; } } return $mark; } //}}} ## modified function TextSection to return section and position as well function FoxTextSection($text, $sections, $args = NULL) { $args = (array)$args; $npat = '[[:alpha:]][-\\w*]*'; if (!preg_match("/#($npat)?(\\.\\.)?(#($npat)?)?/", $sections, $match)) return $text; @list($x, $aa, $dots, $b, $bb) = $match; if (!$dots && !$b) $bb = $npat; if ($aa) { $pos = strpos($text, "[[#$aa]]"); if ($pos === false) return false; if (@$args['anchors']) while ($pos > 0 && $text[$pos-1] != "\n") $pos--; else $pos += strlen("[[#$aa]]"); $text = substr($text, $pos); } if ($bb) { $text = preg_replace("/(\n)[^\n]*\\[\\[#$bb\\]\\].*$/s", '$1', $text, 1); } $tsections = array('text' => $text, 'pos' => $pos); return $tsections; } //}}} ## variable substitutions of template ## see also notes on FoxDocumentation/TemplateMarkup function FoxTemplateEngine($pn, $template, $fx, $tg, $linekeyseed=NULL, $caller=NULL) { global $FoxDebug; if($FoxDebug) echo " FoxTemplateEngine> "; //DEBUG// global $EnablePostDirectives, $FoxFxSafeKeys; if($template=="") return ''; // create the data to be added, from template and variables $string = $template; // handle the {$$name[]} variables. $result = array(); $parts = explode('{[foxsection]}',$string); foreach($parts as $section) { //find all occurences of {$$name[]} if( preg_match_all('/\\{\\$\\$([A-Za-z][-_:.\\w]*)\\[\\]\\}/',$section, $matches)) { $names = array_unique($matches[1]); $max = 0; $keys = array(); $vals = array(); foreach($names as $i=>$var) { //get value $val = (array)$fx[$var]; $max = max($max,count($val)); $keys[$i] = '{$$'.$var.'[]}'; $vals[$i] = $val; } $reps = array(); for($i=0; $i < $max; $i++) { foreach((array)$vals as $k=>$val) $reps[$i][$k] = $val[$i]; } //if more than one target page, map vars to target index if ($tg['t_count']>1) { $result[] = str_replace($keys, $reps[$tg['t_idx']], $section); //for one target build repeated sections } else for ($i=0; $i < $max; $i++) { $result[] = str_replace($keys,$reps[$i],$section); } } else $result[] = $section; } // replace {$$var}, {$$var[num]} and {$$(func...)} markup. $result = FoxTemplateVarReplace($pn, $fx, $tg, $result); //replace {$$$...} with {$$...} for posting of forms with replacement vars $result = str_replace('{$$$','{$$',$result); //replace {=$$....} and {PageName=$$...} with {=$....} and {PageName=$...} for template posting $result = preg_replace('/(\\{\\*|!?[-\\w.\\/\\x80-\\xff]*)=\\$(\\$:?\\w+\\})/',"$1=$2",$result); //replace \n by newlines $result = preg_replace('/\\\\n/',"\n",$result); //create a unique linekeyseed, if necessary if ($linekeyseed==NULL) { $time1 = date('ymd-His', time() - date('Z')); $linekeyseed = $time1.'-'.rand(0,100000); #$linekeyseed = time().'a'.rand(0,100000); } foreach ($result as $index => $entry) { //skip if delete link already exists if (preg_match("/\\{\\[foxdel([^]]+)FullName\\}\\s*\\]\\}/", $entry)) continue; $linekey = $linekeyseed.''.$index; //adding linekey + pagename to any foxdelete markup for unique id if (preg_match("/button/", $entry)) { // Add linekey to delete button for line delete $entry = str_replace( '{[foxdelline button', "{[foxdelline button $linekey {\$FullName} ", $entry ); // Add linekey to delete button for range delete $entry = str_replace( '{[foxdelrange button', "{[foxdelrange button $linekey {\$FullName} ", $entry ); } else { // Add linekey to delete link for line delete $entry = str_replace( '{[foxdelline', "{[foxdelline $linekey {\$FullName} ", $entry ); // Add linekey to delete link for range delete $entry = str_replace( '{[foxdelrange', "{[foxdelrange $linekey {\$FullName} ", $entry ); } //Add line-key to delete range begin marker $entry = str_replace( '#foxbegin#', "#foxbegin $linekey#", $entry ); // Add line-key to delete range end marker $entry = str_replace( '#foxend#', "#foxend $linekey#", $entry ); $result[$index] = $entry; } return implode("\n",$result); } //}}} ## fields to be ignored in initial variable replacements SDVA($FoxFxSafeKeys, array( 'n','foxpage','action','foxaction','foxname','post', 'put', 'foxfields',':foxaction',':fulltarget',':put',':foxfields', 'foxtemplate', 'foxpreviewtemplate', 'foxdisplaytemplate', )); ## input field var replacements, exclude fields we know are not variables function FoxInputVarReplace($pn, &$fx) { global $FoxDebug; if($FoxDebug) echo "<br/> INPUT-VarRep> ";//DEBUG// global $FoxFxSafeKeys; $fx_check = $fx; foreach ($fx_check as $val) { foreach ($FoxFxSafeKeys as $key) { if (array_key_exists($key, $fx_check)) { unset($fx_check[$key]); } } } foreach($fx_check as $key => $value) { if(is_array($value)) { foreach($value as $i=>$val) { if (strstr($val, '{$$')) { if($FoxDebug>3) echo "<pre>IN-arr> ".$key."[".$i."] = ".$val."</pre>";//DEBUG// $fx[$key][$i] = FoxVarReplace($pn,$fx,'',$val); } } } else if (strstr($value, '{$$')) { if($FoxDebug>3) echo "<pre>IN-str> ".$key." = ".$value."</pre>";//DEBUG// $fx[$key] = FoxVarReplace($pn,$fx,'',$value); } } } //}}} ## replace any {$$var} or {$$(func...)} in $arg using values from $fx function FoxTemplateVarReplace($pn, $fx, $tg, $args) { global $FoxDebug; if($FoxDebug) echo "<br /> TEMPLATE-VarRep> "; //DEBUG// if( is_array($args) ) { $data = array( 'more' => false, 'pn' => $pn, 'fx' => $fx, 'tg' => $tg ); array_walk_recursive( $args, 'FoxVarRepRecursive' , $data ); } else FoxVarReplace($pn, $fx, $tg, $args); return $args; } //}}} function FoxVarRepRecursive(&$v, $k, $d) { global $FoxMaxIterations,$FoxDebug; if($FoxDebug>4) echo "FoxVarRepRecursive>"; if ($d['tg']=='') return; SDV($FoxMaxIterations, 100); static $cnt = 0; FoxVarReplace($d['pn'], $d['fx'], $d['tg'], $v); $cnt++; $maxcnt = $FoxMaxIterations + $d['tg']['t_idx']; if( $cnt >= $maxcnt ) FoxAbort( $d['fx']['foxpage'], "$[Error: max iterations exceeded while replacing variables!]" ); } //}}} ## replaces variables by checking pattern and if success returns value function FoxVarReplace($pn, $fx, $tg, &$str) { global $Now, $FoxDebug; if($FoxDebug>4) echo "<pre><b>VREP=</b><br/>".$str."</pre>"; //DEBUG// # {$$:ptv} $str = preg_replace('/\\$\\$:/', '$$ptv_', $str); # {$$var} $str = preg_replace_callback('/\\{\\$\\$([a-z][-_\\w]*)\\}/i', function($m) use($fx,$tg) { return FoxValue($fx,$tg,$m[0],$m[1]);}, $str); # {$$var[]} $str = preg_replace_callback('/\\{\\$\\$([a-z][-_\\w]*)\\[\\s*([a-z0-9]+)\\s*\\]\\}/i', function($m) use($fx,$tg) { return FoxValue($fx,$tg,$m[0],$m[1],$m[2]);}, $str); # {=$pagevar} $str = preg_replace_callback('/\\{(\\*|!?[-\\w.\\/\\x80-\\xff]*)\\=(\\$:?\\w+)\\}/', function($m) use($pn) { return PVSE(PageVar($pn, $m[2], $m[1]));}, $str); # {$$(date)} $str = preg_replace_callback('/\\{\\$\\$\\(date[:\\s]+(.*?)\\)\\}/', function($m) { return date($m[1]);}, $str); # {$$(timestamp)} $str = preg_replace('/\\{\\$\\$\\(timestamp\\)\\}/', $Now, $str); # {$$(expr)} $str = preg_replace_callback('/\\{\\$\\$(\\(\\w+\\b.*?\\))\\}/', function($m) use($pn) { return MarkupExpression($pn, $m[1]);}, $str); if($FoxDebug>4) echo "<pre><b>VNEW=</b><br/>".$str."</pre>"; //DEBUG// return $str; } //}}} # looks up a field name and returns its value for {$$field} and {$$field[num]} # either strings or arrays. The final optional parameter is the value of num. If # num isn't given, and the field is an array, it uses the value of $tg['t_idx'] # (index of targetpage process) if it exists, or else 0. # In the InputVarReplace process if the field is an array, a comma-separated list of # the array elements will be returned (not just the first array element) function FoxValue($fx, $tg, $fullvar, $var, $idx=NULL) { global $FoxDebug; if($FoxDebug>2) { if(is_null($idx)) echo "<pre>VALUE(".$var.")="; else echo "<pre>VALUE(".$var."[".$index."])=";}//DEBUG// $fti = 'none'; if ($tg && $tg['t_count']>1) $fti = $tg['t_idx']; if (array_key_exists($var, $fx) ) { $val = $fx[$var]; if(is_array($val) ) { if ($fti==='none' && is_null($idx)) $val = implode(',',$val); elseif (is_null($idx) ) $val = $val[$fti]; else $val = $val[$idx]; } //DEBUG// if($GLOBALS['FoxDebug']>2) { if (is_null($idx) && $fti==='none') echo "<pre>VALUE(".$var.")=".$val."</pre>"; else if (is_null($idx)) echo "<pre>VALUE(".$var."[".$fti."])=".$val."</pre>"; else echo "<pre>VALUE(".$var."[".$idx."])=".$val."</pre>"; } //DEBUG return $val; } //var is no key name: if action 'add' return empty, otherwise full var string if(isset($tg['foxaction']) && $tg['foxaction']=='add') $fullvar = ''; return $fullvar; } //}}} ## get arguments from POST or GET function FoxRequestArgs ($fx = NULL) { if (is_null($fx)) $fx = array_merge($_GET, $_POST); foreach ($fx as $key=>$val) { if(is_array($val)) foreach($val as $k=>$v) { $fx[$key][$k] = str_replace("\r",'',stripmagic($v)); } else $fx[$key] = str_replace("\r",'',stripmagic($val)); } return $fx; } //}}} ## call external filter functions SDV($FoxFilterFunctions, array()); function FoxFilter($pagename, &$fx) { global $FoxDebug; if($FoxDebug) echo " FoxFilter> "; //DEBUG// global $FoxFilterFunctions; //get filter keynames $fx['foxfilter'] = preg_split("/[\s,|]+/", $fx['foxfilter'], -1, PREG_SPLIT_NO_EMPTY); foreach($fx['foxfilter'] as $f) { $ffn = $FoxFilterFunctions[$f]; if (function_exists($ffn) ) { // use specific filter if(is_callable($ffn, false, $callable_name)) { $fx = $callable_name($pagename, $fx); if(!$fx) Redirect($pagename); // Filter is telling us to abort; } } } } //}}} ## create NAME fields from ptv_NAME fields and add NAME to ptvfields array function FoxPTVFields($pagename, &$fx) { global $FoxDebug, $FoxPTVArraySeparator; if($FoxDebug) echo "<br/> FoxPTVFields><br/>"; //DEBUG// //strip ptv_ and make name fields if(isset($fx['ptvfields'])) $fx['ptvfields'] = explode(",",$fx['ptvfields']); foreach($fx as $n => $v) { if(substr($n,0,4)=="ptv_") { $n = substr($n,4); $fx[$n] = $v; $fx['ptvfields'][] = $n; } } if(isset($fx['ptvfields']) && is_array($fx['ptvfields'])) $fx['ptvfields'] = array_unique($fx['ptvfields']); //flatten input values as arrays for ptvs if (is_array(@$fx['ptvfields'])) { foreach($fx['ptvfields'] as $n) { if (is_array($fx[$n])) { if($FoxDebug) echo "<pre>[".$n."] => "; foreach($fx[$n] as $k => $v) { if(preg_match('/[\s\']/',$v)) $fx[$n][$k] = '"'.$v.'"'; //doublequote text with spaces or single quotes } $fx[$n] = implode($FoxPTVArraySeparator, $fx[$n]); //array input gets converted to text if($FoxDebug) echo $fx[$n]."</pre>"; } } } } //}}} ## add & update page text variables function FoxPTVAddUpdate($pagename, $text, $fx, $tg ) { global $PageTextVarPatterns, $EnablePostDirectives, $FoxPTVArraySeparator, $FoxDebug, $FoxClearPTVFmt, $EnableFoxPTVDelete, $FoxPTVDeleteKey, $FoxMsgFmt; if ($FoxDebug) echo " FoxPTVAddUpdate>"; //DEBUG// //PTVs to check if ($tg['ptvfields']) { $ptvs = (is_array($tg['ptvfields'])) ? $tg['ptvfields'] : explode(',', $tg['ptvfields']); foreach ($ptvs as $n) $update[$n] = $fx[$n]; //use ptvfields list to check for PTVs } else $update = $fx; //use all fields to check for PTVs $ptvclear = (isset($tg['ptvclear'])) ? explode(",", $tg['ptvclear']) : array(); //array of input fields which will clear PTVs if empty $pageptvs = array(); //to build array of PTV names in page //look through PTV patterns and replace matches foreach ($PageTextVarPatterns as $pat) { if (!preg_match_all($pat, $text, $match, PREG_SET_ORDER)) continue; foreach ($match as $m) //$m[0]=all, $m[1]=beforevalue, $m[2]=name, $m[3]=value, $m[4]=aftervalue { $k = $pageptvs[] = $m[2]; if (!array_key_exists($k, $update)) continue; $v = (isset($update[$k])) ? $update[$k] : ''; //new value if ($m[3]==$v) continue; //no change on this PTV if ($v=='' && !($ptvclear[0]==1 || in_array($k, $ptvclear))) continue; //empty input gets ignored, unless ptvclear is set to 1 or to ptv field names if ($v==$FoxClearPTVFmt) $v = ''; // 'NULL' or other special string clears ptv if (is_array($v)) $v = implode($FoxPTVArraySeparator, $v); //array input gets converted to text if ($EnablePostDirectives==false) $v = FoxDefuseItem($v);//prevent posting of directives & markup expressions if ($FoxDebug>4) echo "<pre> ".$k."=>".$v."</pre>"; //new ptv name=value if (!preg_match('/s[eimu]*$/', $pat)) $v = str_replace("\n", ' ', $v); //for any inline pattern replace newlines with spaces if (strstr($m[4],'[[#')) { $v = trim($v); $m[4] = "\n".$m[4];} //preserve linebreak before ending anchor if ($EnableFoxPTVDelete==1 && $v==$FoxPTVDeleteKey) $text = str_replace($m[0], '', $text);//erasing ptv else $text = str_replace($m[0], $m[1].$v.$m[4], $text); //update ptv in text $GLOBALS['PCache'][$tg['target']]['=p_'.$k] = $v; //update ptv in $PCache } } //add any new ptvs named in ptvfields and do not exist in ptvtarget page if ($ptvs) { $ptvfmt = isset($tg['ptvfmt']) ? $tg['ptvfmt'] : 'hidden'; $newptvs = array_diff($ptvs, $pageptvs ); foreach ($newptvs as $k) { $v = $update[$k]; if (is_array($v)) $v = implode($FoxPTVArraySeparator,$v); switch ($ptvfmt) { case 'text' : $text = $text."\n$k: $v\n"; break; //add as text: val case 'deflist' : $text = $text."\n: $k : $v\n"; break; //add as definition list case 'section' : $text = $text."\n[[#".$k."]]\n$v\n[[#".$k."end]]\n"; break; //add as anchor section case 'extra' : $text = $text."\n(::$k:\n$v\n::)\n"; break; //add as extra hidden PTV case 'hidden' : $text = $text."\n(:$k: $v:)\n"; break; //add as hidden PTV default : $FoxMsgFmt[] = "$[Error: cannot recognise PTV format] $ptvfmt"; } $GLOBALS['PCache'][$tg['target']]['=p_'.$k] = $v; //add ptv to $PCache } } return $text; } //}}} ## check page posting permissions function FoxPagePermission($pagename, $act, $tn, $fx) { global $FoxDebug; if($FoxDebug) echo "<br/> FoxPagePermission for <i>$act</i>> $tn <br>"; global $FoxMsgFmt, $FoxConfigPageFmt, $FoxPagePermissions; if ($act=='display') return true; //display is allowed if (!$act) { $FoxMsgFmt[] = "ERROR ($tn): Unknown action: $act . Cannot proceed!"; return;} // get name patterns from FoxConfig page $Name = PageVar($pagename, '$Name'); $Group = PageVar($pagename, '$Group'); $config = FmtPageName($FoxConfigPageFmt, $pagename); if (PageExists($config)) { $cfpage = ReadPage($config, READPAGE_CURRENT); if ($cfpage) { $text = $cfpage['text']; if(preg_match_all("/^\\s*([\\*\\w][^\\s:]*):\\s*(.*)/m", $text, $matches, PREG_SET_ORDER)) foreach($matches as $m) $FoxPagePermissions[$m[1]] = $m[2]; } } //show($FoxPagePermissions, 'PagePermissions'); // name check for $act against $FoxPagePermissions $pnames = array(); if(is_array($FoxPagePermissions)) foreach($FoxPagePermissions as $n => $t) { if(strstr($t,'-'.$act)||strstr($t,'none')) { $pnames[$n]='-'.$n; continue; } if(strstr($t,$act)||strstr($t,'all')) $pnames[$n]=$n; } $pnames = FmtPageName(implode(',',$pnames),$pagename); //if no permitted names exclude all pages if($pnames=='') $pnames = '-*.*'; $px = MatchPageNames($tn,$pnames); $namecheck = (boolean)MatchPageNames($tn,$pnames); // string check against string patterns $strcheck = 0; if(PageExists($tn)) { $page = ReadPage($tn, READPAGE_CURRENT); $strcheck = (boolean)( preg_match("/\\(:fox(prepend|append|allow)/", $page['text']) //allow on page which got special markup OR preg_match("/\\(:fox ".$fx['foxname']." /", $page['text']) ); //allow on page which got the form } if($namecheck==0 && $strcheck==0) { //if ($FoxDebug) echo "Permission denied to $act on $tn "; $FoxMsgFmt[] = "PERMISSION DENIED to $act on $tn!"; return false; } else return true; } //}}} ## check access code, captcha, new page exists, and required fields ## this runs before individual page processing function FoxSecurityCheck($pagename, $targets, &$fx) { global $FoxDebug; if($FoxDebug) echo "<br/>FoxSecurityCheck><br>"; global $FoxNameFmt, $FoxMsgFmt, $EnableAccessCode, $EnablePostCaptchaRequired, $EnablePost; //if preview if (isset($fx['preview'])) return ''; //if enabled check for access code match if($EnableAccessCode AND (!(isset($fx['access'])&&($fx['access']==$fx['accesscode'])))) { FoxAbort($pagename, "$[Error: Missing or wrong Access Code!]"); } //if enabled check for Captcha code (captcha.php is required) if($EnablePostCaptchaRequired AND !IsCaptcha()) { FoxAbort($pagename, "$[Error: Missing or wrong Captcha Code!]"); } //check pagecheck: if pagecheck page names exists already if(isset($fx['pagecheck'])) { $check = explode(',',$fx['pagecheck']); $stop = 0; // pagecheck=1 checks all target pages if($check[0]==1) $check = $targets; foreach ($check as $pt) { $page = MakePageName($pagename, $pt); if($pagename==$page) { $FoxMsgFmt[] = "$[Error: You are not allowed to post to this page!]"; $stop=1; continue;} if(PageExists($page) AND in_array($page, $targets)) { $FoxMsgFmt[] = "$[Page] [[$pt]] $[exists already. Please choose another page name!]"; $stop=1; continue;} } if ($stop==1) FoxAbort($pagename,""); } //check for 'post' and 'cancel' from submit button if ( !isset($fx['post']) AND !isset($fx['postdraft']) AND !isset($fx['cancel']) AND !isset($fx['preview']) ) { FoxAbort($pagename, "$[Error: No text or missing post!]"); } } //}}} ## defuse posting of directives (:...:) and expressions {(...)} by rendering as code ## check only relevant input fields function FoxDefuseMarkup($pagename, &$fx ) { global $EnablePostDirectives, $FoxFxSafeKeys; $fx_check = $fx; unset($fx_check['foxtemplate']); foreach ($fx_check as $val) { foreach ($FoxFxSafeKeys as $key) { if (array_key_exists($key, $fx_check)) { unset($fx_check[$key]); } } } array_walk_recursive( $fx_check, 'FoxDefuseItem' ); $fx = array_merge($fx, $fx_check); } //}}} ## defuse by rendering as code any markup directives and markup expressions function FoxDefuseItem( &$item ) { global $FoxDebug; if($FoxDebug>2) echo " DEFUSE> <code>$item</code>"; //DEBUG// if (is_array($item)) return $item; if (!preg_match("/\\(:|\\{\\(/", $item)) return $item; // render {(..)} and (:...:) as code by using HTML character codes $item = preg_replace("/\\{(\\(\\w+\\b.*?\\))\\}/", "{$1}", $item); $item = str_replace("(:", "(:", $item); $item = str_replace(":)", ":)", $item); //undo for markup directives wrapped in [@...@] or [=...=] if (preg_match_all("/(\\[[@|=])[^\\]]*(\\(:)(.*?[@|=]\\])/s", $item, $mp)) { foreach($mp[0] as $i => $v) { $v = str_replace("(:","(:",$v); $v = str_replace(":)",":)",$v); $item = str_replace( $mp[0][$i], $v, $item); } } //undo for markup expressions wrapped in [@...@] or [=...=] if (preg_match_all("/(\\[[@|=])[^\\]]*({\\(.*?\\)})(.*?[@|=]\\])/s", $item, $mp)) { foreach($mp[0] as $i => $v) { $v = str_replace("{(","{(",$v); $v = str_replace(")}",")}",$v); $item = str_replace( $mp[0][$i], $v, $item); } } } //}}} ## make a WikiWord out of a string function FoxWikiWord($str) { global $FoxDebug; if($FoxDebug) echo " FoxWikiWord> "; //DEBUG// global $MakePageNamePatterns; $str = preg_replace('/[#?].*$/', '', $str); $nm = $str; foreach($MakePageNamePatterns as $pat => $rep) { if(is_callable($rep) && $rep != '_') $nm = preg_replace_callback($pat,$rep,$nm); else $nm = preg_replace($pat,$rep,$nm); } return $nm; } //}}} ## newedit opens page in the edit form, it can only run as last page process function FoxNewEdit($pagename, $template, $fx, $tg) { global $FoxDebug; if($FoxDebug) echo " FoxNewEdit> "; //DEBUG// if(PageExists($tg['target'])) Redirect($tg['target']); //jump to existing page $urlfmt = '$PageUrl?action=edit'; if ($template) { //merging fields and template, put into Session var for use with ?action=edit&foxtemptext=1 @session_start(); $_SESSION["FoxTempPageText"] = $template; //add special template marker before redirecting to edit $urlfmt.= '&foxtemptext=1'; } Redirect($tg['target'], $urlfmt); // open new page to edit } //}}} ## upload files function FoxPostUpload($pagename, $fx, $auth='upload') { global $FoxDebug; if ($FoxDebug) echo " FoxPostUpload>"; global $UploadVerifyFunction, $UploadDir, $UploadPrefix, $UploadPrefixFmt, $LastModFile, $EnableUploadVersions, $Now, $FoxMsgFmt, $FmtV; $uptarget = $fx['uptarget']; if (function_exists('MakeUploadPrefix')) $upprefix = MakeUploadPrefix($uptarget); else $upprefix = FmtPageName("$UploadPrefixFmt", $uptarget); $dirpath = $UploadDir.$upprefix; $page = RetrieveAuthPage($uptarget, $auth, true, READPAGE_CURRENT); if (!$page) FoxAbort($pagename, "?cannot upload to $uptarget"); foreach($_FILES as $n => $upfile) { $upname = $upfile['name']; if ($upname=='') continue; // check for new upload filename if ($fx[$n.'_name']) $upname = $fx[$n.'_name']; $upname = MakeUploadName($uptarget, $upname); if (!function_exists($UploadVerifyFunction)) FoxAbort($pagename, '?no UploadVerifyFunction available'); $filepath = $dirpath.'/'.$upname; $result = $UploadVerifyFunction($uptarget, $upfile, $filepath); if ($result=='') { $filedir = preg_replace('#/[^/]*$#','',$filepath); mkdirp($filedir); if (IsEnabled($EnableUploadVersions, 0)) @rename($filepath, "$filepath, $Now"); if (!move_uploaded_file($upfile['tmp_name'], $filepath)) { FoxAbort($pagename, "?cannot move uploaded file to $filepath"); return; } fixperms($filepath, 0444); if ($LastModFile) { touch($LastModFile); fixperms($LastModFile); } $result = "upresult=success"; } # process results for message $re = explode('&',substr($result,9)); # special cases: if($re[0]=='badtype' OR $re[0]=='toobigext') { global $upext, $upmax; $r1 = explode('=',$re[1]); $upext = $r1[1]; $r2 = explode('=',$re[2]); $upmax = $r2[1]; } $result = $re[0]; $FoxMsgFmt[] = "$[UL$result] $upname"; } } //}}} # last function FoxFinish($pagename, $fx, $msg) { StopWatch('FoxFinish start'); global $InputValues, $FoxMsgFmt; // wipe out input values, so there's no redisplay if (isset($fx['keepinput'])) { //keep values for selected input fields $keep = explode(',', $fx['keepinput']); if ($fx['keepinput']!=1) { foreach($InputValues as $i => $v) { if (in_array($i, $keep)) continue; unset($GLOBALS['InputValues'][$i]); } } } else //wipe all foreach($InputValues as $i => $v) unset($GLOBALS['InputValues'][$i]); HandleDispatch($pagename,'browse',$msg); exit; } //}}} ## abort by displaying error message and returning to page function FoxAbort($pagename, $msg) { global $InputValues, $FoxMsgFmt, $MessagesFmt; $FoxMsgFmt[] = $msg; $MessagesFmt[] = "<div class='wikimessage'>$msg</div>"; //legacy using (:messages:) markup HandleDispatch($pagename,'browse'); exit; } //}}} # FoxTimer aborts if $FoxProcessTimeMax is exceeded, returns process time, # sets entries in $StopWatch array, displayed with config settings: # $EnableDiag = 1; # $HTMLFooterFmt['stopwatch'] = 'function:StopWatchHTML 1'; //function is in scripts/diag.php function FoxTimer($pagename, $x) { global $FoxProcessTimeMax, $StopWatch; static $wstart = 0; $wtime = strtok(microtime(), ' ') + strtok(''); if (!$wstart) $wstart = $wtime; $wtime = $wtime-$wstart; $StopWatch[] = sprintf("%04.2f %s", $wtime, $x); $xtime = sprintf("%04.2f %s", $wtime, ''); if($xtime>$FoxProcessTimeMax) FoxAbort($pagename, "$[Error: processing stopped before maximum script timeout.] $[Page process:] $xtime sec"); return $xtime; } //}}} ## validation check for form input as set by (:foxcheck ... :) markup function FoxInputCheck($pagename, $fx) { global $FoxDebug; if($FoxDebug) echo " FoxInputCheck> "; //DEBUG// global $FoxCheckError, $FoxCheckErrorMsg, $FoxMsgFmt, $FoxClearPTVFmt; if (isset($fx['cancel'])) return ''; $check = array(); foreach($fx as $k => $ar) { if (substr($k,0,4)!='chk_') continue; if (!is_array($ar)) continue; foreach($ar as $i => $v) { $n = substr($k,4); // remove leading 'chk_' if ($v=='') continue; // set only non-empty values if ($n=='name') { $nms = explode(',',$v); foreach($nms as $j => $nm) $check[$i]['names'][$j] = $nm; } else $check[$i][$n] = $v; } } if($FoxDebug>4) show($check,'check'); $FoxCheckError = array(); foreach($check as $i => $opt) { foreach($opt['names'] as $n) { if ($opt['empty']==1 && ($fx[$n]=='' || $fx[$n]==$FoxClearPTVFmt)) continue; if (!isset($opt['match'])) $opt['match'] = "?*"; $pat = (isset($opt['regex'])) ? $opt['regex'] : ".+"; list($inclp, $exclp) = GlobToPCRE($opt['match']); if ( $inclp && !preg_match("/$inclp/is", $fx[$n]) || $exclp && preg_match("/$exclp/is", $fx[$n]) || !preg_match("/$pat/is", $fx[$n])) { $FoxMsgFmt[$n] = isset($opt['msg']) ? $opt['msg'] : "$[Invalid parameter:] $n"; $FoxCheckError[] = $n; } if (@$opt['if'] && !CondText($pagename, 'if '.$opt['if'], 'yes')) { $FoxMsgFmt[$n] = isset($opt['msg']) ? $opt['msg'] : "$[Input condition failed]"; $FoxCheckError[] = $n; } } } $errmsg = (isset($fx['foxcheckmsg'])) ? $fx['foxcheckmsg'] : $FoxCheckErrorMsg; //avoid abort or call to foxedit when preview if (isset($fx['preview'])) unset($FoxCheckError); if ($FoxCheckError) { if ($_SESSION['foxedit'][$pagename]) FoxHandleEdit($pagename); else FoxAbort($pagename, $errmsg); } } //}}} ## build javascript for simple validation that required fields have values function FoxJSFormCheck($formcheck) { $reqfields = preg_split("/[\s,|]+/", $formcheck, -1, PREG_SPLIT_NO_EMPTY); $out = " <script type='text/javascript' language='JavaScript1.2'><!-- function checkform ( form ) { "; foreach($reqfields as $required) { $out .= "if (form.$required && form.$required.value == \"\") { window.alert( 'Entry in field \"$required\" is required!' ); form.$required.focus(); return false ; } "; } $out .= "return true; } --></script>"; return $out; } //}}} ## provide {$AccessCode} page variable: $FmtPV['$AccessCode'] = rand(100, 999); ## add page variable {$FoxPostCount}, counts message items per page $FmtPV['$FoxPostCount'] = 'FoxStringCount($pn,"#foxbegin")'; function FoxStringCount($pagename,$find) { $page = ReadPage($pagename, READPAGE_CURRENT); $n = substr_count($page['text'], $find); // if ($n==0) return ''; #suppressing 0 return $n; } ## use like (:input default $:MyPTV[] {(foxfixptv $:MyPTV)} :) ## for fixing CSV PTV data to space separated data for use in (:input default ...:) $MarkupExpr['foxfixptv'] = 'FoxFixPTV($pagename, $args[0])'; function FoxFixPTV($pn, $ptv) { $p = PageTextVar($pn, substr($ptv,2)); preg_match_all('/(\\"[^\\"]+\\")|[^,\\s]+/',$p,$m); $a = $m[0]; if(!is_array($a)) return ''; foreach($a as $k => $v) $a[$k] = PHSC(trim($v,', '),ENT_NOQUOTES); return implode(" ",$a); } ## helper function for php 4 which has no array_walk_recursive function if (!function_exists('array_walk_recursive')) { function array_walk_recursive(&$input, $funcname, $userdata = "") { if (!is_callable($funcname) || !is_array($input)) return false; foreach ($input as $key => $value) { if (is_array($input[$key])) array_walk_recursive($input[$key], $funcname, $userdata); else { $saved_value = $value; if (!empty($userdata)) $funcname($value, $key, $userdata); else $funcname($value, $key); if ($value != $saved_value) $input[$key] = $value; } } return true; } } //}}} ## decode htmlspecialchars function Fox_htmlspecialchars_decode($str, $style=ENT_COMPAT) { if ($style === ENT_COMPAT) $str = str_replace('"','\"',$str); if ($style === ENT_QUOTES) $str = str_replace(''','\'',$str); $str = str_replace('<','<',$str); $str = str_replace('>','>',$str); $str = str_replace('&','&',$str); return $str; } # debug helper function to echo preformatted array with optional label if (!function_exists('show')) { function show($arr,$lbl=''){echo "<br /><pre><b>$lbl</b> ";print_r($arr);echo "</pre>";} } //}}} ///EOF