* @category class
* @package core
* @subpackage form
* @copyright 2005-2017 Titan Framework
* @license http://www.titanframework.com/license/ BSD License (3 Clause)
* @see View, Search
*/
class Form
{
/**
* This variable assure the existance of a unique Form object instance for
* each form artefact in instance.
*
* @var Form array
* @access private
* @static
*/
static private $forms = array ();
/**
* Form unique indentification.
*
* @var string
* @access protected
*/
protected $assign = NULL;
/**
* XML file name on section path.
*
* @var string
* @access protected
*/
protected $file = '';
/**
* Name of table primary key.
*
* @var string
* @access protected
*/
protected $primary = '';
/**
* Name of DB table.
*
* @var string
* @access protected
*/
protected $table = '';
/**
* Navigation definition array.
* Control layer.
*
* @var array
* @access protected
*/
protected $go = array ();
/**
* Loaded fields from XML.
*
* @var Type array
* @access protected
*/
protected $fields = array ();
/**
* Fields in groups mapping array.
*
* @var array
* @access protected
*/
protected $groups = array ();
/**
* Loaded fields groups definitions.
*
* @var array
* @access protected
*/
protected $groupsInfo = array ();
/**
* Specify if form is loaded.
*
* @var boolean
* @access protected
*/
protected $loaded = FALSE;
/**
* Actual ID value for loaded DB object.
*
* @var integer
* @access protected
*/
protected $itemId = 0;
/**
* Class constructor.
* Designed for use in singleton or direct access.
* In singleton the unique instance of object will be guaranteed by
* assign and forms array variable.
*
* @see singleton ()
*/
public function __construct ($files)
{
$section = Business::singleton ()->getSection (Section::TCURRENT);
$action = Business::singleton ()->getAction (Action::TCURRENT);
$fileName = FALSE;
if (!is_array ($files))
$files = array ($files);
foreach ($files as $trash => $file)
{
if (!file_exists ('section/'. $section->getName () .'/'. $file) || is_dir ('section/'. $section->getName () .'/'. $file))
continue;
$fileName = $file;
break;
}
if ($fileName === FALSE)
throw new Exception ('XML file not found in [section/'. $section->getName () .'/].');
$file = 'section/'. $section->getName () .'/'. $fileName;
$cacheFile = Instance::singleton ()->getCachePath () .'parsed/'. fileName ($file) .'_'. md5_file ($file) .'.php';
if (file_exists ($cacheFile))
$array = include $cacheFile;
else
{
$xml = new Xml ($file);
$array = $xml->getArray ();
if (!isset ($array ['form'][0]))
throw new Exception ('A tag <form></form> não foi encontrada no XML ['. $fileName .']!');
xmlCache ($cacheFile, $array);
}
if (!array_key_exists ('form', $array))
throw new Exception ('Invalid XML Form file [section/'. $section->getName () .'/].');
$array = $array ['form'][0];
$this->assign = md5 ($section->getName () .'.'. $action->getName () .'.'. $fileName);
$this->file = $fileName;
if (array_key_exists ('table', $array))
$this->table = $array ['table'];
if (array_key_exists ('primary', $array))
$this->primary = $array ['primary'];
if (array_key_exists ('go-to', $array) && is_array ($array ['go-to']))
foreach ($array ['go-to'] as $trash => $go)
{
if (!array_key_exists ('flag', $go) || !array_key_exists ('action', $go))
continue;
$this->go [$go ['flag']] = $go ['action'];
}
$user = User::singleton ();
$groupId = 0;
$this->groupsInfo [$groupId] = array ();
if (array_key_exists ('field', $array) && is_array ($array ['field']))
foreach ($array ['field'] as $trash => $field)
if ($obj = Type::factory ($this->getTable (), $field))
{
while ($perm = $obj->getRestrict ())
if (!$user->hasPermission ($perm))
continue 2;
$this->fields [$obj->getAssign ()] = $obj;
$this->groups [$groupId][] = $obj->getAssign ();
}
if (array_key_exists ('group', $array) && is_array ($array ['group']))
foreach ($array ['group'] as $trash => $group)
{
$groupId++;
if (array_key_exists ('label', $group))
$label = $group ['label'];
else
$label = '';
if (array_key_exists ('display', $group))
$display = $group ['display'];
else
$display = 'visible';
if (array_key_exists ('info', $group))
$info = $group ['info'];
else
$info = '';
$this->groupsInfo [$groupId] = array ($groupId, $label, $display, $info);
if (array_key_exists ('field', $group) && is_array ($group ['field']))
foreach ($group ['field'] as $trash => $field)
if ($obj = Type::factory ($this->getTable (), $field))
{
while ($perm = $obj->getRestrict ())
if (!$user->hasPermission ($perm))
continue 2;
$this->fields [$obj->getAssign ()] = $obj;
$this->groups [$groupId][] = $obj->getAssign ();
}
}
reset ($this->fields);
reset ($this->groupsInfo);
reset ($this->groups);
}
public function __call ($method, $arguments)
{
if ($method == 'goTo')
return $this->goToAction (@$arguments [0]);
}
/**
* Singleton function.
*
* @return Database
* @static
* @see $forms
*/
static public function singleton ()
{
$files = func_get_args ();
$class = __CLASS__;
$action = Business::singleton ()->getAction (Action::TCURRENT);
if ($action->getXmlPath () !== FALSE && trim ($action->getXmlPath ()) != '')
array_unshift ($files, $action->getXmlPath ());
array_push ($files, $action->getName (), $action->getEngine ());
$form = new $class ($files);
if (array_key_exists ($form->getAssign (), self::$forms))
return self::$forms [$form->getAssign ()];
self::$forms [$form->getAssign ()] =& $form;
return $form;
}
/**
* Get XML section file name.
*
* @return string
*/
public function getFile ()
{
return $this->file;
}
public function getTable ()
{
return $this->table;
}
public function setTable ($table)
{
$this->table = $table;
}
public function getPrimary ()
{
return $this->primary;
}
public function getAssign ()
{
return $this->assign;
}
public function setAssign ($assign)
{
$this->assign = $assign;
}
public function setLoad ($status = TRUE)
{
$this->loaded = $status;
}
public function isLoaded ()
{
return (bool) $this->loaded;
}
public function getId ()
{
return $this->itemId;
}
public function setId ($itemId)
{
$this->itemId = $itemId;
}
public function addField ($field, $group = 0)
{
if (!array_key_exists ($group, $this->groups))
$group = 0;
if (is_object ($field))
{
$this->fields [$field->getAssign ()] = $field;
$this->groups [$group][] = $field->getAssign ();
}
elseif ($obj = Type::factory ($this->getTable (), $field))
{
while ($perm = $obj->getRestrict ())
if (!$user->hasPermission ($perm))
return FALSE;
$this->fields [$obj->getAssign ()] = $obj;
$this->groups [$group][] = $obj->getAssign ();
return TRUE;
}
return FALSE;
}
public function removeField ()
{
$array = func_get_args ();
foreach ($array as $trash => $field)
{
if (is_object ($field))
$field = $field->getAssign ();
if (!array_key_exists ($field, $this->fields))
continue;
unset ($this->fields [$field]);
}
return TRUE;
}
public function goToAction ($flag = FALSE)
{
global $section;
if ($flag != 'fail' && isset ($_GET['goTo']) && array_key_exists ($_GET['goTo'], $this->go))
$flag = $_GET['goTo'];
elseif ($flag === FALSE || !array_key_exists ($flag, $this->go))
return $section->getAction (Action::TDEFAULT);
switch ($this->go [$flag])
{
case '[default]':
return $section->getAction (Action::TDEFAULT);
case '[same]':
return $section->getAction (@$_GET ['toAction']);
default:
return $section->getAction ($this->go [$flag]);
}
}
public function getFields ()
{
return $this->fields;
}
public function getUniques ()
{
$uniques = array ();
foreach ($this->fields as $key => $field)
if ($field->isUnique ())
$uniques [$key] = $field;
return $uniques;
}
public function getRequireds ()
{
$requireds = array ();
foreach ($this->fields as $key => $field)
if ($field->isRequired ())
$requireds [$key] = $field;
return $requireds;
}
public function recovery ($formData = FALSE)
{
if ($this->isLoaded ())
return TRUE;
if (is_array ($formData))
foreach ($formData as $key => $value)
{
if (is_string ($value))
$formData [$key] = $value;
}
else
$formData = $_POST;
foreach ($formData as $assign => $value)
{
if (!array_key_exists ($assign, $this->fields))
continue;
if ($this->fields [$assign]->isReadOnly ())
$this->fields [$assign]->setValue (unserialize (base64_decode ($value)));
else
$this->fields [$assign]->setValue (self::fromForm ($this->fields [$assign], $value));
}
$this->setLoad ();
return TRUE;
}
public function load ($sql, $ownOnly = FALSE)
{
if ($this->isLoaded ())
return TRUE;
if (is_numeric ($sql))
{
$itemId = $sql;
$this->setId ($itemId);
$fields = array ();
foreach ($this->fields as $assign => $field)
if ($field->isLoadable ())
$fields [] = Database::toSql ($field);
if (!sizeof ($fields))
{
reset ($this->fields);
$this->setLoad ();
return TRUE;
}
$sql = "SELECT ". implode (', ', $fields) ." FROM ". $this->getTable () ." WHERE ". $this->getPrimary () ." = '". $itemId ."'";
if ($ownOnly)
$sql .= " AND _user = '". User::singleton ()->getId () ."'";
}
//throw new Exception ($sql);
$db = Database::singleton ();
$sth = $db->prepare ($sql);
$sth->execute ();
$obj = $sth->fetch (PDO::FETCH_OBJ);
if (!$obj)
return FALSE;
foreach ($this->fields as $assign => $field)
if ($field->isLoadable ())
$this->fields [$assign] = Database::fromDb ($field, $obj);
elseif (isset ($itemId))
$this->fields [$assign]->load ($itemId);
reset ($this->fields);
$this->setLoad ();
return TRUE;
}
public function save ($itemId = 0, $useLog = TRUE)
{
$fields = array ();
$values = array ();
$binds = array ();
foreach ($this->fields as $key => $field)
if (!$field->isReadOnly () && $field->isSavable ())
{
$assign = $field->getAssign ();
$fields [$assign] = $field->getColumn ();
if ($field->getBind ())
{
$values [$assign] = ":". $field->getColumn ();
$binds [$assign] = Database::toBind ($field);
$types [$assign] = $field->getBindType ();
if (method_exists ($field, 'getMaxLength'))
$sizes [$assign] = (int) $field->getMaxLength ();
else
$sizes [$assign] = 0;
}
else
$values [$assign] = Database::toValue ($field);
}
// Legacy code. Old pattern to save last date of update still used by mandatory '_user' table.
if ($useLog && array_pop (explode ('.', $this->getTable ())) == '_user')
{
$fields [] = '_update_date';
$values [] = 'NOW()';
}
$mandatory = Database::getMandatoryColumns ($this->getTable ());
foreach ($mandatory as $trash => $column)
{
$aux = $this->getFieldByColumn ($column);
if (is_null ($aux) || !is_object ($aux) || !$aux->isSavable ())
continue;
$$column = $aux;
}
if (in_array ('_user', $mandatory) && !isset ($_user))
{
$fields [] = '_user';
$values [] = User::singleton ()->getId ();
}
if (in_array ('_update', $mandatory) && !isset ($_update))
{
$fields [] = '_update';
$values [] = 'NOW()';
}
if (in_array ('_change', $mandatory) && !isset ($_change))
{
$fields [] = '_change';
$values [] = 'NOW()';
}
if (!is_numeric ($itemId) || (int) $itemId)
{
$aux = array ();
foreach ($fields as $key => $field)
$aux [] = $field ." = ". $values [$key];
$sql = "UPDATE ". $this->getTable () ." SET ". implode (", ", $aux) ." WHERE ". $this->getPrimary () ." = '". $itemId ."'";
}
else
{
if (in_array ('_author', $mandatory) && !isset ($_author))
{
$fields [] = '_author';
$values [] = User::singleton ()->getId ();
}
$itemId = Database::nextId ($this->getTable (), $this->getPrimary ());
$sql = "INSERT INTO ". $this->getTable () ." (". $this->getPrimary () .", ". implode (", ", $fields) .") VALUES (". $itemId .", ". implode (", ", $values) .")";
}
// throw new Exception ($sql);
$db = Database::singleton ();
$sth = $db->prepare ($sql);
foreach ($binds as $assign => $trash)
if ($sizes [$assign] && $types [$assign] == PDO::PARAM_STR)
$sth->bindParam ($values [$assign], $binds [$assign], $types [$assign], $sizes [$assign]);
else
$sth->bindParam ($values [$assign], $binds [$assign], $types [$assign]);
if (!$sth->execute ())
return FALSE;
$this->setId ($itemId);
Lucene::singleton ()->save ($itemId, $this->getResume ($itemId, TRUE));
foreach ($this->fields as $key => $field)
if (!$field->isSavable ())
$field->save ($itemId);
reset ($this->fields);
return $itemId;
}
public function saveSession ($itemId = 0)
{
if (!isset ($_SESSION['_TITAN_FORMS_FOR_SAVE_']))
return FALSE;
$forms = $_SESSION['_TITAN_FORMS_FOR_SAVE_'];
if (array_key_exists ('_ITEM_ID_', $forms) && $forms ['_ITEM_ID_'])
$itemId = $forms ['_ITEM_ID_'];
elseif ($itemId)
$_SESSION['_TITAN_FORMS_FOR_SAVE_']['_ITEM_ID_'] = $itemId;
else
throw new Exception ('Deve existir uma tupla para este item no BD. Houve falha no salvamento dos dados.');
if (!$this->save ($itemId))
return FALSE;
unset ($forms [$this->getAssign ()]);
unset ($forms ['_ITEM_ID_']);
foreach ($forms as $assign => $file)
{
$form = new Form ($file);
$form->setAssign ($assign);
$form->loadFromSession ();
if (!$form->save ($itemId))
throw new Exception ('Houve falha no salvamento de pelo menos um passo! Tente novamente.');
unset ($_SESSION['_TITAN_FORMS_FOR_SAVE_'][$assign]);
}
return TRUE;
}
public function saveOnSession ()
{
$fields = array ();
foreach ($this->fields as $key => $field)
$fields [$field->getAssign ()] = $field->getValue ();
reset ($this->fields);
$_SESSION['_TITAN_FORMS_']['_TITAN_FORM_'. $this->getAssign () .'_'] = serialize ($fields);
if (!array_key_exists ('_TITAN_FORMS_FOR_SAVE_', $_SESSION) || !array_key_exists ($this->getAssign (), $_SESSION['_TITAN_FORMS_FOR_SAVE_']))
$_SESSION['_TITAN_FORMS_FOR_SAVE_'][$this->getAssign ()] = $this->getFile ();
return TRUE;
}
public function loadFromSession ($itemId)
{
if ($this->isLoaded ())
return TRUE;
if (!isset ($_SESSION['_TITAN_FORMS_']['_TITAN_FORM_'. $this->getAssign () .'_']))
{
if (is_numeric ($itemId) && (int) $itemId)
{
$fields = array ();
foreach ($this->fields as $assign => $field)
if ($field->isLoadable ())
$fields [] = Database::toSql ($field);
if (!sizeof ($fields))
{
reset ($this->fields);
$this->setLoad ();
return TRUE;
}
$sql = "SELECT ". implode (', ', $fields) ." FROM ". $this->getTable () ." WHERE ". $this->getPrimary () ." = '". $itemId ."'";
//throw new Exception ($sql);
$db = Database::singleton ();
$sth = $db->prepare ($sql);
$sth->execute ();
$obj = $sth->fetch (PDO::FETCH_OBJ);
if (!$obj)
return FALSE;
foreach ($this->fields as $assign => $field)
if ($field->isLoadable ())
$this->fields [$assign] = Database::fromDb ($field, $obj);
$_SESSION['_TITAN_FORMS_FOR_SAVE_']['_ITEM_ID_'] = $itemId;
}
$this->saveOnSession ();
}
else
{
$fields = unserialize ($_SESSION['_TITAN_FORMS_']['_TITAN_FORM_'. $this->getAssign () .'_']);
foreach ($fields as $assign => $value)
if (array_key_exists ($assign, $this->fields))
$this->fields [$assign]->setValue ($value);
}
$this->setLoad ();
return TRUE;
}
public function saveOnLdap ($ldap, $fields = FALSE, $uid = FALSE)
{
if (!is_array ($fields))
$fields = array ();
foreach ($this->fields as $assign => $field)
{
if (!$uid && $field->getLdap () == 'uid')
$uid = Ldap::toLdap ($field);
if ($field->isLdapField () && !$field->isReadOnly ())
$fields [$field->getLdap ()] = Ldap::toLdap ($field);
}
if (!sizeof ($fields))
return TRUE;
if (!$ldap->isConnected ())
$ldap->connect (FALSE, FALSE, TRUE);
if (!$ldap->userExists ($uid))
{
$ldap->close ();
throw new Exception ('O usuário não existe no servidor LDAP!');
}
$ldap->update ($fields, $uid);
$ldap->close ();
return TRUE;
}
public function createLdapUser ($uid, $ldap, $fields = FALSE)
{
if (!$ldap->isConnected ())
$ldap->connect (FALSE, FALSE, TRUE);
if (!is_array ($fields))
$fields = array ();
foreach ($this->fields as $assign => $field)
if ($field->isLdapField ())
$fields [$field->getLdap ()] = Ldap::toLdap ($field);
if ($ldap->userExists ($uid))
{
$ldap->close ();
throw new Exception ('O usuário já existe no servidor LDAP!');
}
$ldap->create ($fields, $uid);
$ldap->close ();
return TRUE;
}
public function deleteFromLdap ($ldap, $uid = FALSE)
{
if (!$ldap->isConnected ())
$ldap->connect (FALSE, FALSE, TRUE);
if (!$uid)
foreach ($this->fields as $assign => $field)
if ($field->isLdapField () && $field->getLdap () == 'uid')
{
$uid = Ldap::toLdap ($field);
break;
}
if (!$ldap->userExists ($uid))
{
$ldap->close ();
Message::singleton ()->addWarning ('O usuário não existe no servidor LDAP: '. $uid);
return FALSE;
}
$ldap->delete ($uid);
$ldap->close ();
return TRUE;
}
public function loadFromLdap ($uid, $ldap)
{
if (!$ldap->isConnected ())
$ldap->connect (FALSE, FALSE, FALSE);
$search = array ();
foreach ($this->fields as $assign => $field)
if ($field->isLdapField ())
$search [] = $field->getLdap ();
$ldapUser = $ldap->load ($uid, $search);
foreach ($this->fields as $assign => $field)
if ($field->isLdapField () && array_key_exists (strtolower ($field->getLdap ()), $ldapUser))
$this->fields [$assign] = Ldap::fromLdap ($field, $ldapUser [$field->getLdap ()]);
$ldap->close ();
return TRUE;
}
public function delete ($itemId = 0, $permanent = TRUE)
{
if (is_numeric ($itemId) && !(int) $itemId)
return FALSE;
if ($permanent)
$sql = "DELETE FROM ". $this->getTable () ." WHERE ". $this->getPrimary () ." = '". $itemId ."'";
else
$sql = "UPDATE ". $this->getTable () ." SET _deleted = '1' WHERE ". $this->getPrimary () ." = '". $itemId ."'";
$db = Database::singleton ();
$sth = $db->prepare ($sql);
if (!$sth->execute ())
return FALSE;
Lucene::singleton ()->delete ($itemId);
return TRUE;
}
public function getResume ($itemId = 0, $friendly = FALSE)
{
if (!$this->isLoaded () && !$this->load ($itemId))
return '[Impossível carregar informações do item #'. $itemId .']';
$resume = "";
if (!$friendly)
$resume .= "[ID# ". $itemId ."] \n\n";
while ($group = $this->getGroup ())
{
if ($group->getId ())
$resume .= "> ". $group->getLabel () ."\n\n";
while ($field = $this->getField (FALSE, $group->getId ()))
$resume .= (trim ($field->getLabel ()) != '' ? $field->getLabel () .": \n" : '') . self::toText ($field) ." \n\n";
}
reset ($this->fields);
return $resume;
}
public function getField ($assign = FALSE, $group = FALSE)
{
if ($assign !== FALSE)
{
if (!array_key_exists ($assign, $this->fields))
return NULL;
$field = $this->fields [$assign];
if ($group !== FALSE)
unset ($this->fields [$assign]);
return $field;
}
$field = each ($this->fields);
while ($field !== FALSE)
{
if ($group === FALSE || (array_key_exists ($group, $this->groups) && in_array ($field ['value']->getAssign (), $this->groups [$group])))
return $field ['value'];
$field = each ($this->fields);
}
reset ($this->fields);
return NULL;
}
public function getFieldByColumn ($column)
{
foreach ($this->fields as $assign => $field)
if ($field->getColumn () == $column)
return $field;
return NULL;
}
public function getGroup ($id = FALSE)
{
if ($id !== FALSE)
{
if (!array_key_exists ($id, $this->groupsInfo))
return NULL;
return new Group ($this->groupsInfo [$id]);
}
$group = each ($this->groupsInfo);
if ($group !== FALSE)
return new Group ($group ['value']);
reset ($this->groupsInfo);
return NULL;
}
public static function toForm ($field, $scope = '')
{
if (!is_object ($field))
return '';
$fieldName = $field->getAssign ();
$fieldId = (trim ($scope) == '' ? 'field' : $scope) .'_'. $field->getAssign ();
if ($field->isReadOnly ())
return self::toHtml ($field) .' ';
$instance = Instance::singleton ();
$db = Database::singleton ();
$type = get_class ($field);
do
{
$file = $instance->getTypePath ($type) .'toForm.php';
if (file_exists ($file))
return include $file;
$type = get_parent_class ($type);
} while ($type != 'Type' && $type !== FALSE);
return '';
}
public static function fromForm ($field, $value)
{
if (!is_object ($field))
return $value;
$instance = Instance::singleton ();
$type = get_class ($field);
do
{
$file = $instance->getTypePath ($type) .'fromForm.php';
if (file_exists ($file))
return include $file;
$type = get_parent_class ($type);
} while ($type != 'Type' && $type !== FALSE);
return $value;
}
public static function toLabel ($field, $showRequired = FALSE)
{
if (trim ($field->getLabel ()) == '')
return ' ';
if ($showRequired)
return ($field->isRequired () ? '' : '') . $field->getLabel ();
return $field->getLabel ();
}
public static function toHtml ($field)
{
if (!is_object ($field))
return $field;
$instance = Instance::singleton ();
$fieldId = 'field_'. $field->getAssign ();
$db = Database::singleton ();
$type = get_class ($field);
do
{
$file = $instance->getTypePath ($type) .'toHtml.php';
if (file_exists ($file))
return include $file;
$type = get_parent_class ($type);
} while ($type != 'Type' && $type !== FALSE);
return $field->getValue ();
}
public static function toText ($field)
{
if (!is_object ($field))
return $field;
$instance = Instance::singleton ();
$fieldId = 'field_'. $field->getAssign ();
$db = Database::singleton ();
$type = get_class ($field);
do
{
$file = $instance->getTypePath ($type) .'toText.php';
if (file_exists ($file))
return include $file;
$type = get_parent_class ($type);
} while ($type != 'Type' && $type !== FALSE);
$type = get_class ($field);
do
{
$file = $instance->getTypePath ($type) .'toHtml.php';
if (file_exists ($file))
return include $file;
$type = get_parent_class ($type);
} while ($type != 'Type' && $type !== FALSE);
return strip_tags ($field->getValue ());
}
public static function toHelp ($field)
{
if (trim ($field->getHelp ()) == '')
return ' ';
return '';
}
}