FormToolsWithRDB.php
<?php
require(__DIR__.'/JSLikeHTMLElement.php');
class FormToolsWithRDB
{
protected $html = '';
protected $doc;
public function __construct($formHtml,$hideXmlErrors=true)
{
$this->html = $formHtml;
libxml_use_internal_errors($hideXmlErrors);
$domDoc = new \DomDocument();
$domDoc->registerNodeClass('DOMElement', 'JSLikeHTMLElement');
$domDoc->loadHtml($formHtml);
libxml_use_internal_errors(false);
$this->doc = $domDoc;
}
public function resultsFromLookup($type,$lookup){
$queries = explode(';',$lookup);
$qs = [];
$binds = [];
$addColumns = [];
foreach ($queries as $q){
$parts = explode(':',$q);
$key = $parts[0];
$value = $parts[1] ?? null;
if ($value===null)continue;
$qs[] = $key.' = :'.$key;
$binds[':'.$key] = $value;
$addColumns[$key] = $value;
}
$where = implode(' AND ',$qs);
// var_dump($where);
// print_r($binds);
// echo "\n\n";
$results = \RDB::find($type,$where,$binds);
return ['addcolumns'=>$addColumns,'results'=>$results];
}
public function SchemaPrepareDisplay(){
$hostXPath='//*[@typeof]';
$hostAttribute='typeof';
$childAttribute='property';
$xPath = new DOMXpath($this->doc);
$entities = $xPath->query($hostXPath);
$inputs = [];
foreach ($entities as $input) {
$type = $input->getAttribute($hostAttribute);
$lookup = $input->getAttribute('lookup');
$ret = $this->resultsFromLookup($type,$lookup);
$results = $ret['results'];
$addColumns = $ret['addcolumns'];
$obj = null;
if (count($results)>1) throw new \Exception ("There were multiple matches. Contact the web developer.");
else $obj = array_shift($results);//[0];
// var_dump($obj);
// exit;
$xPath = new DOMXpath($this->doc);
$props = $xPath->query($hostXPath.'//*[@'.$childAttribute.']');
if ($obj==null){
$obj = \RDB::dispense($type);
foreach ($props as $node){
// var_dump($node);
$propName = $node->getAttribute($childAttribute);
$obj->$propName = $this->nodeInnerHTML($node);
}
foreach ($addColumns as $key=>$value){
$obj->$key=$value;
}
\RDB::store($obj);
} else {
// var_dump($obj);
foreach ($props as $node){
$prop = $node->getAttribute($childAttribute);
$newVal = $obj->$prop;
$node->innerHTML = $newVal;
}
}
}
return $inputs;
}
public function SchemaPrepareEdit($popupDialog){
$this->SchemaPrepareDisplay();
$xPath = new DOMXpath($this->doc);
$entities = $xPath->query('//*[@typeof]');
$inputs = [];
foreach ($entities as $node) {
$class = $node->getAttribute('class');
$list = explode(' ',$class);
$list[] = 'SchemaEditable';
$node->setAttribute('class',implode(' ',$list));
}
$bodyXPath = new \DOMXpath($this->doc);
$bodies = $bodyXPath->query('//body');
$body = $bodies[0];
$body->innerHTML = $body->innerHTML . $popupDialog;
// return $inputs;
}
public function SchemaPrepareForm(){
$xPath = new DOMXpath($this->doc);
$entities = $xPath->query('//form[@name]');
$form = $entities[0];
$type = $form->getAttribute('name');
$lookup = $form->getAttribute('lookup');
$view = $form->getAttribute('view');
$ret = $this->resultsFromLookup($type,$lookup);
$results = $ret['results'];
$obj = reset($results);
// var_dump($obj);
// exit;
$this->RDBPrepareEdit($type,$obj->id??null);
// $form->innerHTML = "\n".'<input type="hidden" name="id" value="'.$obj->id.'"/>'."\n".$form->innerHTML;
// $form->innerHTML = "\n".'<input type="hidden" name="_type" value="'.$type.'"/>'."\n".$form->innerHTML;
$form->innerHTML = "\n".'<input type="hidden" name="object_view" value="'.$view.'"/>'."\n".$form->innerHTML;
$form->setAttribute('method','POST');
$form->setAttribute('action','/submit_auto_form/');
// $xPath = new DOMXpath($this->doc);
// $entities = $xPath->query('//form[@name]');
// $inputs = [];
// foreach ($entities as $form) {
// $name = $form->getAttribute('name');
// $lookup = $form->getAttribute('lookup');
// $form->setAttribute('data-lookup',$name.' '.$lookup);
// }
}
public function getValidSubmissions($userSubmissions,$ignore=[]){
$inputs = $this->getInputs();
$validSubmissions = [];
foreach ($ignore as $index=>$ignoreKey){
unset($userSubmissions[$ignoreKey]);
}
foreach ($inputs as $index=>$inputData){
extract($inputData);
$pass = $this->verify($inputData,$userSubmissions[$name]??null);
if (!$pass){
throw new \Exception("Input '{$name}' failed verification");
}
$validSubmissions[$name] = $userSubmissions[$name];
unset($userSubmissions[$name]);
}
if (count($userSubmissions)>0){
throw new \Exception("More data was sent to the server than was requested.");
}
return $validSubmissions;
}
protected function verify($inputData, $value)
{
//see $this->getInputs() to see what is extracted
extract($inputData);
if (empty($value) && $required) {
return false;
}
if ($type == 'phone') {
$clean = preg_replace('/[^0-9]/', '', $value);
if (strlen($clean) == 10
|| strlen($clean) == 11) {
return true;
}
return false;
}
if ($type == 'text') {
return true;
}
if ($type == 'email') {
$email = filter_var($value, FILTER_VALIDATE_EMAIL);
if ($email === false) {
return false;
}
return true;
}
return true;
}
public function getInputs()
{
$xPath = new DOMXpath($this->doc);
$htmlInputs = $xPath->query('//*[@name]');
$inputs = [];
foreach ($htmlInputs as $input) {
if ($input->tagName == 'form') {
continue;
}
$data = [];
$data['name'] = $input->getAttribute('name');
$data['required'] = $input->hasAttribute('required');
$data['type'] = $input->getAttribute('type');
$data['tag'] = $input->tagName;
$data['input'] = $input;
if (isset($inputs[$data['name']])) {
throw new \Exception("Input '" . $data['name'] . "' occurs twice on the form, which FormTools doesn't know how to handle.");
}
$inputs[$data['name']] = $data;
continue;
}
return $inputs;
}
public function getFormName()
{
$forms = $this->doc->getElementsByTagName('form');
if ($forms->count() > 1) {
throw new \Exception("There are two forms... Form name cannot be retrieved.");
} else if ($forms->count() == 0) {
throw new \Exception("There are no forms. Cannot get form name.");
}
$name = $forms->item(0)->getAttribute('name');
if ($name == null) {
$name = null;
}
return $name;
}
public function insertPlaceholders($placeholders)
{
$xPath = new DOMXpath($this->doc);
$htmlInputs = $xPath->query('//*[@name]');
$formName = $this->getFormName();
foreach ($htmlInputs as $input) {
if ($input->tagName == 'form') {
// $input->getAttribute('name');
continue;
}
$name = $input->getAttribute('name');
$ph = $placeholders[$name] ?? null;
// var_dump($name,$ph);
if ($ph === null) {
continue;
}
$input->setAttribute('placeholder', $ph);
}
}
public function nodeInnerHtml($node){
$innerHTML = '';
$children = $node->childNodes;
foreach ($children as $child) {
$innerHTML .= $child->ownerDocument->saveXML( $child );
}
return $innerHTML;
}
public function innerBodyHTML(){
// this was in my old FormsPlus code...
$output = $this->doc->saveHtml($this->doc->childNodes[1]->childNodes[0]);
$output = substr($output,strlen('<body>'),-strlen('</body>'));
return $output;
}
public function __toString()
{
return $this->doc->saveHtml();
}
public function RDBPrepareEdit($tableName = null, $id = null)
{
// Saving this from my old FormsPlus... sets the source for an img element that relates to an input[type=file]
// if ($tagName=='input'&&strtolower($input->getAttribute('type'))=='file'){
// $queryStr = "//*[@data-inputname='{$attr}']";
// // echo "\n{$queryStr}\n\n";
// $holderList = $xPath->query($queryStr);
// // print_r($imgHolder);
// $fileHolder = $holderList[0];
// $fileHolder->setAttribute('src',$this->src->$attr);
// // exit;
// }
$tableName = $tableName===null?null: preg_replace('/[^a-z]/','',strtolower($tableName));
$domDoc = $this->doc;
$xPath = new DOMXpath($domDoc);
$htmlInputs = $xPath->query('//*[@name]');
$formName = $tableName ?? $this->getFormName();
$bean = \RDB::findOne($formName, 'id = ?', [$id ?? $_GET['id'] ?? null]);
if ($bean == null) {
$bean = \RDB::dispense($formName);
}
foreach ($htmlInputs as $input) {
if ($input->tagName == 'form') {
continue;
}
$name = $input->getAttribute('name');
$value = $bean->$name ?? null;
if ($value !== null) {
// this MIGHT need to be textContent for a <textarea>
$this->setFormInputValue($input,$value);
}
}
// return $domDoc->saveHTML();
}
public function setFormInputValue($node,$value){
$tagName = strtolower($node->tagName);
$type = strtolower($node->getAttribute('type'));
if ($tagName=='textarea'){
$node->innerHTML = $value;
} else if ($tagName=='input'){
$node->setAttribute('value',$value);
}
return true;
}
public function RDBSubmit($postData, $formName = null)
{
$fields = $this->getInputs();
$formName = $formName ?? $this->getFormName();
foreach ($fields as $index => $data) {
extract($data);
$pass = $this->verify($data, $postData[$name] ?? null);
if (!$pass) {
throw new \Exception("Input '{$name}' failed verification");
}
$saveData[$name] = $postData[$name];
unset($postData[$name]);
}
if (count($postData) > 0) {
throw new \Exception("More data was sent to the server than was requested.");
}
$bean = \RDB::dispense($formName);
$bean->import($saveData);
\RDB::store($bean);
}
public static function uploadFile($file, $destinationFolder, $validExts = ['jpg', 'png'], $maxMB = 15)
{
if (!is_array($file) || $file == []
|| $file['size'] == 0
|| $file['name'] == ''
|| $file['tmp_name'] == ''
|| !is_int($file['error'])) {
return false;
}
try {
if (!isset($file['error']) ||
is_array($file['error'])
) {
throw new RuntimeException('Invalid parameters.');
}
switch ($file['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_NO_FILE:
throw new RuntimeException('No file sent.');
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
throw new RuntimeException('Exceeded filesize limit.');
default:
throw new RuntimeException('Unknown errors.');
}
// You should also check filesize here.
if ($file['size'] > ($maxMB * 1024 * 1024)) {
throw new RuntimeException('Exceeded filesize limit.');
}
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
if (!in_array($ext, $validExts)) {
// var_dump($ext);
// var_dump($validExts);
// var_dump($file);
// exit;
throw new RuntimeException('Invalid file format.');
}
if (!file_exists($destinationFolder)) {
mkdir($destinationFolder, 0775, true);
}
$fileName = sha1_file($file['tmp_name']) . '.' . $ext;
if (!move_uploaded_file(
$file['tmp_name'],
$destinationFolder . '/' . $fileName
)
) {
throw new RuntimeException('Failed to move uploaded file.');
}
return $fileName;
} catch (RuntimeException $e) {
throw $e;
}
}
}