<?php if (!defined('PmWiki')) exit();
/**
 * Copyright 2006-05-14, 2007-02-08 Ben Stallings (Ben@InterdependentWeb.com)
 * based on forms.php, Copyright 2005 Patrick R. Michaud (pmichaud@pobox.com)
 * updated to PHP7 by Peter Kay (pkay42@gmail.com) and Ben Stallings
 * This file is designed to work with PmWiki; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.  See pmwiki.php for full details.
 * Database constants (DB_*) must be defined either here or in config.php.
 */

# Special handling for user info
# set this to the database field that corresponds to a user's wiki username, if any.
# SDV($UpdateUserID,'email');

# If you want to use the open-source dFilter.js script for input masks,
# download it from http://www.javascriptsource.com/forms/dfilter.html and
# uncomment the following line; otherwise comment (#) it out:
# $HTMLHeaderFmt['dFilter'] = '<script type="text/javascript" src="/dFilter.js"></script>';

# $UpdateAttrs are the attributes we allow in output tags
SDV($UpdateAttrs, array('name', 'value', 'id', 'class', 'rows', 'cols',
  'size', 'maxlength', 'action', 'method', 'accesskey',
  'checked', 'disabled', 'readonly', 'enctype', 'tabindex', 'onKeyDown'));

# Set up formatting for text, submit, hidden, radio, etc. types
foreach(array('text', 'submit', 'hidden', 'password', 'radio', 'checkbox',
              'reset', 'file') as $t) {
  SDV($UpdateTags[$t][':html'], "<input type='$t' \$UpdateFormArgs />");
}

# (:update form:)
SDVA($UpdateTags['form'], array(
  ':args' => array('action', 'method', 'table', 'fields', 'required', 'where', 'tabindex'),
  ':html' => "<form \$UpdateFormArgs>",
  'action' => ($EnablePathInfo ? $ScriptUrl.'/'.$pagename : $ScriptUrl.'?n='.$pagename),
  'method' => 'post',
));

# (:update end:)
SDV($UpdateTags['end'][':html'], '</form>');

# (:update textarea:)
SDVA($UpdateTags['textarea'], array(
  ':html' => "<textarea \$UpdateFormArgs>\$UpdateTextarea</textarea>"));

# (:update select:)
SDVA($UpdateTags['select'], array(
  ':args' => array('name', 'size', 'multiple', 'value', 'label', 'from', 'where', 'order', 'tabindex'),
  ':html' => "<select \$UpdateFormArgs>\n\$UpdateSelectOptions</select>\n"));

Markup('update', 'fulltext',
  '/\\(:update\\s+(\\w+)(.*?):\\)/i',
  "UpdateMarkup");

# define username depending on whether we're using UserAuth or AuthUser
$UpdateUsername = (defined('USER_AUTH_VERSION') ? $_SESSION['username'] : $_SESSION['authid'][0]);

SDVA($SQdata,$_REQUEST);

function UpdateMarkup($M) {
  global $UpdateTags, $UpdateAttrs, $InputValues, $FmtV, $UpdateFields,
   $UpdateUserID, $UpdateUsername, $Author, $UpdateDependencies, $UpdateTabIndex, $SQdata;
  $pagename=$GLOBALS['MarkupToHTML']['pagename'];
  $type=$M[1];
  $args=$M[2];

  //this preprocessing is the same as in the InputMarkup function; only the names have been changed
  if (!@$UpdateTags[$type]) return "(:update $type $args:)";
  $opt = array_merge($UpdateTags[$type], ParseArgs($args));
  $args = @$opt[':args'];
  if (!$args) $args = array('name', 'value');
  while (count(@$opt['']) > 0 && count($args) > 0)
    $opt[array_shift($args)] = array_shift($opt['']);

  if ($type == 'form') {
    // Connect to Database using PHP Database Objects (PDO)
    $dblink = new PDO('mysql:host='. DB_SERVER. ';dbname='. DB_NAME, DB_USER, DB_PASS);
    if (count($_POST)>0) { // Check for required fields
      $present = 0;
      $required = explode(',', $opt['required']);
      foreach ($required as $req) {
        if (($req > '') and ($_POST[$req] > '')) $present++;
      }
      if ($present == count($required)) { // process form

     $success = 0;
     $timestamp = date('Y-m-d H:i:s');

     //Check for a 'where' parameter
     foreach (explode(',',$opt['where']) as $req) {
      if (!$_POST[$req]) $present--;
     }
     if ($present==count($required)) { //an existing entry has been specified.
      unset ($where);
      unset ($dependencies);
      $params = array();
      foreach (explode(',',$opt['where']) as $wherefield) {
       $where[] = "$wherefield = :wherefield";
       $params[':wherefield'] = $_POST[$wherefield];
       //prepare to delete dependent entries, if necessary
       foreach (explode(',',$UpdateDependencies[$opt['table'].".$wherefield"]) as $dep) {
        list($deptable,$depfield) = explode('.',$dep);
        $dependencies[] = "DELETE FROM $deptable WHERE $depfield = :wherefield";
       }
      }

      if ($_POST[$opt['delete']]) { //delete an existing entry
        $query = "DELETE FROM ".$opt['table']." WHERE ".implode(' AND ',$where);
        //$out.= "<p>$query</p>";
        try {
         $dblink->prepare($query)->execute($params);
         foreach ($dependencies as $dep) $dblink->prepare($dep)->execute($params);
         $success = 1;
         $out.= "<h3 style='color:red'>Successfully deleted this record and its dependencies.</h3>";
        } catch (PDOException $e) {
         $out.= "<h3 style='color:red'>Unable to delete this record.</h3><p>$query</p><p>". $e->getMessage() ."</p>";
        }

      } else { // update an existing entry
        $query = "UPDATE ".$opt['table']." SET ";
        $params = array();
        foreach (explode(',',$opt['fields']) as $field) {
         if ((strpos(",".$opt['null'].",",",".$field.",") !== false) AND ($_POST[$field]=='')) {
          $query .= "`$field` = NULL, ";
         } else {
          $query .= "`$field` = :$field, ";
          $params[":$field"] = $_POST[$field];
         }
        }
        if ($opt['timestamp']>'') $query .= $opt['timestamp']." = '$timestamp'";

        $query = rtrim($query, ', ')." WHERE ".implode(' AND ',$where);
        //$out.= "<p>$query</p>";
        try {
         $dblink->prepare($query)->execute($params);
         $success = 1;
         $out.= "<h3 style='color:red'>Successfully made your changes.</h3>";
        } catch (PDOException $e) {
         $out.= "<h3 style='color:red'>Unable to make your changes.</h3><p>$query</p><p>"
         . $e->getMessage()."</p>";
        }
      }

     } else { // insert new entry
      $queryA = "INSERT INTO ".$opt['table']." (";
      $queryB = ") VALUES (";
      $params = array();
      foreach (explode(',',$opt['fields']) as $field) {
       //is the field listed as having a default?  If not, include it.
       if ((strpos(",".$opt['default'].",",",".$field.",") === false) OR ($_POST[$field])) {
        $queryA .= "`$field`,";
        //if this is the UserID field and no value is given, use the UserID
        if (in_array($UpdateUserID,explode(',',$opt['where'])) and ($field==$UpdateUserID) and (!$_POST[$field])) {
          $params[':UpdateUsername'] = $UpdateUsername;
          $queryB .= ':UpdateUsername,';
        } else {
          $params[":$field"] = $_POST[$field];
          $queryB .= ":$field,";
        }
       }
      }
      if ($opt['timestamp']>'') {
       $queryA .= $opt['timestamp'];
       $queryB .= "'$timestamp'";
      }
      $query = rtrim($queryA, ',').rtrim($queryB, ',').")";
      try {
       $dblink->prepare($query)->execute($params);
       $success=1;
       $out.= "<h3 style='color:red'>Successfully added this information to the database.</h3>";
       $wherevalue = $dblink->lastInsertId();
      } catch (PDOException $e) {
       $out.= "<h3 style='color:red'>Unable to add this information to the database.</h3><p>$query</p><p>"
       . $e->getMessage()."</p>";
      }
     }
    } else { //missing values
     $out .= "<h3>Please fill in all <span style='color:red'>required fields.</span></h3>\n";
     $UpdateFields = array();
     $UpdateFields += $_POST;
    }
   } //endif $_POST

   if (($success == 1) and (isset($opt['redirect']))) { //redirect to specified page
    Redirect($opt['redirect']);
   } else {

    // Get existing info from database, if any
    unset ($where);
    $params = array();
    foreach (explode(',',$opt['where']) as $wherefield) {
     if ($wherefield == $UpdateUserID) {
      // It's not "any kind of query," Sark.  It's a *User* query.
      $where[] = "$UpdateUserID = :UpdateUsername";
      $params[":UpdateUsername"] = $UpdateUsername;
     } else {
      $where[] = "$wherefield = :wherevalue";
      $params[':wherevalue'] = ($_REQUEST[$wherefield] ? $_REQUEST[$wherefield] : $wherevalue);
      SDV($SQdata[$wherefield],($_REQUEST[$wherefield] ? $_REQUEST[$wherefield] : $wherevalue));
     }
    }
    if ((isset($opt['table'])) and (isset($opt['fields'])) and (isset($where))) {
     $query = "SELECT " . $opt['fields'] . " FROM " . $opt['table'] . " WHERE " . implode(" AND ",$where);
     //$out.="$query";
     if ($sth = $dblink->prepare($query)->execute($params)) {
       $UpdateFields = $sth->fetch(PDO::FETCH_ASSOC);
       SDVA($UpdateFields,$_POST);
       if (defined($UpdateFields)) SDVA($SQdata,$UpdateFields);
     }
    }
   }
   $dblink = NULL;
  //end of $type = 'form'

  //a little bit of magic to create drop-down menus from a query!
  } elseif (($type=='select') and (isset($opt['from']))) {
   //if value and/or label are not provided, fill them in with what we do know
   SDV($opt['value'],$opt['name']);
   SDV($opt['label'],$opt['value']);
   // Connect to Database
   $dblink = new PDO('mysql:host='. DB_SERVER .';dbname='. DB_NAME, DB_USER, DB_PASS);
   $selectq = "SELECT ". $opt['value'] .", ". $opt['label'] ." FROM ". $opt['from']
   ." WHERE ". ($opt['where'] ? html_entity_decode($opt['where']) : 1)
   . ($opt['order'] ? " ORDER BY ".$opt['order'] : "");
   $selectd = $dblink->query($selectq);
   unset($FmtV['$UpdateSelectOptions']);
   if (isset($opt['null'])) $FmtV['$UpdateSelectOptions'] = "<option value='' "
   . ($UpdateFields[$opt['name']]=='' ? "selected='selected' " : "")
   . ">" . $opt['null'] . "</option>\n";
   foreach ($selectd as $option) {
    $FmtV['$UpdateSelectOptions'] .= "<option value='".$option[0]."' "
    . ((($option[0]==$UpdateFields[$opt['name']]) or ($option[0]==$SQdata[$opt['name']])) ? "selected='selected' " : "")
    . ">" . $option[1] . "</option>\n";
   }
   //don't display database info in HTML source
   unset($opt['value']);
   $dblink = NULL;

  } // endif $type

  // if given a parameter with no value, set the value to the name of the parameter
  // for example, "checked" should become "checked='checked'" to be valid HTML
  foreach ((array)@$opt[''] as $a)
    if (!isset($opt[$a])) $opt[$a] = $a;

  //insert $SQdata info into field, if value given is a parameter name in `backquotes`
  if (strrpos('`',$opt['value'])!==false) {
   $opt['value'] = $SQdata[str_replace("`","",$opt['value'])];
  }

  if (($type=='text') or ($type=='hidden') or ($type=='password')) {
   // insert database info into field
   if (isset($UpdateFields[$opt['name']])) $opt['value'] = $UpdateFields[$opt['name']];
   // insert $_GET info into field, if any
   if (isset($_GET[$opt['name']])) $opt['value'] = $_GET[$opt['name']];
  }

  // another bit of magic to allow values in textareas
  if ($type=='textarea')
   $FmtV['$UpdateTextarea'] = $UpdateFields[$opt['name']]? $UpdateFields[$opt['name']]: "";

  // auto-input user ID into text or hidden fields when no other value given
  if ((($type=='text') or ($type=='hidden')) and ($opt['name']==$UpdateUserID))
   SDV($opt['value'],$UpdateUsername);

  //content masking with optional Javascript
  unset($opt['onKeyDown']);
  if ($opt['mask']>'')
   $opt['onKeyDown']='javascript:return dFilter (event.keyCode, this, "'.$opt['mask'].'");';

  //automatic tabindex
  if ($opt['tabindex']>0) {
   $UpdateTabIndex = $opt['tabindex']+1;
  } elseif ($opt['tabindex']===0) {
   $UpdateTabIndex = 0;
  } elseif (($UpdateTabIndex > 0) and ($type!='form') and ($type != 'end') and ($type != 'hidden')) {
   $opt['tabindex'] = $UpdateTabIndex++;
  }

  //not sure what this little loophole is for, but it's in forms.php, so I copied it
  if (!isset($opt['value']) && isset($InputValues[@$opt['name']]))
    $opt['value'] = $InputValues[$opt['name']];

  //put quotes around values for HTML compliance
  $attr = array();
  foreach ($UpdateAttrs as $a) {
    if (!isset($opt[$a])) continue;
    $attr[] = "$a='".str_replace("'", '&#39;', $opt[$a])."'";
  }

  //set radio and checkboxes to match database info
  if ((($type=='radio') or ($type=='checkbox')) and ($opt['value'] == $UpdateFields[$opt['name']]))
    $attr[] = "checked='checked'";

  //exit code copied bodily from forms.php
  $FmtV['$UpdateFormArgs'] = implode(' ', $attr);
  $out .= FmtPageName($opt[':html'], $pagename);
  return preg_replace_callback('/<(\\w+\\s)(.*)$/s',function ($m) { return "<{$m[1]}".Keep(($m[2]));}, $out);
}