* @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 ''; } }