<?php if (!defined('PmWiki')) exit();

/*
* @Description: This recipe extends forms capability on PmWiki, including:
* flexible data storage, retrieval, logging, email processing, and more.
* Also allows for an alternate way to register and authenticate members.
* Includes threads, required fields, conditionals, extra forms markup, etc.
* For info, see docs at http://www.pmwiki.org/wiki/Cookbook/FASTData.  Enjoy!
* @author: Dan Vis aka Caveman <editor àt fast döt st>  
* @license http://www.gnu.org/licenses/gpl.html GNU General Public License
*/


SDV($DataKey, "Data-");

## DATA FORM ENGINE
$HandleActions['data'] = 'Data';
function Data() {
	global $WorkDir, $WikiDir, $pagename, $DataKey, $ScriptUrl, $data, $log, $ts, $MessagesFmt, $m;
	$m = "";  // holds system messages.
	$data = "";
	$log = "";
	$ts = time();
	$nocode = array('"', "'", '%0D');
    $encode = array('&quot;', '&apos;', '%0a');
	if (!isset($_POST[nextpage]) || $_POST[nextpage] = "") $_POST[nextpage] = $pagename;
	if (!isset($_POST[datapage]) || $_POST[datapage] = "") $_POST[datapage] = $pagename;
	
	foreach ($_POST as $field => $value) {

## PAGE FIELDS
		if (substr($field, -4, 4) == "page") {
			if ($value != "") $_POST[$field] = FixPage($_POST[$field]);
			if ($field == "linkpage") $_POST[linkpage] = "$ScriptUrl?n=$_POST[linkpage]";
			}
		
		if (substr($field, 0, 7) == "datakey") $DataKey = $field;
			
		if (substr($field, 0, 8) == "makepage") {
			$contents = "";
			$s = explode("|", $_POST[$field]);
			if (isset($s[1])) $contents = GetTemplate("page",$s[1]);
			if (! PageExists($s[0])) {
				$page['text'] = $contents;
				$page['title'] = $_POST[title];
				Lock(2);
				WritePage($s[0], $page);
				Lock(0);
			    PageIndexUpdate($s[0]);
				$m .= "Page $s[0] created.";
				}
			else "Page $s[0] already exists.  Could not be created";
			}

## MISC FIELDS
		if (substr($field, 0, 5) == "count") {
			if (substr($_POST[$field], 0, 1) == "=") $_POST[$field] = substr($_POST[$field], 1);
			else $_POST[$field] = $_POST[$field] + 1;
			}

		if (substr($field, 0, 6) == "random") {
			$n = explode("|", $value);
			$_POST[$field] = rand($n[0],$n[1]);
			}

		if ($field == "timestamp") $_POST[$field] = $ts;
	
		if (substr($field, -3, 3) == "fmt") {
			switch ($value) {
				case "date" :
					$_POST[$field] = strftime("%B %d, %Y", $_POST[substr($field, 0, -3)]);
					break;
				case "time" :
					$_POST[$field] = strftime("%c", $_POST[substr($field, 0, -3)]);
					break;
				}
			}

		if (substr($field, 0, 8) == "messages") $m = $_POST[$field];

## DATA FUNCTIONS					
		if (substr($field, 0, 8) == "passdata") {
			$d = explode(",", $value);
			foreach ($d as $f => $v) {
				$_POST[nextpage] .= "?$v=$_POST[$v]";
				}
			}

		if (substr($field, 0, 7) == "getdata") {
			$g = explode(",", $value);
			foreach ($g as $f => $v) {
				$_POST[$v] = GetData("$DataKey$_POST[datapage]",$v);
				}
			}

		if (substr($field, 0, 8) == "savedata") {
			if ((PageExists("$DataKey$_POST[datapage]")) or ($pagecreated == "true")) GetPage("$DataKey$_POST[datapage]");
			else $pagecreated = "true";
			$data = "";
			$d = explode(",", $value);
			foreach ($d as $f => $v) {
				$vv = urldecode(str_replace($nocode, $encode, urlencode($_POST[$v])));
				$data .= "$v=\"$vv\" \n\n"; 
				}
			SetPage("$DataKey$_POST[datapage]");
			$m .= "Data has been successfully saved.  ";
			}

## LOG FUNCTIONS
		if (substr($field, 0, 7) == "savelog") {
			if (PageExists("$DataKey$_POST[datapage]")) GetPage("$DataKey$_POST[datapage]");
			$newlog = "";
			$prepend = substr($value, 0, 1);
			if ($prepend == "<") $_POST[$field] = substr($value, 1);
			if (!isset($_POST[logpage])) $_POST[logpage] = $nextpage;
			if (isset($_POST[logtemplate])) $newlog = GetTemplate("log",$field);
			else {
				$rb1 = array(':','.','{log}','{delete}','{author}','{admin}');
				$rb2 = array('[==]','[[<<]] \n','{log}','{delete}','{author}','{admin}');
				$d = explode(",", $value);
				foreach ($d as $f => $v) {
					if (in_array($v, $rb1)) $newlog .= str_replace($rb1, $rb2, $v);
					else $newlog .= "$_POST[$v] \n"; 
					}
				}
			$re1 = array('{log}','{delete}','{author}','{admin}');
			$re2 = array('[[#Log$ts]]','(:input form:)(:input hidden action data:)(:input hidden datapage "$DataKey$_POST[datapage]":)(:input hidden id "$ts":)(:input hidden update log:)(:input submit value="Delete":)(:input end:)','(:if authid $GLOBALS[AuthId]:)[[$logpage?update=author&id=$ts|Update]](:if:)','(:if auth admin:)[[$logpage?update=admin&id=$ts|Update]](:if:)');
			$newlog .= str_replace($re1, $re2, $newlog); 
			if ($prepend == "<") $log = $newlog . "\n" . $log;
			else $log = $log . $newlog . "\n";
			SetPage("$DataKey$_POST[datapage]",$contents);
			$m .= "Log has been successfully saved.  ";
			}

		if (substr($field, 0, 9) == "updatelog") {
			GetPage("$DataKey$_POST[datapage]");
			$entry = explode("[[#Log", $log); 
			foreach ($entry as $e => $ee) {
				if (substr($value, 0, 4) != "trim") {
					if (substr($entry[$e], 0, 10) == $_POST['id']) {
						if ($value == "remove") unset($entry[$e]);
						if ($value == "update") {	
							$t = explode("[==]", $entry[$e]);
							$t[1] = $_POST[newlog];
							$entry[$e] = implode("[==]", $t);
							}
						}
					}
				elseif (($ts - substr($entry[$e], 0, 10)) > (substr($value, 4) * 86400)) unset($entry[$e]);
				}
			$log = "[[#Log" . implode("[[#Log", $entry);
			SetPage("$DataKey$_POST[datapage]");
			}

## EMAIL FUNCTIONS
		if ($field == "emailto") {
			$list = explode(",", $value);
			foreach ($list as $l => $ll) {
				if ($list[$l] == "self") $list[$l] = $_POST[emailfrom];
				if (substr($list[$l], 0, 5) == "list:") {
					$m .= "hi: " . GetList(substr($list[$l], 5));
					$list[$l]= GetList(substr($list[$l], 5));
					}
				}
			$_POST[emailto] = implode(",", $list);
			}

		if (substr($field, 0, 9) == "emailer") {
			$emailbody = '';
			if ($_POST[emailto] == "") Warning("No recipient designated.  Email not sent.");
			if ($_POST[emailfrom] == "") Warning("No return address entered.  Email not sent.");
			if ($_POST[emailsubject] == "") Warning("No email subject entered.  Email not sent.");
			if (isset($_POST[emailtemplate])) $emailbody = GetTemplate("email",$field);
			else {
				$d = explode(",", $value);
				foreach ($d as $f => $v) {
					if (substr($v,0,1) == '-' ) {
						$v = substr($v,1);
						$emailbody .= "$_POST[$v] \n\n"; 
						}
					else {
						$emailbody .= "$v = $_POST[$v] \n\n";
						}
					}
				}
			if ($emailbody == "") Warning("No contents in message.  Email not sent.");
			$list = explode(",", $_POST[emailto]);
			foreach ($list as $to) {
				if (ereg("^.+@.+\..+$", $to)) mail($to, $_POST[emailsubject], $emailbody, "From: $_POST[emailfrom]");
				}
			$m .= "Mail successfully sent.  ";
			}

## SPECIAL FUNCTIONS
		if (substr($field, 0, 6) == "delete") {
			if (PageExists("$DataKey$_POST[datapage]")) {
				if (substr($_POST[delete], 0, 4) == "page") {
					if (substr($_POST[delete], 4, 1) == ":") $WikiDir->delete(substr($_POST[delete],5)); 
					else $WikiDir->delete("$DataKey$_POST[datapage]");
					$m .= "Page has been deleted.  ";
					}
				if ($_POST[delete] == "data") {
					GetPage("$DataKey$_POST[datapage]");
					$data = "";
					SetPage("$DataKey$_POST[datapage]",$contents);
					$m .= "Data has been deleted.  ";
					}
				if ($_POST[delete] == "log") {
					GetPage("$DataKey$_POST[datapage]");
					$log = "";
					SetPage("$DataKey$_POST[datapage]",$contents);
					$m .= "Log has been deleted.  ";
					}
				}
			if (! PageExists("$DataKey$_POST[datapage]")) $m .= "Page not found. Info not deleted.";
			}
			
		if ($field == "required") {
			$required = true;
			$r = explode(",", $value);  // $r = required fields
			foreach ($r as $f => $v) {
				switch ($v) {
					case "newmember" :
						if ($_POST[$v] == "") Warning("Please enter a member name.");
						if (PageExists("$dataprofiles.$_POST[$v]")) Warning("Member name already taken.  Please try again.");
						if ($_POST[$v] != PageVar(MakePageName($pagename, $_POST[$v]), '$Name')) Warning("Invalid member name. Please try again.");
						break;
					case "member" :
						if (! PageExists("$dataprofiles.$_POST[$v]")) Warning("Member name does not exist. Please try again.");
						break;
					case "newpage" :
						if (PageExists("$_POST[$v]")) Warning("Page already exists. Please try again.");
						break;
					case "newgroup" :
						if (PageExists("$_POST[$v].RecentChanges")) Warning("Group already exists. Please try again.");
						break;
					case "Email" :
						if (!ereg("^.+@.+\..+$", $_POST[$v])) Warning("Invalid email address entered.");
						break;
					default :
						if ($_POST[$v] == "") Warning("Field \"$v\" required. Please try again.");
						break;
					}
				}
			}

		if (substr($field, 0, 6) == "upload") {
			if (($_POST['uploadfile'] != "") && ($_POST['upname'] != "")) HandlePostUpload($pagename);
			else $m .= "Missing fields required for upload feature.";
			}

		if ($field == "login") {
			$dp = $dataprofiles . "." . $_POST[member];
			if (($value == "auto") and (! PageExists($dp))) {
				AuthUserId($pagename, $_POST['member']);
				$m .= "You have been successfully logged in.  ";					
				}
			$pass1 = GetData($dp,"Password");
			$pass2 = $_POST['passwd'];
			if ($pass1 == $pass2) {			   
				AuthUserId($pagename, $_POST['member']);
				$m .= "You have been successfully logged in.  ";
				}
			else Warning("Incorrect member name or password.");
			}

## CONDITIONAL FUNCTIONS
		if (substr($field, 0, 2) == "if") {
			$f = explode(",", $value);
			foreach ($f as $stuff) {
				$ff = explode("|", $stuff);
				if ($_POST[substr($field, 2)] == $ff[0]) $_POST[$ff[1]] = $ff[2];
				}
			}

		if (substr($field, 0, 7) == "replace") {
			$d = explode(",", $value);
			foreach ($d as $f => $v) {
				$c = explode("|", $v);
				if ($c[2] == "") $_POST[$c[1]] = $_POST[$c[0]];
				else $_POST[$c[1]] = str_replace($c[2], $_POST[$c[0]], $_POST[$c[1]]);
				}
			}
		}

	$MessagesFmt[] = "<h5 class='wikimessage'>$[$m]</h5>";
	if ($_POST[nextpage] == $pagename)	HandleBrowse($pagename);
  	else Redirect(FmtPageName($_POST[nextpage], $pagename));
	}



## MISC FUNCTIONS
function Warning($m) {
	global $pagename, $MessagesFmt;
	$MessagesFmt[] = "<h5 class='wikimessage'>$[$m]</h5>";
	HandleBrowse($pagename);
	die();
	}

function GetPage($d) {
	global $WorkDir, $data, $log;
	if (PageExists("$d")) {
		$page = ReadPage($d);
		$contents = $page['text'];
		$v = explode("(:comment data:) ", $contents);
		$data = substr($v[1], 2);
		$log = substr($v[2], 13);
		}
	return;
	}

function SetPage($d) {
	global $WorkDir, $data, $log, $_POST;
	$contents = "(:title $_POST[title]:)\n(:comment data:) \n\n$data(:comment data:) \n\n[[#Log]] \n\n$log(:comment data:) \n\n";
	$page['text'] = $contents;
	$page['title'] = $_POST[title];
	Lock(2);
	WritePage($d, $page);
	Lock(0);
    PageIndexUpdate($d);
	return;
	}

function GetData($p,$v) {
	global $WorkDir, $data, $log;
	if (PageExists($p)) {
		GetPage($p);
		$field = explode(" \n\n", $data);
		foreach ($field as $value) {
			$vv = explode ("=", $value);
			if ($vv[0] == $v) {
				return substr($vv[1], 1, -1);
				}
			}
		}
	return;
	}

function GetLog($d,$id) {
	global $WorkDir, $data, $log;
	GetPage($d);
	$entry = explode("[[#Log", $log); 
	foreach ($entry as $e => $ee) {
		if (substr($ee, 0, 10) == $id) {
			$t = explode("[==]", $ee);
			return $t[1];
			}
		}
	return;
	}

function GetList($l) {
	global $WorkDir;
	if (PageExists("$l")) {
		$page = ReadPage($l);
		$contents = $page['text'];
		$v = explode("(:comment data:) ", $contents);
		$list = substr($v[2], 13);
		$list = substr(urldecode(str_replace('+%0A%0A', ',', urlencode($list))), 0, -1);
		}
	return $list;
	}

function GetTemplate($x,$fields) {
	global $WorkDir, $data, $log, $_POST;
	$f = explode(",", $fields);
	if (substr($_POST["$xtemplate"], 0, 5) == "page:") {
		$page = ReadPage(substr($_POST["$xtemplate"], 5));
		$xx = $page['text'];
		}
	else $xx = $_POST["$xtemplate"];
	foreach ($f as $ff) {
		$xx = str_replace("{$ff}", $_POST[$ff], $xx);
		}
	return $xx;
	}

function FixPage($v) {
	global $pagename, $ts, $_GET;
	if (!strpos($v, ".")) return $pagename;
	$vv = explode(".", $v);
	$pn = explode(".", $pagename);
	if (strpos($vv[1], "?")) {
		$vv[2] = substr($vv[1], strpos($vv[1], "?"));
		$vv[1] = substr($vv[1], 0, strpos($vv[1], "?"));
		}
    if (substr($vv[1], 0, 3) == "GET") $vv[1] = $_GET[substr($vv[1], 3)];
	$e = 999;
	$t = time();
	if (strpos("$vv[0]$vv[1]", "#") || strpos("$vv[0]$vv[1]", "+")) {
		$g = substr("$DataKey$v", 0, strlen($value)-2);
		foreach(ListPages("/^$g\\.\\d/") as $n) {
			$n = substr($n,strlen($g)+1);
			$e = max($e,$n);
			}
		$ee = $e + 1;
		if ($ts == "") $ts = $e;
		}
	$r1 = array('*','^'); 
	$r2 = array("$pn[0]","$pn[1]");
	$v = str_replace($r1, $r2, $vv[0]) . "." . $vv[1];
	$rr1 = array('*','^','@','=','#','+'); 
	$rr2 = array("$pn[1]","$pn[0]","$GLOBALS[AuthId]","$e","$ee","$ts");
	$v = str_replace($rr1, $rr2, $v) . $vv[2];
	return $v;
	}



## EXTRA FORMS MARKUPS
Markup('select', 'inline', '/\(:select (.*?):\\)/', '<select name=$1>');
Markup('option', 'inline', '/\\(:option (.*?):\\)/e', "Keep(PSS(\"<option value='$1'>\"))");
Markup('selectend', 'inline', '/\(:selectend:\\)/', '</select>');
Markup('textarea', 'inline', '/\\(:textarea (.*?):\\)/e', "Keep(PSS(\"<textarea $1>\"))");
Markup('textareaend', 'inline', '/\(:textareaend:\\)/', '</textarea>');
Markup('keep', '>{$var}', '/\\(:keep (.*?):\\)/esi', "Keep(PSS('$1'))");



## READ DATA DIRECTIVE
Markup('fastdata', '<{$var}', '/\(:data(.*?):\)/ei', "ReadData('$1')");

# Function called by directive to retrieve form data
function ReadData($d) {
	global $WorkDir, $FmtPV, $pagename, $DataKey, $data, $log, $GET;
    $encode = array('&quot;', '&apos;');
	$decode = array('"', '`');
	if (strpos($d,"|")) {
		$dd = substr($d, strpos($d,"|")+1);
		$d = substr($d, 0, strpos($d,"|"));
		}
## SET GET VALUES
	if ($dd) {
		$get = explode(",", $dd);
		foreach ($get as $g) {
		switch ($g) {
			case "id" :
				if ($_GET[$g] != "") $FmtPV["$$g"] = $_GET[$g];
				$FmtPV['$logid'] = "'" . GetLog($d,$_GET[id]) . "'";
				break;
			default :
				if ($_GET[$g] != "") $FmtPV["$$g"] = $_GET[$g];
				break;
			}
		}
	}
## SET DATA VALUES
    $d = $DataKey . FixPage(substr($d, 1));
	$FmtPV['$thread'] = "'" . substr($d, strpos($d, ".")+1) . "'";
	if (PageExists("$d")) {
		GetPage($d);
		$field = explode(" \n\n", $data);
		foreach ($field as $value) {
			$v = explode ("=", $value);
			$v[1] = substr($v[1],1,-1); //stripslashes(substr($v[1],1,-1));
			$v[1] = str_replace($encode, $decode, $v[1]);
			if ($v[0] != "") $FmtPV["$$v[0]"] = "'" . $v[1] . "'";
			}
		}
	return;
	}