Преглед изворни кода

updated edit form for Table and Ant acl

Piotr Labudda пре 7 година
родитељ
комит
c931ec6299
4 измењених фајлова са 464 додато и 186 уклоњено
  1. 291 15
      SE/se-lib/AntAclBase.php
  2. 21 7
      SE/se-lib/TableAcl.php
  3. 1 1
      SE/se-lib/TableAjax.php
  4. 151 163
      SE/se-lib/UI.php

+ 291 - 15
SE/se-lib/AntAclBase.php

@@ -17,6 +17,7 @@ class AntAclBase extends Core_AclBase {
 		$this->_rootNamespace = '';
 		$this->_primaryKey = '';
 		$this->_fields = [];
+		$this->_xsdRestrictions = [];
 		$this->_zasobyInfoFetched = false;
 	}
 	public function getDB() { return $this->_db; }
@@ -168,13 +169,12 @@ class AntAclBase extends Core_AclBase {
 	}
 	public function getFieldIdByName($fieldName) {
 		if (!$fieldName) return null;
-		$idZasob = null;
 		foreach ($this->getFields() as $field) {
-			if ($fieldName === $field['name']) {
-				if ($field['idZasob']) $idZasob = $field['idZasob'];
-			}
+			if ($fieldName !== $field['name']) continue;
+			if (!$field['idZasob']) continue;
+			return $field['idZasob'];
 		}
-		return $idZasob;
+		return null;
 	}
 
 	public function getFieldType($fieldName) { return null; }
@@ -203,17 +203,38 @@ class AntAclBase extends Core_AclBase {
 	}
 	public function getXsdMaxOccurs($fieldName) {
 		$field = $this->_getField($fieldName);
-		return $field['maxOccurs'];
+		return (int)$field['maxOccurs'];
 	}
 	public function getXsdMinOccurs($fieldName) {
 		$field = $this->_getField($fieldName);
-		return $field['minOccurs'];
+		return (int)$field['minOccurs'];
 	}
 	public function getAttributesFromZasoby() {
 		return [];// TODO: ...
 	}
-	public function getXsdFieldParam($fieldName, $paramKey) { // TODO: fetch from db
-		return null;
+	public function getXsdFieldParam($fieldName, $paramKey) {
+		switch ($paramKey) {
+			case 'enumeration': return $this->getEnumerations($fieldName);
+		}
+		$xsdType = $this->getXsdFieldType($fieldName);
+		$defaultValue = null;
+		if ('enumeration' === $paramKey && 'p5:enum' === $xsdType) $defaultValue = [];
+		if ('maxLength' === $paramKey && 'xsd:string' === $xsdType) $defaultValue = 255;
+		return V::get($paramKey, $defaultValue, $this->getXsdRestrictions($fieldName));
+	}
+	public function getEnumerations($fieldName) {
+		$restrictions = $this->getXsdRestrictions($fieldName);
+		return V::get('enumeration', [], $restrictions, 'array');
+	}
+	public function getXsdRestrictions($fieldName) {
+		if (array_key_exists($fieldName, $this->_xsdRestrictions)) return $this->_xsdRestrictions[$fieldName];
+		$this->_xsdRestrictions[$fieldName] = [];
+		$field = $this->_getField($fieldName);
+		if (!$field['xsdRestrictions']) return [];
+		if (is_string($field['xsdRestrictions']) && '{' === substr($field['xsdRestrictions'], 0, 1)) {
+			$this->_xsdRestrictions[$fieldName] = @json_decode($field['xsdRestrictions'], $assoc = true);
+		}
+		return $this->_xsdRestrictions[$fieldName];
 	}
 	// public function getXsdFieldParam($fieldName, $paramKey) { // TableAcl
 	// 	return ($this->_schemaClass)
@@ -228,16 +249,79 @@ class AntAclBase extends Core_AclBase {
 	//   return $this->_simpleSchema['root'][$fieldName]['@@params'][$paramKey];
 	// }
 
+	public function getFieldDefaultValue($fieldName) { // TODO: get dafault value from xsd file - TODO: p5:default attribute
+		// TODO: get from xsd file (acl cache field)
+		// TODO: if not set then:
+		// - 'NULL' for nillable fields
+		// - '0' for xsd:integer, xsd:decimal
+		// - '' else ...
+		return '';
+	}
+
+	public function convertObjectFromUserInput($userItem, $type = 'array_by_id', $prefix = 'f') {// TODO: rename / Legacy
+		$item = $this->parseUserItem($userItem, $type = 'array_by_id', $prefix = 'f');
+		foreach ($item as $fieldName => $value) {
+			$item[$fieldName] = $this->validateAndFixField($fieldName, $value);
+		}
+		DBG::log(['userItem' => $userItem, 'item' => $item], 'array', "after parseUserItem, validateAndFixField");
+		return $item;
+	}
+	public function parseUserItem($userItem, $type = 'array_by_id', $prefix = 'f') {
+		$item = [];
+		foreach ($this->getFieldListByIdZasob() as $userKey => $fieldName) {
+			if (!array_key_exists("f{$userKey}", $userItem)) continue;
+			$item[$fieldName] = $userItem["f{$userKey}"];
+		}
+		return $item;
+	}
+	public function validateAndFixField($fieldName, $value) {
+		if (empty($value) && 0 === strlen($value)) {// TODO: fixEmptyValueFromUser
+			return $this->fixFieldEmptyValue($fieldName);
+		}
+		$xsdType = $this->getXsdFieldType($fieldName);
+		switch ($xsdType) {
+			case 'xsd:decimal': return str_replace([',', ' '], ['.', ''], $value);
+			case 'p5:price': return V::convert($value, 'price');
+		}
+		return $value;
+	}
+	public function fixFieldEmptyValue($fieldName) {// TODO: legacy - TODO: FIX
+		return $this->getFieldDefaultValue($fieldName);
+		// $value = '';
+		// $xsdType = $this->getXsdFieldType($fieldName);
+		// // $type = $this->getFieldType($fieldName); // TODO: RM
+		// // if (!$type) return '';
+		// if ('xsd:date' === $xsdType) return $this->getFieldDefaultValue($fieldName);
+		// if ('xsd:integer' === $xsdType) return (int)$this->getFieldDefaultValue($fieldName);
+		// // fix bug when field is unique and is null allowed: change empty string to null
+		// if ($type['null']) {
+		// 	$value = 'NULL';
+		// }
+		// // fix bug when field is enum and is set to '0': for php '0' is empty
+		// if (substr($type['type'], 0, 4) == 'enum') {// && $args["f{$fieldID}"] === '0') {
+		// 	// if (false !== strpos($type['type'], "''")) {
+		// 	// 	// enum('', '1','2')
+		// 	// 	$value = '';
+		// 	// } else if (false !== strpos($type['type'], "'0'")) {
+		// 	// 	// enum('0', '1','2')
+		// 	// 	$value = '0';
+		// 	// } else {
+		// 		$value = $this->getFieldDefaultValue($fieldName);
+		// 	// }
+		// }
+		// return $value;
+	}
+
+
 	public function isGeomField($fieldName) {
 		return ('the_geom' === $fieldName); // TODO: check by xsdType
 	}
 	public function isEnumerationField($fieldName) {
-		return false; // TODO: ...
+		$xsdType = $this->getXsdFieldType($fieldName);
+		if ('p5:enum' === $xsdType) return true;
+		return false;
 	}
 
-	public function canWriteField($fieldName) {
-		return false; // TODO: $this->getAclInfo($fieldName); @see canReadField
-	}
 	public function canCreateField($fieldName) {
 		try {
 			$fieldAclInfo = $this->getAclInfo($fieldName);
@@ -251,6 +335,11 @@ class AntAclBase extends Core_AclBase {
 	}
 	public function canReadField($fieldName) {
 		try {
+			if ('A_RECORD_CREATE_DATE' === $fieldName) return true;
+			if ('A_RECORD_CREATE_AUTHOR' === $fieldName) return true;
+			if ('A_RECORD_UPDATE_DATE' === $fieldName) return true;
+			if ('A_RECORD_UPDATE_AUTHOR' === $fieldName) return true;
+			if ($this->getPrimaryKeyField() === $fieldName) return true;
 			$fieldAclInfo = $this->getAclInfo($fieldName);
 			DBG::log($fieldAclInfo, 'array', "AntAclBase: canReadField({$fieldName})...");
 			return ($fieldAclInfo['PERM_R'] > 0 || $fieldAclInfo['PERM_V'] > 0 || $fieldAclInfo['PERM_O'] > 0);
@@ -261,10 +350,43 @@ class AntAclBase extends Core_AclBase {
 		return false;
 	}
 	public function canReadObjectField($fieldName, $object) {
-		return true; // TODO: $this->getAclInfo($fieldName); @see canReadField
+		try {
+			if ('A_RECORD_CREATE_DATE' === $fieldName) return true;
+			if ('A_RECORD_CREATE_AUTHOR' === $fieldName) return true;
+			if ('A_RECORD_UPDATE_DATE' === $fieldName) return true;
+			if ('A_RECORD_UPDATE_AUTHOR' === $fieldName) return true;
+			if ($this->getPrimaryKeyField() === $fieldName) return true;
+			$fieldAclInfo = $this->getAclInfo($fieldName);
+			DBG::log([$fieldAclInfo, 'V' => ($fieldAclInfo['PERM_V'] > 0), 'R'=>($fieldAclInfo['PERM_R'] > 0 && $this->canReadRecord($record)), 'O'=>($fieldAclInfo['PERM_O'] > 0 && $this->canReadRecord($record))], 'array', "AntAclBase: canWriteObjectField({$fieldName})...");
+			if ($fieldAclInfo['PERM_V'] > 0) return true;
+			if ($fieldAclInfo['PERM_R'] > 0 && $this->canReadRecord($record)) return true;
+			if ($fieldAclInfo['PERM_O'] > 0 && $this->canReadRecord($record)) return true;
+		} catch (Exception $e) {
+			DBG::log($e);
+		}
+		return false;
+	}
+	public function canWriteField($fieldName) {
+		try {
+			$fieldAclInfo = $this->getAclInfo($fieldName);
+			DBG::log($fieldAclInfo, 'array', "AntAclBase: canReadField({$fieldName})...");
+			if ($fieldAclInfo['PERM_W'] > 0 || $fieldAclInfo['PERM_S'] > 0) return true;
+		} catch (Exception $e) {
+			DBG::log($e);
+		}
+		return false;
 	}
 	public function canWriteObjectField($fieldName, $record) {
-		return false; // TODO: $this->getAclInfo($fieldName); @see canReadField
+		try {
+			$fieldAclInfo = $this->getAclInfo($fieldName);
+			DBG::log($fieldAclInfo, 'array', "AntAclBase: canWriteObjectField({$fieldName})...");
+			DBG::log([$fieldAclInfo, 'S' => $fieldAclInfo['PERM_S'] > 0, 'W'=>$fieldAclInfo['PERM_W'], 'canWrite'=>$this->canWriteRecord($record), $record], 'array', "AntAclBase: canWriteObjectField({$fieldName})...");
+			if ($fieldAclInfo['PERM_S'] > 0) return true;
+			if ($fieldAclInfo['PERM_W'] > 0 && $this->canWriteRecord($record)) return true;
+		} catch (Exception $e) {
+			DBG::log($e);
+		}
+		return false;
 	}
 	public function getAclInfo($fieldName = null) {
 		static $_aclInfo = []; // [ fieldName => [ id => {idZasob}, perms => 'RWX...' ] // TODO: , sort_prio => {SORT_PRIO}, label => {CELL_LABEL}  ]
@@ -290,6 +412,86 @@ class AntAclBase extends Core_AclBase {
 		return ($fieldName) ? $_aclInfo[ $this->getID() ][ $fieldName ] : $_aclInfo[ $this->getID() ];
 	}
 
+	public function hasFieldPerm($fieldID, $perm) { // TODO: legacy
+		$field = $this->getField();
+		if (!$field) return false;
+		try {
+			$fieldAclInfo = $this->getAclInfo($fieldName);
+			DBG::log($fieldAclInfo, 'array', "AntAclBase: hasFieldPerm({$fieldName})...");
+			if (!array_key_exists("PERM_{$perm}", $fieldAclInfo)) return false;
+			return ($fieldAclInfo["PERM_{$perm}"] > 0);
+		} catch (Exception $e) {
+			DBG::log($e);
+		}
+		return false;
+	}
+	public function isAllowed($fieldID, $taskPerm, $record = null) {// TODO: legacy - replace with canWriteField, canReadField, canWriteObjectField, canReadObjectField, canCreateField
+		$field = $this->getField($fieldID);
+		if (!$field) return false;
+		$fieldName = $field['name'];
+
+		switch ($taskPerm) {
+			case 'C': return false; // 'PERM_C'
+			case 'R': return ($record) ? $this->canReadObjectField($fieldName, $record) : $this->canReadField($fieldName);
+			case 'W': return ($record) ? $this->canWriteObjectField($fieldName, $record) : $this->canWriteField($fieldName);
+			default: throw new Exception("Not Implemented isAllowed perm '{$taskPerm}'");
+		}
+
+		$adminFields = array();
+		$adminFields[] = $this->getPrimaryKeyField();
+		$adminFields[] = 'A_RECORD_CREATE_DATE';
+		$adminFields[] = 'A_RECORD_CREATE_AUTHOR';
+		$adminFields[] = 'A_RECORD_UPDATE_DATE';
+		$adminFields[] = 'A_RECORD_UPDATE_AUTHOR';
+		if ($taskPerm == 'R' && in_array($fieldName, $adminFields)) return true;
+		if ($taskPerm == 'W' && in_array($fieldName, $adminFields)) return false;
+
+		// check perm: allow 'RS', 'WS' - can R/W field even if cant read record
+		// check 'O' - can read field even if cant read field but can read record
+		DBG::log([
+			'record' => $record,
+			'canReadRecord' => $this->canReadRecord($record),
+			'hasFieldPerm(O) || canWriteRecord'=>'"'.$this->hasFieldPerm($fieldID, 'O').'" || "'.$this->canReadRecord($record).'"',
+			'hasFieldPerm(S)'=>'"'.$this->hasFieldPerm($fieldID, 'S').'"',
+			'hasFieldPerm(V)'=>'"'.$this->hasFieldPerm($fieldID, 'V').'"',
+		], 'array', "isAllowed({$fieldName}[{$fieldID}], {$taskPerm})");
+
+		if (!$this->hasFieldPerm($fieldID, $taskPerm)) {
+			if ($taskPerm == 'R' && $this->hasFieldPerm($fieldID, 'V')) {
+				return true;
+			} else if ($taskPerm == 'R'
+				&& $record
+				&& $this->hasFieldPerm($fieldID, 'O')
+				&& ($this->canReadRecord($record) || $this->canWriteRecord($record))
+			) {
+				return true;// 'WO' or 'CO'
+			}
+			return false;
+		}
+
+		// check 'R' - require can read record, or V - Super View
+		if ($taskPerm == 'R') {
+			if ($this->canReadRecord($record) || $this->hasFieldPerm($fieldID, 'V')) {
+				return true;
+			} else {
+				return false;
+			}
+		}
+
+		// // 'C' and 'W' require colType
+		// $colType = $this->getFieldTypeById($fieldID);
+		// if (!$colType) {
+		// 	return false;
+		// }
+		if ($taskPerm == 'W') {
+			if ($record) {
+				if(V::get('DBG_ACL', '', $_REQUEST) > 1){echo '(Field: '.$fieldID.', canWriteRecord: ' . $this->canWriteRecord($record) . ' || (hasFieldPerm(S): ' . $this->hasFieldPerm($fieldID, 'S') . ' && hasFieldPerm(W): ' . $this->hasFieldPerm($fieldID, 'W') . '))';}
+				return ($this->canWriteRecord($record) || $this->hasFieldPerm($fieldID, 'S'));
+			}
+		}
+		return true;
+	}
+
 	public function getFields() { // TODO: conflict return structure with TableAcl
 		if (empty($this->_fields)) {
 			// TODO: fetch fields from DB
@@ -421,4 +623,78 @@ class AntAclBase extends Core_AclBase {
 	public function init($force = false) { }
 	public function isInitialized($force = false) { return true; }
 
+	public function updateItem($itemPatch) {
+		if (is_object($itemPatch)) {
+			$itemPatch = (array)$itemPatch;
+		} else if (!is_array($itemPatch)) {
+			throw new HttpException('Item patch is not array', 400);
+		}
+		if (empty($itemPatch)) {
+			DBG::log("Item patch is empty - after validation");
+			// throw new Exception('Item patch is empty');
+			return 0;// nothing to change
+		}
+
+		$primaryKeyField = $this->getPrimaryKeyField();
+		if (empty($itemPatch[$primaryKeyField])) throw new HttpException("Item Primary Key not set!", 400);
+
+		$primaryKey = $itemPatch[$primaryKeyField];
+		$itemOld = (array)$this->getItem($primaryKey);
+		if (!$itemOld) throw new HttpException("Item not exists!", 404);
+
+		if (!$this->canWriteRecord($itemOld) && !$this->hasPermSuperWrite()) throw new HttpException("Brak dostępu do rekordu", 403);
+
+		// $itemPatch from user input to $validPatch
+		$validPatch = array();
+		DBG::log($itemPatch, 'array', "Item patch - before validation");
+		foreach ($this->getFieldListByIdZasob() as $kID => $fieldName) {
+			if (!array_key_exists($fieldName, $itemPatch)) continue;
+			if (!$this->isAllowed($kID, 'W', $itemOld)) continue;
+			// default value for perms 'W' without 'R' is '*****'
+			if (!$this->isAllowed($kID, 'R', $itemOld) && '*****' == $itemPatch[$fieldName]) continue;
+
+			$value = $itemPatch[$fieldName];
+			if (empty($itemPatch[$fieldName]) && strlen($itemPatch[$fieldName]) == 0) {// fix bug in input type date and value="0000-00-00"
+				$value = $this->fixFieldEmptyValue($fieldName);
+			}
+			if ($value != $itemOld[$fieldName]) {
+				$validPatch[$fieldName] = $value;
+			}
+		}
+		DBG::log($validPatch, 'array', "Item patch - after validation");
+		if (empty($validPatch)) {
+			DBG::log("Item patch is empty - after validation");
+			// throw new Exception('Item patch is empty');
+			return 0;// nothing to change
+		}
+
+		DBG::log($validPatch, 'array', "TODO: EDIT valid item patch");
+		$affected = DB::getPDO()->update($this->getRootTableName(), $primaryKeyField, $primaryKey, array_merge(
+			$validPatch,
+			[
+				'A_RECORD_UPDATE_DATE' => 'NOW()',
+				'A_RECORD_UPDATE_AUTHOR' => User::getLogin(),
+			]
+		));
+		if ($affected) {
+			$affected += $this->saveUpdateHist(array_merge(
+				$validPatch,
+				[
+					'ID_USERS2' => $primaryKey,
+					'A_RECORD_UPDATE_DATE' => 'NOW()',
+					'A_RECORD_UPDATE_AUTHOR' => User::getLogin(),
+				]
+			));
+		}
+		return $affected;
+	}
+
+	public function saveUpdateHist($histPatch) {
+		try {
+			$idHist = DB::getPDO()->insert($this->getRootTableName() . '_HIST', $histPatch);
+		} catch (Exception $e) {
+			DBG::log($e);
+		}
+	}
+
 }

+ 21 - 7
SE/se-lib/TableAcl.php

@@ -1583,10 +1583,13 @@ class TableAcl extends Core_AclBase {
 			->fetchValue();
 	}
 
-	public function getColDefault($fieldName) {
+	public function getFieldDefaultValue($fieldName) { // TODO: get dafault value from xsd file - TODO: p5:default attribute
 		$ds = $this->getDataSource();
 		return $ds->getColDefault($fieldName);
 	}
+	public function getColDefault($fieldName) {
+		return $this->getFieldDefaultValue($fieldName);
+	}
 
 	public function getSpecialFilters() {
 		$ds = $this->getDataSource();
@@ -1898,12 +1901,11 @@ class TableAcl extends Core_AclBase {
 	}
 
 	public function getEnumerations($fldName) {
-		$enum = array();
 		$type = $this->getFieldType($fldName);
-		if (!$type) return $enum;
-		if (!$this->isEnumerationField($fldName)) return $enum;
-
+		if (!$type) return [];
+		if (!$this->isEnumerationField($fldName)) return [];
 		$values = explode(',', str_replace(array('(',')',"'",'"'), '', substr($type['type'], 5)));
+		$enum = array();
 		foreach ($values as $val) {
 			$enum[$val] = $val;
 		}
@@ -1971,6 +1973,7 @@ class TableAcl extends Core_AclBase {
 		else if ($this->isDecimalField($fieldName)) return 'xsd:decimal';
 		else if ($this->isDateTimeField($fieldName)) return 'xsd:dateTime';
 		else if ($this->isDateField($fieldName)) return 'xsd:date';
+		else if ($this->isTextField($fieldName)) return 'p5:text';
 		else if ($this->isGeomField($fieldName)) {
 			//$fldType = 'gml:GeometryPropertyType';
 			$geomType = $this->getGeomFieldType($fieldName);
@@ -1980,7 +1983,7 @@ class TableAcl extends Core_AclBase {
 			else if ('linestring' == $geomType) return 'gml:LineStringPropertyType';
 			else return 'gml:GeometryPropertyType';
 		}
-		else if ($this->isEnumerationField($fieldName)) return 'xsd:string';
+		else if ($this->isEnumerationField($fieldName)) return 'xsd:string'; // TODO: p5:enum ?
 		else if ($this->isBinaryField($fieldName)) return 'xsd:base64Binary';
 		return $fldType;
 	}
@@ -1988,10 +1991,21 @@ class TableAcl extends Core_AclBase {
 	public function getXsdFieldParam($fieldName, $paramKey) {
 		return ($this->loadSchema())
 			? $this->_schemaClass->getFieldParam($fieldName, $paramKey)
-			: null
+			: $this->_getXsdFieldParam($fieldName, $paramKey)
 		;
 	}
 
+	public function _getXsdFieldParam($fieldName, $paramKey) {
+		switch ($paramKey) {
+			case 'maxLength': {
+				$colType = $this->getFieldType($fieldName);
+				if (!$colType) return null;
+				return (int)str_replace(array(' ','(',')'), '', substr($colType['type'], strpos($colType['type'], '(') + 1, -1));
+			}
+		}
+		return null;
+	}
+
 	public function getXsdTypes() {
 		$xsdTypes = [];
 		foreach ($this->getFields() as $idZasob => $field) {

+ 1 - 1
SE/se-lib/TableAjax.php

@@ -4211,7 +4211,7 @@ function hidePopover() {
 		$fieldParams = array('widthClass'=>'inside-modal', 'maxGrid'=>6);
 		if (!empty($tsValues[ $record['ID'] ])) $fieldParams['typespecialValue'] = $tsValues[ $record['ID'] ];
 		if (!empty($tsValues[ $record['ID'] ])) $response['typespecialValue'] = $tsValues[ $record['ID'] ];
-		$vDefault = $this->_acl->getColDefault($fieldName);
+		$vDefault = $this->_acl->getFieldDefaultValue($fieldName);
 		if (!empty($vDefault)) $fieldParams['default'] = $vDefault;
 		if (!empty($vDefault)) $response['defaultValue'] = $vDefault;
 		{

+ 151 - 163
SE/se-lib/UI.php

@@ -456,6 +456,7 @@ class UI {
 	 */
 	public static function hGetFormItem($acl, $fieldName, $taskPerm, $fieldID, $fName, $fValue, $params = array(), $record = null) {
 		Lib::loadClass('Typespecial');
+		DBG::log(['$fieldName'=>$fieldName, '$taskPerm'=>$taskPerm, '$fieldID'=>$fieldID, '$fName'=>$fName, '$fValue'=>$fValue, '$params'=>$params, '$record'=>$record], 'array', "hGetFormItem()");
 
 		if (!$acl->isAllowed($fieldID, $taskPerm, $record)) {
 			switch ($taskPerm) {
@@ -467,8 +468,9 @@ class UI {
 
 		if ($fieldName == 'ID') return ''; // TODO: hide primaryKey?
 
-		$colType = $acl->getFieldTypeById($fieldID);
-		if (!$colType) return "Error - unknown type";
+		// $colType = $acl->getFieldTypeById($fieldID);
+		// if (!$colType) return "Error - unknown type";
+		$xsdType = $acl->getXsdFieldType($fieldName);
 
 		$html = new stdClass();
 		$html->_params = array();
@@ -479,180 +481,166 @@ class UI {
 		$html->attrs['name'] = $fName;
 		$html->attrs['type'] = 'text';
 		$html->attrs['value'] = $fValue;// BUG htmlspecialchars($fValue); - convert chars in edit form (" to " and & to &)
-		if (isset($params['tabindex'])) {
-			$html->attrs['tabindex'] = $params['tabindex'];
-		}
-		if (!$acl->hasFieldPerm($fieldID, $taskPerm)) {
-			$html->attrs['disabled'] = 'disabled';
-		}
+		if (isset($params['tabindex'])) $html->attrs['tabindex'] = $params['tabindex'];
+		// if (!$acl->hasFieldPerm($fieldID, $taskPerm)) {
+		// 	$html->attrs['disabled'] = 'disabled';
+		// }
 		$maxGrid = V::get('maxGrid', 10, $params);
 
-		if (substr($colType['type'], 0, 3) == 'int'
-				|| substr($colType['type'], 0, 7) == 'tinyint'
-				|| substr($colType['type'], 0, 8) == 'smallint'
-				|| substr($colType['type'], 0, 6) == 'bigint'
-		) {
-			//$h->Type_value = (int)str_replace(array(' ','(',')'), '', substr($h->Type, 4));
-			$html->attrs['type'] = 'number';
-			$html->attrs['class'][] = 'input-small';
-		}
-		else if (substr($colType['type'], 0, 6) == 'double') {
-			$html->attrs['type'] = 'text';
-			$html->attrs['class'][] = 'input-small';
-		}
-		else if (substr($colType['type'], 0, 7) == 'decimal') {
-			$html->attrs['type'] = 'text';
-			$html->attrs['class'][] = 'input-small';
-		}
-		else if (substr($colType['type'], 0, 7) == 'varchar'
-				|| substr($colType['type'], 0, 4) == 'char'
-			) {
-			//$h->Type_value = (int)str_replace(array(' ','(',')'), '', substr($h->Type, 8));
-			$html->attrs['type'] = 'text';
-			$maxLength = (int)str_replace(array(' ','(',')'), '', substr($colType['type'], strpos($colType['type'], '(') + 1, -1));
-			if ($maxLength > 0) {
-				$html->attrs['maxlength'] = $maxLength;
-			}
-			$valLength = strlen($fValue);
-			if (isset($params['widthClass'])) {
-				if ($params['widthClass'] == 'inside-modal') {
-					$html->attrs['style'] = 'width:98%;';
-				} else {
-					$html->attrs['style'] = 'width:98%;';
+		switch ($xsdType) {
+
+			case 'xsd:integer': {
+				$html->attrs['type'] = 'number';
+				$html->attrs['class'][] = 'input-small';
+			} break;
+
+			case 'xsd:decimal':
+			case 'p5:price': {
+				$html->attrs['type'] = 'text';
+				$html->attrs['class'][] = 'input-small';
+			} break;
+
+			case 'xsd:string':
+			case 'p5:www_link':
+			case 'p5:string': {
+				$html->attrs['type'] = 'text';
+				$maxLength = (int)$acl->getXsdFieldParam($fieldName, 'maxLength');
+				if ($maxLength > 0) {
+					$html->attrs['maxlength'] = $maxLength;
 				}
-			} else {
-				/*
-				if ($maxLength < 11) {
-					$html->attrs['class'][] = 'span2';
-				} else if ($maxLength < 31) {
-					$html->attrs['class'][] = 'span5';
-				} else if ($maxLength < 51) {
-					$html->attrs['class'][] = (8 <= $maxGrid)? 'span8' : "span{$maxGrid}";
-				} else if ($maxLength < 101) {
-					$html->attrs['class'][] = (10 <= $maxGrid)? 'span10' : "span{$maxGrid}";
-				} else {
-					$html->attrs['class'][] = (12 <= $maxGrid)? 'span12' : "span{$maxGrid}";
+				$valLength = strlen($fValue);
+				if (isset($params['widthClass'])) {
+					if ($params['widthClass'] == 'inside-modal') {
+						$html->attrs['style'] = 'width:98%;';
+					} else {
+						$html->attrs['style'] = 'width:98%;';
+					}
 				}
-				*/
-			}
 
-			if ($maxLength > 255) {// Fix for long varchar - use textarea
-				$html->tag = 'textarea';
-				$html->childrens[] = $fValue;
-				$html->attrs['rows'] = '3';
-				unset($html->attrs['type']);
-				unset($html->attrs['value']);
-			}
-		}
-		else if (substr($colType['type'], 0, 4) == 'date') {
-			$testDatePicker = true;
-			if ($testDatePicker) {
-				$html->attrs['type'] = 'text';
-				$html->_params[] = 'date';
-				if (substr($colType['type'], 0, 8) == 'datetime') {
-					$html->attrs['class'][] = 'se_type-datetime';// datetimepicker';
-					$html->attrs['data-format'] = 'yyyy-MM-dd hh:mm';
-					$html->attrs['maxlength'] = 19;
-				} else {
-					$html->attrs['class'][] = 'se_type-date';// datetimepicker';
-					$html->attrs['maxlength'] = 10;
+				if ($maxLength > 255) {// Fix for long varchar - use textarea
+					$html->tag = 'textarea';
+					$html->childrens[] = $fValue;
+					$html->attrs['rows'] = '3';
+					unset($html->attrs['type']);
+					unset($html->attrs['value']);
 				}
-				if (substr($html->attrs['value'], 0, 10) == '0000-00-00') {
-					$html->attrs['value'] = '';
+			} break;
+
+			case 'xsd:dateTime':
+			case 'xsd:date': {
+				$testDatePicker = true;
+				if ($testDatePicker) {
+					$html->attrs['type'] = 'text';
+					$html->_params[] = 'date';
+					if ('xsd:dateTime' === $xsdType) {
+						$html->attrs['class'][] = 'se_type-datetime'; // datetimepicker';
+						$html->attrs['data-format'] = 'yyyy-MM-dd hh:mm';
+						$html->attrs['maxlength'] = 19;
+					} else {
+						$html->attrs['class'][] = 'se_type-date'; // datetimepicker';
+						$html->attrs['maxlength'] = 10;
+					}
+					if (substr($html->attrs['value'], 0, 10) == '0000-00-00') {
+						$html->attrs['value'] = '';
+					}
+				} else {
+					$html->attrs['type'] = 'date';
 				}
-			} else {
-				$html->attrs['type'] = 'date';
-			}
-		}
-		else if ($colType['type'] == 'time') {
-			$testDatePicker = true;
-			if ($testDatePicker) {
-				$html->attrs['type'] = 'text';
-				$html->_params[] = 'time';
-				$html->attrs['class'][] = 'se_type-time';// datetimepicker';
-				$html->attrs['data-format'] = 'hh:mm:ss';
-				$html->attrs['maxlength'] = 8;
-				if (substr($html->attrs['value'], 0, 8) == '00:00:00') {
-					$html->attrs['value'] = '';
+			} break;
+
+			// case 'xsd:time': { // TODO: ... TableAcl not returns xsd type 'xsd:time'
+			// 	$testDatePicker = true;
+			// 	if ($testDatePicker) {
+			// 		$html->attrs['type'] = 'text';
+			// 		$html->_params[] = 'time';
+			// 		$html->attrs['class'][] = 'se_type-time';// datetimepicker';
+			// 		$html->attrs['data-format'] = 'hh:mm:ss';
+			// 		$html->attrs['maxlength'] = 8;
+			// 		if (substr($html->attrs['value'], 0, 8) == '00:00:00') {
+			// 			$html->attrs['value'] = '';
+			// 		}
+			// 	} else {
+			// 		$html->attrs['type'] = 'time';
+			// 	}
+			// } break;
+
+			// case 'timestamp': { // TODO: timestamp is xsd:integer or xsd:dateTime?
+			// 	$testDatePicker = true;
+			// 	if ($testDatePicker) {
+			// 		$html->attrs['type'] = 'text';
+			// 		$html->_params[] = 'date';
+			// 		$html->attrs['class'][] = 'se_type-datetime';// datetimepicker';
+			// 		$html->attrs['data-format'] = 'yyyy-MM-dd hh:mm';
+			// 		$html->attrs['maxlength'] = 19;
+			// 		if (substr($html->attrs['value'], 0, 10) == '0000-00-00') {
+			// 			$html->attrs['value'] = '';
+			// 		}
+			// 	} else {
+			// 		$html->attrs['type'] = 'date';
+			// 	}
+			// } break;
+
+			case 'p5:enum': {
+				DBG::log($acl->getField($fieldID), 'array', "\$field($fieldName)");
+				unset($html->attrs['type']);
+				unset($html->attrs['value']);
+				$html->tag = 'select';
+				$defaultValue = $acl->getFieldDefaultValue($fieldName);
+				$values = $acl->getEnumerations($fieldName);
+
+				// 	$values = explode(',', str_replace(array('(',')',"'",'"'), '', substr($colType['type'], 5)));
+				$selValue = $fValue;
+				if (empty($selValue) && $selValue !== '0' && !empty($defaultValue)) {
+					if ($taskPerm == 'C') {
+						$selValue = $defaultValue;
+					} else if ($taskPerm == 'W' && $acl->isAllowed($fieldID, 'R', $record)) {
+						$selValue = $defaultValue;
+					}
 				}
-			} else {
-				$html->attrs['type'] = 'time';
-			}
-		}
-		else if ($colType['type'] == 'timestamp') {
-			$testDatePicker = true;
-			if ($testDatePicker) {
-				$html->attrs['type'] = 'text';
-				$html->_params[] = 'date';
-				$html->attrs['class'][] = 'se_type-datetime';// datetimepicker';
-				$html->attrs['data-format'] = 'yyyy-MM-dd hh:mm';
-				$html->attrs['maxlength'] = 19;
-				if (substr($html->attrs['value'], 0, 10) == '0000-00-00') {
-					$html->attrs['value'] = '';
+
+				$html->childrens[] = [ 'option', [ 'value' => "" ], "" ];
+				if (!empty($selValue) && !array_key_exists($selValue, $values)) {
+					$html->childrens[] = [ 'option', [ 'value' => $selValue, 'selected' => "selected" ], $selValue ];
 				}
-			} else {
-				$html->attrs['type'] = 'date';
-			}
-		}
-		else if (substr($colType['type'], 0, 4) == 'enum') {
-			unset($html->attrs['type']);
-			unset($html->attrs['value']);
-			$html->tag = 'select';
-
-			$values = explode(',', str_replace(array('(',')',"'",'"'), '', substr($colType['type'], 5)));
-			$selValue = $fValue;
-			if (empty($selValue) && $selValue !== '0' && !empty($colType['default'])) {
-				if ($taskPerm == 'C') {
-					$selValue = $colType['default'];
-				} else if ($taskPerm == 'W' && $acl->isAllowed($fieldID, 'R', $record)) {
-					$selValue = $colType['default'];
+				foreach ($values as $val => $label) {
+					$html->childrens[] = [ 'option', array_merge(
+						[ 'value' => $val ],
+						(!empty($selValue) && $selValue == $val) ? [ 'selected' => "selected" ] : []
+					), $label ];
 				}
-			}
+			} break;
 
-			$html->childrens[] = [ 'option', [ 'value' => "" ], "" ];
-			if (!empty($selValue) && !in_array($selValue, $values)) {
-				$html->childrens[] = [ 'option', [ 'value' => $selValue, 'selected' => "selected" ], $selValue ];
-			}
-			foreach ($values as $val) {
-				$html->childrens[] = [ 'option', array_merge(
-					[ 'value' => $val ],
-					($selValue == $val)
-						? [ 'selected' => "selected" ]
-						: []
-				), $val ];
-			}
-		}
-		else if (substr($colType['type'], 0, 4) == 'text'
-			|| substr($colType['type'], 0, 8) == 'tinytext'
-			|| substr($colType['type'], 0, 10) == 'mediumtext'
-			|| substr($colType['type'], 0, 8) == 'longtext'
-		) {
-			$html->tag = 'textarea';
-			$html->childrens[] = $fValue;
-			if (isset($params['widthClass'])) {
-				if ($params['widthClass'] == 'inside-modal') {
-					$html->attrs['style'] = 'width:98%;';
+			case 'p5:text': {
+				$html->tag = 'textarea';
+				$html->childrens[] = $fValue;
+				if (isset($params['widthClass'])) {
+					if ($params['widthClass'] == 'inside-modal') {
+						$html->attrs['style'] = 'width:98%;';
+					} else {
+						$html->attrs['style'] = 'width:98%;';
+					}
 				} else {
-					$html->attrs['style'] = 'width:98%;';
+					//$html->attrs['class'][] = (8 <= $maxGrid)? 'span8' : "span{$maxGrid}";
 				}
-			} else {
-				//$html->attrs['class'][] = (8 <= $maxGrid)? 'span8' : "span{$maxGrid}";
-			}
-			$html->attrs['rows'] = '3';
-			unset($html->attrs['type']);
-			unset($html->attrs['value']);
-		}
-		else if ('polygon' == $colType['type']) { return '...'; }// Wielokąt
-		else if ('multipolygon' == $colType['type']) { return '...'; }// Zbiór wielokątów
-		else if ('linestring' == $colType['type']) { return '...'; }// Krzywa z interpolacji liniowej pomiędzy punktami
-		else if ('point' == $colType['type']) { return '...'; }// Punkt w przestrzeni 2-wymiarowej
-		else if ('geometry' == $colType['type']) { return '...'; }// Typy, które mogą przechowywać geometrię dowolnego typu
-		else if ('multipoint' == $colType['type']) { return '...'; }// Zbiór punktów
-		else if ('multilinestring' == $colType['type']) { return '...'; }// Zbiór krzywych z interpolacji liniowej pomiędzy punktami
-		else if ('geometrycollection' == $colType['type']) { return '...'; }// Zbiór obiektów geometrycznych dowolnego typu
-		else {
-			return 'unknown Type "'.$colType['type'].'"';
+				$html->attrs['rows'] = '3';
+				unset($html->attrs['type']);
+				unset($html->attrs['value']);
+			} break;
+
+			case 'gml:GeometryPropertyType':
+			case 'gml:LineStringPropertyType':
+			case 'gml:PointPropertyType':
+			case 'gml:PolygonPropertyType': {
+				return '...';
+			} break;
+
+			case 'xsd:base64Binary': {
+				return '...';
+			} break;
+
+			default: return "unknown Type \"{$xsdType}\"";
 		}
+
 		$html->attrs['class'][] = 'form-control';
 		if (!empty($html->attrs['class'])) $html->attrs['class'] = implode(" ", $html->attrs['class']);
 		$nodeHtml = (in_array($html->tag, array('select', 'textarea')))
@@ -680,7 +668,7 @@ class UI {
 		if (true == V::get('appendBack', '', $params)
 				&& !in_array('date', $html->_params)
 				&& !in_array('time', $html->_params)
-			 ) {
+			) {
 			if ($html->tag == 'input' && $taskPerm == 'W') {
 				$nodeHtml = [ 'div', [ 'class' => "input-group show-last-value" ], [
 					$nodeHtml,