<?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
*/


## DATA FORMS ENGINE
$HandleActions['data'] = 'Data';
function Data() {
	global $WorkDir, $WikiDir, $pagename, $ScriptUrl, $title, $data, $log, $ts, $MessagesFmt, $m;
	$nextpage = $pagename;
	$datapage = "Data-" . $pagename;
	$dataprofiles = "Data-Profiles";  // default group for user accounts.
	$pagecreated = "false";
	$m = "";  // holds system messages.
	$data = "";
	$log = "";
	$ts = time();
	$nocode = array('"', "'", '%0D');
    $encode = array('&quot;', '&apos;', '%0a');

	foreach ($_POST as $field => $value) {

## PAGE FIELDS
		if (substr($field, -4, 4) == "page") {
			if ($value != "") $_POST[$field] = FixPage($_POST[$field]);
			if ($field == "nextpage") $nextpage = $_POST[$field];
			if ($field == "datapage") $datapage = "Data-" . $_POST[datapage];
			if ($field == "linkpage") $_POST[linkpage] = "$ScriptUrl?n=$_POST[linkpage]";
			}

## MISC FIELDS
		if ($field == "title") $title = "(:title $value:)";

		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;
				}
			}

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

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

		if (substr($field, 0, 8) == "savedata") {
			if ((PageExists("$datapage")) or ($pagecreated == "true")) GetPage($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($datapage);
			$m .= "Data has been successfully saved.  ";
			}

## LOG FUNCTIONS
		if (substr($field, 0, 7) == "savelog") {
			if ((PageExists("$datapage")) or ($pagecreated == "true")) GetPage($datapage);
			else $pagecreated = "true";
			$newlog = "";
			$prepend = substr($value, 0, 1);
			if ($prepend == "<") $value = substr($value, 1);
			$d = explode(",", $value);
			foreach ($d as $f => $v) {
				switch ($v) {
					case "log" :
						$newlog .= "[[#Log$ts]] \n"; 
						break;
					case ":" :
						$newlog .= "[==]";
						break;
					case "." :
						$newlog .= "[[<<]] \n";
						break;
					case "author" :
						$logpage = $_POST[logpage];
						if ($logpage == "") $logpage = $nextpage;
						$newlog .= "(:if authid $GLOBALS[AuthId]:)[[$logpage?update=author&id=$ts|Update]](:if:)";
						break;
					case "admin" :
						$logpage = $_POST[logpage];
						if ($logpage == "") $logpage = $nextpage;
						$newlog .= "(:if auth admin:)[[$logpage?update=admin&id=$ts|UpdateTest]](:if:)";
						break;
					default:
						$newlog .= "$_POST[$v] \n"; 
						break;
					}
				}
			if ($prepend == "<") $log = $newlog . "\n" . $log;
			else $log = $log . $newlog . "\n";
			SetPage($datapage,$contents);
			$m .= "Log has been successfully saved.  ";
			}

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

## SPECIAL FUNCTIONS
		if (substr($field, 0, 8) == "makepage") {
			$s = explode("|", $_POST[$field]);
			if (! PageExists($s[0])) {
				$page['text'] = $s[1];
				Lock(2);
				WritePage($s[0], $page);
				Lock(0);
				$m .= "Page $s[0] created.";
				}
			else "Page $s[0] already exists.  Could not be created";
			}

		if (substr($field, 0, 6) == "delete") {
			if (PageExists("$datapage")) {
				if ($_POST[delete] == "page") {
					$WikiDir->delete($datapage);
					$m .= "Datapage has been successfully deleted.  ";
					}
				if ($_POST[delete] == "data") {
					GetPage($datapage);
					$data = "";
					SetPage($datapage,$contents);
					$m .= "Data has been successfully deleted.  ";
					}
				if ($_POST[delete] == "log") {
					GetPage($datapage);
					$log = "";
					SetPage($datapage,$contents);
					$m .= "Log has been successfully deleted.  ";
					}
				}
			if (! PageExists("$datapage")) $m .= "Datapage 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.");
			}

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

## 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 ($ff[1] == "nextpage") $nextpage = $_POST[nextpage];
				if ($ff[1] == "datapage") $datapage = $_POST[datapage];	
				}
			}

		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]]);
				if ($c[1] == "nextpage") $nextpage = $_POST[nextpage];
				if ($c[1] == "datapage") $datapage = $_POST[datapage];
				}
			}
		}

	$MessagesFmt[] = "<h5 class='wikimessage'>$[$m]</h5>";
	if ($nextpage == $pagename)	HandleBrowse($pagename);
  	else Redirect(FmtPageName($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, $title, $data, $log;
	$contents = "$title(:comment data:) \n\n$data(:comment data:) \n\n[[#Log]] \n\n$log(:comment data:) \n\n";
	$page['text'] = $contents;
	Lock(2);
	WritePage($d, $page);
	Lock(0);
	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 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 ((substr($v, -1, 1) == "#") or (substr($v, -1, 1) == "+")) {
		$g = substr("Data-$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;
		}
	$rg1 = array('*','^','@'); 
	$rg2 = array("$pn[0]","$pn[1]","$GLOBALS[AuthId]");
	$rn1 = array('*','^','@','=','#','+'); 
	$rn2 = array("$pn[1]","$pn[0]","$GLOBALS[AuthId]","$e","$ee","$ts");
	$v = str_replace($rg1, $rg2, $vv[0]) . "." . str_replace($rn1, $rn2, $vv[1]) . $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, $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 = "Data-" . 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;
	}