Piotr Labudda преди 9 години
родител
ревизия
cdb8174c67

+ 152 - 0
SE/se-lib/P5.php

@@ -0,0 +1,152 @@
+<?php
+
+Lib::loadClass('Core_AclHelper');
+Lib::loadClass('TableAcl');
+Lib::loadClass('SchemaFactory');
+/**
+ * Zasoby:
+ *   DATABASE
+ *     TABELA
+ *       KOMORKA
+ */
+class P5 {
+
+  public static function insert($namespace, $item) {
+    $ns = Core_AclHelper::parseNamespace($namespace);
+    $idDatabase = Core_AclHelper::getIdDatabaseFromNamespace($namespace);
+    if ('default_db' == $ns['sourceName']) {
+      // TODO: check if has A_RECORD_CREATE_AUTHOR field
+      if (empty($item['A_RECORD_CREATE_AUTHOR'])) $item['A_RECORD_CREATE_AUTHOR'] = User::getLogin();
+      // TODO: check if has A_RECORD_CREATE_DATE field
+      if (empty($item['A_RECORD_CREATE_DATE'])) $item['A_RECORD_CREATE_DATE'] = 'NOW()';
+      $id = DB::getPDO($idDatabase)->insert($ns['name'], $item);
+      // TODO: check if has _HIST table
+      $item['ID_USERS2'] = $id;
+      DB::getPDO($idDatabase)->insert("{$ns['name']}_HIST", $item);
+      return $id;
+    } else if ('table_objects' == $ns['sourceName']) {
+      return Core_AclHelper::getAclByNamespace($namespace)->addItem($item);
+    } else if ('default_objects' == $ns['sourceName']) {
+      return Core_AclHelper::getAclByNamespace($namespace)->addItem($item);
+    }
+	}
+
+  public static function getAclByNamespace($namespace) {
+    DBG::log("getAclByNamespace({$namespace})");
+    $objectItem = SchemaFactory::loadDefaultObject('SystemObject')->getItem($namespace, ['propertyName' => '*,field']);
+    DBG::log($objectItem, 'array', "getAclByNamespace({$namespace})");
+    if ($objectItem['isStructInstalled']) {
+      return self::buildAclFromSystemObject($objectItem, User::getID());
+    }
+    throw new Exception("Object not installed '{$namespace}'");
+  }
+
+  public static function buildAclFromSystemObject($objItem, $idUser) {
+    if ('AntAcl' == $objItem['_type']) {
+      throw new Exception("TODO: buildAclFromSystemObject - type = '{$objItem['_type']}'");
+    }
+    if ('TableAcl' == $objItem['_type']) {
+      // $this->_db = $arr['db'];
+  		// $this->_name = $arr['name'];
+  		// $this->_rootTableName = V::get('_rootTableName', null, $arr);
+  		// $this->_label = $arr['label'];
+  		// $this->_opis = $arr['opis'];
+  		// $this->_fields = V::get('fields', array(), $arr);
+  		// $this->_virtualFieldsIdList = V::get('virtualFieldsIdList', array(), $arr);
+  		// $this->_types = V::get('types', array(), $arr);
+      $idTable = $objItem['idZasob'];
+      $tableConfig = [
+        'db' => $objItem['idDatabase'],
+        'name' => $objItem['_rootTableName'],
+        '_rootTableName' => $objItem['_rootTableName'],
+        'label' => '...', // TODO: fetch from Zasoby
+        'opis' => '...', // TODO: fetch from Zasoby
+        'fields' => [], // TODO: ...
+        'virtualFieldsIdList' => [], // TODO: fetch additional fields from Zasoby - or add this fields to Storage::reinstallObject action
+        'types' => [], // TODO: ...
+      ];
+      foreach ($objItem['field'] as $field) {
+        $fieldName = $field['fieldNamespace'];
+        $tableConfig['fields'][$fieldName] = [
+          'name' => $fieldName,
+          'xsdType' => $field['xsdType'],
+        ];
+      }
+      return TableAcl::buildInstance($idTable, $tableConfig);
+    }
+    throw new Exception("TODO: buildAclFromSystemObject - type = '{$objItem['_type']}'");
+  }
+
+  public static function getAclById($id) {
+    $sqlIdZasob = DB::getPDO()->quote($id, PDO::PARAM_INT);
+    $zasobInfo = DB::getPDO()->fetchFirst("
+      select t.ID as id
+          , t.`DESC` as name
+          , t.DESC_PL as label
+          , t.OPIS as description
+          , d.ID as idDatabase
+      from CRM_LISTA_ZASOBOW t
+        join CRM_LISTA_ZASOBOW d on(d.ID = t.PARENT_ID)
+      where t.`TYPE` = 'TABELA'
+        and t.A_STATUS in('NORMAL','WAITING')
+        and d.A_STATUS in('NORMAL','WAITING')
+        and t.ID = {$sqlIdZasob}
+    ");
+    if (!$zasobInfo) throw new Exception("Obiekt Nr '{$i}' nie istnieje w bazie zasobów");
+    DBG::nicePrint($zasobInfo, '$zasobInfo');
+    if (false !== strpos($zasobInfo['name'], '/')) {
+      $namespace = "{$zasobInfo['name']}";
+    } else {
+      $sourceName = (DB::getPDO()->getZasobId() == $zasobInfo['idDatabase'])
+        ? 'default_db'
+        : "zasob_{$zasobInfo['idDatabase']}";
+      $namespace = "{$sourceName}/{$zasobInfo['name']}";
+    }
+    $ns = Core_AclHelper::parseNamespaceUrl($namespace);
+    DBG::nicePrint($ns, "P5::getAcl by idZasob({$id}) Namespace({$namespace})");
+  }
+  public static function getAcl($arg) {
+    if (is_numeric($arg)) {// $idZasob
+      return self::getAclById($arg);
+    } else if (is_string($arg)) {// $namespace
+      $namespace = $arg;
+      $ns = Core_AclHelper::parseNamespaceUrl($namespace);
+      DBG::nicePrint($ns, "P5::getAcl by Namespace({$namespace}) TODO: find idZasob");
+      if ('default_db' == substr($ns['sourceName'], 0, strlen('default_db'))) {
+        $idDatabase = DB::getPDO()->getZasobId();
+      } else if ('zasob_' == substr($ns['sourceName'], 0, strlen('zasob_'))) {
+        // 'zasob_931', 'zasob_931__x3A__...'
+        $idDatabase = substr($ns['sourceName'], strlen('zasob_'));
+        if (false !== strpos($idDatabase, '_')) $idDatabase = substr($idDatabase, 0, strpos($idDatabase, '_'));
+        DBG::nicePrint($idDatabase, "P5::getAcl by Namespace({$namespace}) TODO: find idZasob - \$idDatabase");
+        if (!$idDatabase || !is_numeric($idDatabase)) throw new Exception("Not implemented idDatabase({$idDatabase})");
+      } else {
+        throw new Exception("Error Processing Request", 1);
+      }
+      $sqlObjectName = DB::getPDO()->quote($ns['name'], PDO::PARAM_STR);
+      $zasobInfo = DB::getPDO()->fetchFirst("
+        select t.ID as id
+            , t.`DESC` as name
+            , t.DESC_PL as label
+            , t.OPIS as description
+            , d.ID as idDatabase
+        from CRM_LISTA_ZASOBOW t
+          join CRM_LISTA_ZASOBOW d on(d.ID = t.PARENT_ID)
+        where t.`TYPE` = 'TABELA'
+          and t.A_STATUS in('NORMAL','WAITING')
+          and d.A_STATUS in('NORMAL','WAITING')
+          and d.ID = {$idDatabase}
+          and t.`DESC` = {$sqlObjectName}
+      ");
+      DBG::nicePrint($zasobInfo, "P5::getAcl by Namespace({$namespace}) DB({$idDatabase}) zasobInfo");
+      if (!$zasobInfo) {// try to install object
+
+      }
+      // $acl = User::getAcl()->getObjectAcl($ns['prefix'], $ns['name']);
+      // $acl->init($forceTblAclInit);
+      // return $acl;
+    }
+    throw new Exception("TODO: P5::getAcl L." . __LINE__);
+  }
+
+}

+ 2 - 0
SE/se-lib/Route/Storage.php

@@ -585,6 +585,8 @@ jQuery(document).on('p5UIBtnAjax:Storage:checkObjectInstallAjax:ajaxLoaded', fun
 						'struktura OBJ' => UI::h('a', [ 'href' => $this->getLink('objectStruct', [ 'idStorage' => $idStorage, 'namespace' => $item['namespace'] ]) ], "obj struct"),
 						'raw info' => UI::h('a', [ 'href' => $this->getLink('rawInfo', [ 'idStorage' => $idStorage, 'table' => $item['name'] ]) ], "raw info"),
 						'reinstall' => UI::h('a', [ 'href' => $item['reinstallLink'] ], "reinstall"),
+						'view table' => UI::h('a', [ 'href' => Router::getRoute('ViewTableAjax')->getLink('', ['namespace' => $item['namespace']]) ], "view table"),
+						'view object' => UI::h('a', [ 'href' => Router::getRoute('ViewObject')->getLink('', ['namespace' => $item['namespace']]) ], "view object"),
 						// 'xsd' => UI::h('a', [ 'href' => $this->getLink('xsd', [ 'idStorage' => $idStorage ]) ], "xsd"),
 					];
 				}, $objectStorage->getItems([

+ 876 - 0
SE/se-lib/Route/ViewObject.php

@@ -0,0 +1,876 @@
+<?php
+
+Lib::loadClass('RouteBase');
+Lib::loadClass('ProcesHelper');
+Lib::loadClass('TableAjax');
+// Lib::loadClass('Request');
+Lib::loadClass('Response');
+Lib::loadClass('UI');
+Lib::loadClass('Api_WfsNs');
+Lib::loadClass('Core_AclHelper');
+Lib::loadClass('Route_UrlAction');
+Lib::loadClass('Router');
+Lib::loadClass('Typespecial');
+Lib::loadClass('UserProfile');
+Lib::loadClass('P5');
+Lib::loadClass('Route_ViewTableAjax');
+
+class Route_ViewObject extends Route_ViewTableAjax {
+
+	public function getTableAjaxWidget($acl) {
+		$syncUrl = Request::getPathUri() . 'index.php?_route=ViewObject&namespace=' . $acl->getNamespace();
+		$tbl = new TableAjax($acl);
+		$tblLabel = $acl->getNamespace();
+		if ('default_db' == $acl->getSourceName()) {
+			$tblLabel = array();
+			$zasobObj = ProcesHelper::getZasobTableInfo($acl->getID());
+			if (!$zasobObj) throw new Exception("Zasob TABELA ID=" . $acl->getID() . " nie istnieje");
+			if (!empty($zasobObj->DESC_PL)) $tblLabel []= $zasobObj->DESC_PL;
+			if (!empty($zasobObj->OPIS))    $tblLabel []= $zasobObj->OPIS;
+			$tblLabel = implode(" - ", $tblLabel);
+		}
+		$tbl->setSyncUrl($syncUrl);
+		$tbl->setLabel($tblLabel);
+		$tbl->addRowFunction('edit');
+		$tbl->addRowFunction('hist');
+		$tbl->addRowFunction('files');
+		$tbl->addRowFunction('cp');
+		$tbl->addRowFunction('msgs');
+		return $tbl;
+	}
+
+	public function defaultAction() {
+		UI::gora();
+		UI::menu();
+		try {
+			$namespace = V::get('namespace', '', $_GET, 'word');
+			if (!$namespace) {
+				$typeName = V::get('typeName', '', $_GET, 'word');
+				if (!$typeName) throw new Exception("Wrong param typeName");
+				$namespace = Api_WfsNs::getBaseWfsUri() . '/' . str_replace(':', '/', $typeName);
+			}
+			$acl = P5::getAclByNamespace($namespace, $forceTblAclInit = ('1' == V::get('_force', '', $_GET)));
+
+			$forceFilterInit = array();
+			$filterInit = new stdClass();
+			$filterInit->currSortCol = $acl->getPrimaryKeyField();
+			$filterInit->currSortFlip = 'desc';
+			foreach ($_GET as $k => $v) {
+				if (strlen($k) > 3 && substr($k, 0, 2) == 'f_' && !empty($v)) {// filter prefix
+					$filterInit->$k = $v;
+				}
+				else if (strlen($k) > 4 && substr($k, 0, 3) == 'sf_' && !empty($v)) {// special filter prefix
+					$filterInit->$k = $v;
+				}
+				else if (strlen($k) > 4 && substr($k, 0, 3) == 'ff_' && !empty($v)) {// force filter prefix
+					$fldName = substr($k, 3);
+					$forceFilterInit[$fldName] = $v;
+				}
+			}
+
+			$tbl = $this->getTableAjaxWidget($acl);
+			$tbl->setFilterInit($filterInit);
+			if (!empty($forceFilterInit)) $tbl->setForceFilterInit($forceFilterInit);
+			echo $tbl->render();
+
+			if (DBG::isActive() && V::get('DBG_ACL', '', $_GET)) {// test load perms
+				Lib::loadClass('DebugExecutionTime');
+				$dbgExecTime = new DebugExecutionTime();
+				$dbgExecTime->activate();
+				$dbgExecTime->log('start');
+				UI::startContainer(['style'=>'border:1px solid red']);
+				UI::tag('p', null, "TEST - load perms from db");
+				$idTable = $acl->getID();
+				UI::tag('p', null, "DBG idTable({$idTable})");
+				if ($idTable > 0) {
+					$dbgExecTime->log('before sql');
+					$aclTableRows = DB::getPDO()->fetchAll("select * from `CRM_PROCES_idx_TABLE_TO_PROCES_PERMS_VIEW` where ID_TABLE = {$idTable}");
+					$dbgExecTime->log('after sql', ['sql']);
+					UI::table(['caption' => "from CRM_PROCES_idx_TABLE_TO_PROCES_PERMS_VIEW", 'rows' => $aclTableRows]);
+					$csvIdProces = array();
+					foreach ($aclTableRows as $row) {
+						if (!in_array($row['ID_PROCES'], $csvIdProces)) $csvIdProces[] = $row['ID_PROCES'];
+					}
+				}
+
+				$tableName = $acl->getName();
+				$databaseName = DB::getPDO()->getDatabaseName();
+				UI::table([
+					'caption' => "Cell to process",
+					'rows' => array_map(
+						function ($row) use ($aclTableRows, $idTable) {
+							$row['proces'] = array();
+							$row['id_zasob'] = 0;
+							$row['PERM_R'] = 0;
+							$row['PERM_W'] = 0;
+							$row['PERM_X'] = 0;
+							$row['PERM_C'] = 0;
+							$row['PERM_S'] = 0;
+							$row['PERM_O'] = 0;
+							$row['PERM_V'] = 0;
+							$row['PERM_E'] = 0;
+							foreach ($aclTableRows as $aclInfo) {
+								if (strtolower($aclInfo['CELL_NAME']) == strtolower($row['COLUMN_NAME'])) {
+									$row['proces'][] = $aclInfo['ID_PROCES'];
+									$row['id_zasob'] = $aclInfo['ID_CELL'];
+									$row['PERM_R'] += $aclInfo['PERM_R'];
+									$row['PERM_W'] += $aclInfo['PERM_W'];
+									$row['PERM_X'] += $aclInfo['PERM_X'];
+									$row['PERM_C'] += $aclInfo['PERM_C'];
+									$row['PERM_S'] += $aclInfo['PERM_S'];
+									$row['PERM_O'] += $aclInfo['PERM_O'];
+									$row['PERM_V'] += $aclInfo['PERM_V'];
+									$row['PERM_E'] += $aclInfo['PERM_E'];
+								}
+							}
+							$row['proces'] = (empty($row['proces']))
+								? "<i style=\"color:red\">Brak</i>"
+								: implode(", ", $row['proces']);
+							if (!$row['id_zasob']) $row['id_zasob'] = DB::getPDO()->fetchValue("select ID from CRM_LISTA_ZASOBOW where `DESC` = '{$row['COLUMN_NAME']}' and PARENT_ID = {$idTable} limit 1");
+							return $row;
+						}, DB::getPDO()->fetchAll("
+							select t.TABLE_NAME, t.COLUMN_NAME, t.DATA_TYPE, t.COLUMN_TYPE
+							from `information_schema`.`COLUMNS` t
+							where t.TABLE_SCHEMA = '{$databaseName}'
+								and t.TABLE_NAME like '{$tableName}'
+						")
+					)
+				]);
+
+				if (!empty($csvIdProces)) {
+					$csvIdProces = implode(",", $csvIdProces);
+					UI::tag('p', null, "DBG csvIdProces({$csvIdProces})");
+					$userLogin = User::getLogin();
+					$dbgExecTime->log('before sql');
+					$rows = DB::getPDO()->fetchAll("select ID_PROCES from `CRM_PROCES_idx_USER_to_PROCES_VIEW` where ADM_ACCOUNT = '{$userLogin}' and ID_PROCES in({$csvIdProces}) group by ID_PROCES");
+					$dbgExecTime->log('after sql', ['sql']);
+					UI::table(['caption' => "from CRM_PROCES_idx_USER_to_PROCES_VIEW", 'rows' => $rows]);
+					$userIdProces = array(); foreach ($rows as $row) $userIdProces[] = $row['ID_PROCES'];
+					$userTablePerms = array();
+					foreach ($aclTableRows as $row) {
+						if (!in_array($row['ID_PROCES'], $userIdProces)) continue;
+						if (array_key_exists($row['CELL_NAME'], $userTablePerms)) {
+							$userTablePerms[ $row['CELL_NAME'] ][ 'PERM_R' ] += $row['PERM_R'];
+							$userTablePerms[ $row['CELL_NAME'] ][ 'PERM_W' ] += $row['PERM_W'];
+							$userTablePerms[ $row['CELL_NAME'] ][ 'PERM_X' ] += $row['PERM_X'];
+							$userTablePerms[ $row['CELL_NAME'] ][ 'PERM_C' ] += $row['PERM_C'];
+							$userTablePerms[ $row['CELL_NAME'] ][ 'PERM_S' ] += $row['PERM_S'];
+							$userTablePerms[ $row['CELL_NAME'] ][ 'PERM_O' ] += $row['PERM_O'];
+							$userTablePerms[ $row['CELL_NAME'] ][ 'PERM_V' ] += $row['PERM_V'];
+							$userTablePerms[ $row['CELL_NAME'] ][ 'PERM_E' ] += $row['PERM_E'];
+						} else {
+							$userTablePerms[ $row['CELL_NAME'] ] = $row;
+							unset($userTablePerms[ $row['CELL_NAME'] ][ 'TABLE_DESCRIPTION' ]);
+							unset($userTablePerms[ $row['CELL_NAME'] ][ 'ID_PROCES' ]);
+							unset($userTablePerms[ $row['CELL_NAME'] ][ 'FORM_TREAT' ]);
+						}
+					}
+					UI::table(['caption' => "\$userTablePerms", 'rows' => $userTablePerms]);
+				} else UI::alert('warning', "brak \$csvIdProces");
+				$dbgExecTime->printDebug();
+				UI::endContainer();
+			}
+		} catch (Exception $e) {
+			UI::startContainer();
+			UI::alert('danger', "<strong>Wystąpiły błędy!</strong> " . $e->getMessage());
+			UI::endContainer();
+			DBG::log($e);
+		}
+		UI::dol();
+	}
+
+	public function rmUserTableFilterAjaxAction() {
+		Response::sendTryCatchJson(array($this, 'rmUserTableFilterAjax'), $args = 'JSON_FROM_REQUEST_BODY');
+	}
+	public function rmUserTableFilterAjax($args) {
+		$namespace = V::get('namespace', '', $args);
+		$filtrName = V::get('filtrName', '', $args);
+		if (!$namespace) throw new Exception("Missing namespace");
+		if (!$filtrName) throw new Exception("Missing filtrName");
+
+		$userFltrConfKey = "tableColFilters__" . User::getLogin();
+		$currentFilters = DB::getPDO()->fetchValue(" select CONF_VAL from CRM_CONFIG where CONF_KEY = '{$userFltrConfKey}' ");
+		if (!$currentFilters) return [
+			'type' => 'warning',
+			'msg' => "Brak filtrów w bazie",
+		];
+		$currentFilters = json_decode($currentFilters, 'assoc');
+		unset($currentFilters[$namespace][$filtrName]);
+		$affeced = DB::getPDO()->update('CRM_CONFIG', 'CONF_KEY', $userFltrConfKey, [
+			'CONF_VAL' => json_encode($currentFilters)
+		]);
+		return [
+			'type' => 'success',
+			'msg' => 'Zapisano nowy filtr',
+			'data' => $currentFilters[$namespace]
+		];
+	}
+	public function addUserTableFilterAjaxAction() {
+		Response::sendTryCatchJson(array($this, 'addUserTableFilterAjax'), $args = 'JSON_FROM_REQUEST_BODY');
+	}
+	public function addUserTableFilterAjax($args) {
+		$namespace = V::get('namespace', '', $args);
+		$filtrName = V::get('filtrName', '', $args);
+		$visibleCols = V::get('visibleCols', '', $args);
+		if (!$namespace) throw new Exception("Missing namespace");
+		if (!$filtrName) throw new Exception("Missing filtrName");
+		if (!$visibleCols) throw new Exception("Missing visibleCols");
+
+		$userFltrConfKey = "tableColFilters__" . User::getLogin();
+		$currentFilters = DB::getPDO()->fetchValue(" select CONF_VAL from CRM_CONFIG where CONF_KEY = '{$userFltrConfKey}' ");
+		$currentFilters = ($currentFilters) ? json_decode($currentFilters, 'assoc') : [];
+		$currentFilters[$namespace][$filtrName] = $visibleCols;
+		$sqlFltr = json_encode($currentFilters);
+		DB::getPDO()->execSql("
+			insert into CRM_CONFIG (CONF_KEY, CONF_VAL)
+			values ('$userFltrConfKey', '{$sqlFltr}')
+			on duplicate key update CONF_VAL = '{$sqlFltr}'
+		");
+		return [
+			'type' => 'success',
+			'msg' => 'Zapisano nowy filtr',
+			'data' => $currentFilters[$namespace]
+		];
+	}
+	public function getUserTableFilterAjaxAction() {
+		Response::sendTryCatchJson(array($this, 'getUserTableFilterAjax'), $args = 'JSON_FROM_REQUEST_BODY');
+	}
+	public function getUserTableFilterAjax($args) {
+		$namespace = V::get('namespace', '', $args);
+		if (!$namespace) throw new Exception("Missing namespace");
+
+		$userFltrConfKey = "tableColFilters__" . User::getLogin();
+		$currentFilters = DB::getPDO()->fetchValue(" select CONF_VAL from CRM_CONFIG where CONF_KEY = '{$userFltrConfKey}' ");
+		$currentFilters = ($currentFilters) ? json_decode($currentFilters, 'assoc') : [];
+		return [
+			'type' => 'success',
+			'msg' => 'Odczytano filtry użytkownika',
+			'data' => (!empty($currentFilters[$namespace])) ? $currentFilters[$namespace] : []
+		];
+	}
+
+	public function revertFromHistAjaxAction() {
+		Response::sendTryCatchJson(array($this, 'revertFromHistAjax'));
+	}
+	public function revertFromHistAjax() {
+		$typeName = V::get('typeName', '', $_REQUEST, 'word');
+		if (!$typeName) throw new Exception("Wrong param typeName");
+
+		// TODO: use namespace from url
+		// $namespace = V::get('namespace', '', $_GET, 'word');
+		// if (!$namespace) {
+		// 	$typeName = V::get('typeName', '', $_GET, 'word');
+		// 	if (!$typeName) throw new Exception("Wrong param typeName");
+		// 	$namespace = Api_WfsNs::getBaseWfsUri() . '/' . str_replace(':', '/', $typeName);
+		// }
+		// $acl = Core_AclHelper::getAclByNamespace($namespace, $forceTblAclInit = ('1' == V::get('_force', '', $_GET)));
+
+		$id = V::get('ID', '', $_REQUEST, 'word');
+		if (!$id) throw new Exception("Wrong param ID");
+		$idHist = V::get('idHist', '', $_REQUEST, 'word');
+		if (!$idHist) throw new Exception("Wrong param idHist");
+		$fieldName = V::get('fieldName', '', $_REQUEST, 'word');
+		if (!$fieldName) throw new Exception("Wrong param fieldName");
+		$acl = Core_AclHelper::getAclByTypeName($typeName);
+
+		$item = $acl->getItem($id);
+		if (!$item) throw new HttpException("Item not found", 404);
+		if (!$acl->canWriteObjectField($fieldName, $record)) throw new Exception("Missing perm Write for field {$fieldName}");
+		$histItem = $acl->getHistItem($id, $idHist);
+		if (!$histItem) throw new HttpException("Hist Item not found", 404);
+
+		$histValue = V::get($fieldName, 'N/S;', $histItem);
+		if ('N/S;' == $histValue) throw new Exception("Missing field value in hist[{$idHist}] for field({$fieldName}) from item[{$id}]");
+
+		if ($acl->isGeomField($fieldName)) {
+			$wktType = strtoupper($acl->getGeomFieldType($fieldName));
+			if (!$wktType) throw new Exception("Wrong geometry type for field {$fieldName}");
+			if ($wktType != strtoupper(substr($histValue, 0, strlen($wktType)))) throw new Exception("Wrong geometry type for field {$fieldName} in hist value");
+			$coords = trim(substr($histValue, strlen($wktType)), '()');
+			$wktValue = $acl->convertGmlCoordsToWkt($wktType, $coords, ['cs'=>' ', 'ts'=>',']);
+			if (!$wktValue) throw new Exception("BUG in hist record");
+
+			$sqlObj = array();
+			$sqlObj['ID'] = $id;
+			$sqlObj[$fieldName] = "GeomFromText('{$wktValue}')";
+			$affected = DB::getDB()->UPDATE_OBJ($acl->getName(), (object)$sqlObj);
+			if (0 == $affected) throw new AlertInfoException("Nie wprowadzono żadnych zmian");
+			else if ($affected < 0) throw new Exception("Wystąpiły błędy podczas aktualizacji rekordu [{$id}]");
+			$jsonResponse = array();
+			$jsonResponse['type'] = 'success';
+			$jsonResponse['msg'] = "Zaktualizowano dane na podstawie wcześniejszej wartości dla rekordu [{$id}]";
+			$jsonResponse['actions'] = array();
+			$jsonResponse['actions'][] = ['jsFunction'=>'TableAjax__HIST_Route', 'args'=>[$id]];
+			return $jsonResponse;
+		} else {
+			throw new HttpException("Not implemented - update from hist only for the geom field", 501);
+		}
+		throw new Exception("BUG: update field '{$fieldName}' in item[{$id}] from hist[{$idHist}]", 501);
+	}
+
+	public function removeTheGeomAjaxAction() {
+		Response::sendTryCatchJson(array($this, 'removeTheGeomAjax'), $args = 'JSON_FROM_REQUEST_BODY');
+	}
+	public function removeTheGeomAjax($args) {
+		$namespace = V::get('namespace', '', $args, 'word');
+		if (!$namespace) throw new HttpException("Bad Request - missing namespace", 400);
+		$acl = Core_AclHelper::getAclByNamespace($namespace, $forceTblAclInit = ('1' == V::get('_force', '', $_GET)));
+		$primaryKeyField = $acl->getPrimaryKeyField();
+		$primaryKey = V::get($primaryKeyField, 0, $args, 'int');
+		$geomFieldName = 'the_geom';
+		$response = new stdClass();
+
+		if ($primaryKey <= 0) throw new HttpException("Bad Request - Wrong param ID", 400);
+
+		$record = $acl->getItem($primaryKey);
+		if (!$record) throw new HttpException("Nie odnaleziono rekordu nr {$primaryKey}", 404);
+		if (!$acl->canWriteObjectField($geomFieldName, $record)) throw new HttpException("Brak dostępu do zapisu dla pola {$geomFieldName}", 403);
+
+		if (empty($record->{$geomFieldName})) {
+			$response->type = 'info';
+			$response->msg = "Rekord nie jest powiązany z żadnym obiektem na mapie";
+			$response->record = $record;
+			return $response;
+		}
+
+		$itemPatch = array();
+		$itemPatch[$geomFieldName] = "NULL";
+		$itemPatch[$primaryKeyField] = $primaryKey;
+
+		$response = new stdClass();
+		try {
+			$affected = $acl->updateItem($itemPatch);
+
+			if ($affected > 0) {
+				$response->type = 'success';
+				$response->msg = "Usunięto obiekt z mapy dla rekordu {$primaryKey}";// Rekord zapisany pomyślnie
+			} else if ($affected == 0) {
+				$response->type = 'info';
+				$response->msg = "Nie wprowadzono żadnych zmian";
+			}
+			$response->record = $acl->getItem($primaryKey);
+		}
+		catch (Exception $e) {
+			$response->type = 'error';
+			$response->msg = $e->getMessage();
+		}
+
+		return $response;
+	}
+
+	public function moreFunctionsCellAjaxAction() {
+		Response::sendTryCatchJson(array($this, 'moreFunctionsCell'), $args = $_GET);
+	}
+	public function moreFunctionsCell($args) {// ajax task 'MORE_FUNCTIONS_CELL'
+		$id = V::get('ID', 0, $args, 'int');
+		if ($id <= 0) throw new HttpException("404", 404);
+		$namespace = V::get('namespace', '', $args, 'word');
+		if (!$namespace) throw new HttpException("Bad Request - missing namespace", 400);
+		$acl = Core_AclHelper::getAclByNamespace($namespace, $forceTblAclInit = ('1' == V::get('_force', '', $args)));
+
+		$response = new stdClass();
+		$response->type = 'success';
+		$response->msg = 'Funkcje';
+		$response->rowFunctions = Core_AclHelper::getMoreFunctionsCell($acl, array('primary_key' => $id));
+		return $response;
+	}
+
+	public function editFormAction() {// namespace, _hash, _primaryKey
+		$args = $_REQUEST;
+		$id = V::get('_primaryKey', 0, $args, 'int');
+		if ($id <= 0) throw new HttpException("Bad Request - missing primaryKey", 400);
+		$namespace = V::get('namespace', '', $args, 'word');
+		if (!$namespace) throw new HttpException("Bad Request - missing namespace", 400);
+		$acl = Core_AclHelper::getAclByNamespace($namespace);
+		$tbl = $this->getTableAjaxWidget($acl);
+		$tbl->sendAjaxEdit($id, $args);
+	}
+	public function editSaveAjaxAction() {
+		Response::sendTryCatchJson(array($this, 'editSaveAjax'), $args = 'JSON_FROM_REQUEST_BODY');
+	}
+	public function editSaveAjax($args) {
+		$namespace = V::get('namespace', '', $args, 'word');
+		if (!$namespace) throw new HttpException("Bad Request - missing namespace", 400);
+		$acl = Core_AclHelper::getAclByNamespace($namespace);
+
+		$primaryKeyField = $acl->getPrimaryKeyField();
+		$primaryKey = V::get('primaryKey', 0, $args, 'int');
+		if (empty($primaryKey)) throw new HttpException("Bad Request - missing primaryKey!", 400);
+
+		$item = $acl->getItem($primaryKey);
+		if (!$item) throw new HttpException("Item not exists!", 404);
+
+		$itemFromUser = $acl->convertObjectFromUserInput($args['form'], $type = 'array_by_id', $prefix = 'f');
+
+		$response = new stdClass();
+		$response->primaryKey = $primaryKey;
+		try {
+			$itemFromUser[$primaryKeyField] = $primaryKey;
+			$affected = $acl->updateItem($itemFromUser);
+
+			if ($affected > 0) {
+				$response->type = 'success';
+				$response->msg = "Rekord zapisany pomyślnie";//"Record saved successfully";
+			} else if ($affected == 0) {
+				$response->type = 'info';
+				$response->msg = "Nie wprowadzono żadnych zmian";
+			}
+			$response->record = $acl->getItem($primaryKey);
+			$rowFunList = Core_AclHelper::getMoreFunctionsCell($acl, array('primary_key'=>$primaryKey, 'record'=>$response->record));
+			if (!empty($rowFunList)) $response->rowFunctions = $rowFunList;
+		}
+		catch (Exception $e) {
+			$response->type = 'error';
+			$response->msg = "Wystąpiły błędy!";
+			$response->msg .= $e->getMessage();
+		}
+
+		return $response;
+	}
+
+	public function typeSpecialCellAction() {
+		Response::sendTryCatchJson(array($this, 'typeSpecialCell'), $args = $_REQUEST);
+	}
+	public function typeSpecialCell($args) {
+		$namespace = V::get('namespace', '', $args, 'word');
+		if (!$namespace) throw new HttpException("Bad Request - missing namespace", 400);
+		$acl = Core_AclHelper::getAclByNamespace($namespace);
+		$id = V::get('ID', 0, $args, 'int');
+		$fieldName = V::get('col', '', $args);
+		if ($id <= 0 || empty($fieldName)) throw new HttpException("Bad Request - missing id or col", 400);
+
+		$col = $fieldName;// TODO: RM $col
+
+		$jsonData = new stdClass();
+		$idField = $acl->getFieldIdByName($fieldName);
+		if (!$idField) throw new Exception("Wrong field");
+
+		$item = $acl->getItem($id);
+		if (!$acl->canReadObjectField($fieldName, $item)) throw new Exception("Brak dostępu");
+
+		$typeSpecial = Typespecial::getInstance($idField, $fieldName);
+		if ($typeSpecial) {
+			$jsonData->data = $typeSpecial->getReturnData($acl->getID(), $id, $fieldName, '');
+			$jsonData->namespace = 'default_db/' . V::get('tbl_name', '', $jsonData->data);
+		}
+		return $jsonData;
+	}
+
+	/**
+	 * @param $_GET['namespace'] = AclNamespace
+	 * @param $_GET['format'] = 'csv' | 'html'
+	 * @param $_GET['flds'] = csv - coma separated field names
+	 * @param $_GET['sortCol'] = FieldName
+	 * @param $_GET['sortDir'] = SortDir ('desc' | 'asc')
+	 * @param $_GET['f_{$fieldName}'] = filter
+	 * @param $_GET['sf_{$fieldName}'] = force filter
+	 */
+	public function exportAction() {
+		$args = $_GET;
+		$namespace = V::get('namespace', '', $args, 'word');
+		if (!$namespace) throw new HttpException("Bad Request - missing namespace", 400);
+		$acl = Core_AclHelper::getAclByNamespace($namespace);
+
+		$exportLimit = 10000;
+		$params = array();
+		$params['limit'] = $exportLimit;
+		// $params['limitstart'] = 0;
+		$params['order_by'] = V::get('sortCol', '', $args);
+		$params['order_dir'] = V::get('sortDir', '', $args);
+		$params['cols'] = array($acl->getPrimaryKeyField());
+		$toExportFields = explode(',', V::get('flds', '', $_GET));
+		if (empty($toExportFields)) throw new Exception("Nie wybrano żandych pól do exportu.");
+		$allowedExportFieldList = Core_AclHelper::getExportFieldList($acl);
+		foreach ($toExportFields as $fieldName) {
+			if ($fieldName == $acl->getPrimaryKeyField()) continue;
+			if (!in_array($fieldName, $allowedExportFieldList)) throw new Exception("Brak uprawnień do exportu pola '{$fieldName}'");
+			$params['cols'][] = $fieldName;
+		}
+
+		$labels = array();
+		foreach ($toExportFields as $fieldName) {
+			$labels[ $fieldName ] = $acl->getFieldLabel($fieldName);
+		}
+
+		foreach ($args as $k => $v) {
+			if (strlen($k) > 3 && substr($k, 0, 2) == 'f_' && strlen($v) > 0) {// filter prefix
+				$params[$k] = $v;
+			}
+			else if (strlen($k) > 4 && substr($k, 0, 3) == 'sf_' && strlen($v) > 0) {// special filter prefix
+				$params[$k] = $v;
+			}
+		}
+		$total = $acl->getTotal($params);
+		// if ($total > $exportLimit) $params['limit'] = $exportLimit;
+		$items = $acl->getItems($params);
+
+		$format = V::get('format', 'html', $_GET);
+		if ('html' == $format) {
+			UI::gora();
+			UI::startTag('table', ['class'=>'table table-bordered table-hover']);
+				UI::startTag('thead');
+					UI::startTag('tr');
+					foreach ($labels as $fldName => $label) {
+						UI::tag('th', [], $label);
+					}
+					UI::endTag('tr');
+				UI::endTag('thead');
+				UI::startTag('tbody');
+				foreach ($items as $item) :
+					UI::startTag('tr');
+					foreach ($labels as $fldName => $label) :
+						UI::tag('td', [], $item->{$fldName});
+					endforeach;
+					UI::endTag('tr');
+				endforeach;
+				UI::endTag('tbody');
+			UI::endTag('table');
+			UI::dol();
+		}
+		else if ('csv' == $format) {
+			$csvFileName = "Tabela-" . $acl->getName() . "-" . date("Y-m-d_H_s");
+			header('Content-Type: text/csv; charset=utf-8');
+			header("Content-Disposition: attachment; filename={$csvFileName}.csv");
+			$csvSeparator = ';';
+
+			$labelsLine = array();
+			foreach ($labels as $fldName => $label) {
+				$labelsLine[] = '"' . addslashes($label) . '"';
+			}
+			echo implode($csvSeparator, $labelsLine) . "\n";
+
+			foreach ($items as $item) {
+				$itemLine = array();
+				foreach ($labels as $fldName => $label) {
+					$itemLine[] = '"' . addslashes($item->{$fldName}) . '"';
+				}
+				echo implode($csvSeparator, $itemLine) . "\n";
+			}
+		}
+		else {
+			die("Nieobsługiwany format danych.");
+		}
+	}
+
+	public function loadDataAjaxAction() {
+		// $tbl = $this->getTableAjaxWidget($acl);
+		Response::sendTryCatchJson(array($this, 'loadDataAjax'), $args = $_GET);
+	}
+	public function loadDataAjax($args) {
+		$namespace = V::get('namespace', '', $args, 'word');
+		if (!$namespace) throw new HttpException("Bad Request - missing namespace", 400);
+		$acl = P5::getAclByNamespace($namespace);
+
+		$uiConf = [
+			'pageSize' => 10
+		];
+		$DBG = ('1' == V::get('DBG', '', $args));
+		$pageSize = V::get('pageSize', $uiConf['pageSize'], $args, 'int');
+		$page = V::get('page', 0, $args, 'int');
+		$currSortCol = V::get('currSortCol', '', $args);
+		$currSortFlip = V::get('currSortFlip', '', $args);
+		if ($page > 0) {
+			$page -= 1;
+		}
+
+		$params = array();
+		$params['limit'] = $pageSize;
+		$params['limitstart'] = $page * $params['limit'];
+		$params['order_by'] = ($currSortCol)? $currSortCol : '';
+		$params['order_dir'] = $currSortFlip;
+
+		$filters = new stdClass();
+		$filters->currSortCol = $currSortCol;
+		$filters->currSortFlip = $currSortFlip;
+		foreach ($args as $k => $v) {
+			if (strlen($k) > 3 && substr($k, 0, 2) == 'f_' && strlen($v) > 0) {// filter prefix
+				$params[$k] = $v;
+				$filters->{$k} = $v;
+			}
+			else if (strlen($k) > 4 && substr($k, 0, 3) == 'sf_' && strlen($v) > 0) {// special filter prefix
+				$params[$k] = $v;
+				$filters->{$k} = $v;
+			}
+		}
+
+		// TODO: $this->setFilters($filters);
+
+		$vCols = $acl->getVirtualFieldListByIdZasob();
+		if (!empty($vCols)) {
+			if($DBG){echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">vCols (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($vCols);echo'</pre>';}
+		}
+
+		$visibleCols = $acl->getVisibleFieldListByIdZasob();
+		if($DBG){echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">visibleCols (F.' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($visibleCols);echo'</pre>';}
+
+		$jsonData = new stdClass();
+		$jsonData->page = $page + 1;
+		$jsonData->pageSize = $pageSize;
+		$jsonData->filters = $filters;
+		$jsonData->cols = new stdClass();
+		$jsonData->uniqueCol = $acl->getPrimaryKeyField();
+		$ind = 0;
+		foreach ($visibleCols as $fieldID => $col) {
+			$ind++;
+			$columnConfig = (object)array('index'=>$ind);
+			if (in_array($col, array('A_STATUS','A_STATUS_CURRENT','A_SERVICES_STATUS_CURRENT'))) {
+				$columnConfig->format = '<div class="cell-A_STATUS-{0}">{0}</div>';
+			}
+			else if (in_array($col, array('Status'))) {
+				// Ahmes problems Status colors
+				$columnConfig->format = '<div class="cell-Status-{0}">{0}</div>';
+			}
+			else if ($acl->isGeomField($col)) {
+				$columnConfig->type = 'geom';
+			}
+			if ('' !== ($label = $acl->getFieldLabel($col))) {
+				$columnConfig->friendly = $label;
+			}
+			$colType = $acl->getFieldType($col);
+			if ($colType) {// @see MarkTableAjaxFilterColType
+				if($DBG){echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">field('.$col.') $colType (F.' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($colType);echo'</pre>';}
+				if ($colType['type'] == 'date') {
+					//$columnConfig->type = 'date';// TODO: require datetimepicker
+				}
+				if (!empty($colType['simpleType'])) $columnConfig->type = $colType['simpleType'];
+			} else {// typespecial - no type
+				$columnConfig->type = 'special';
+			}
+			$columnConfig->xsdType = $acl->getXsdFieldType($col);
+			if ($columnConfig->xsdType) {
+				$ex = explode(":", $columnConfig->xsdType);
+				switch ($ex[0]) {
+					case 'ref':
+					case 'alias_ref':
+						if (3 != count($ex)) throw new HttpException("Schema Error for field({$col}) xsdType({$columnConfig->xsdType})", 500);
+						$columnConfig->type = 'ref';
+						$columnConfig->xsdRefUri = Api_WfsNs::getNsUri($ex[1]);
+						$columnConfig->xsdRefType = $ex[2];
+						$columnConfig->xsdRefNsPrefix = $ex[1];
+						break;
+					case 'xsd':
+						switch ($ex[1]) {
+							case 'string': $columnConfig->type = 'string'; break;
+							case 'ind': $columnConfig->type = 'number'; break;// TODO: bug 'ind'?
+						}
+						break;
+					case 'p5':
+						switch ($ex[1]) {
+							case 'alias': {
+								$format = $acl->getXsdFieldParam($col, 'format');
+								if ($format) $columnConfig->format = $format;
+							} break;
+							case 'string': {
+								$columnConfig->type = 'p5:string';
+								$columnConfig->formatByValue = $acl->getXsdFieldParam($col, 'formatByValue');
+							} break;
+						}
+						break;
+				}
+			}
+
+			$typeSpecial = Typespecial::getInstance($fieldID, $col);
+			if ($typeSpecial) {
+				if($DBG){echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">typeSpecial (F.' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($typeSpecial);echo'</pre>';}
+				$columnConfig->_tsRetId = $typeSpecial->getReturnId();
+				if ($columnConfig->_tsRetId == 0) {
+					$tsParamOut = V::get('param_out', null, $typeSpecial);
+					if ($tsParamOut) {
+						$tsFormat = V::get('format', null, $tsParamOut);
+						$tsValues = V::get('values', null, $tsParamOut);
+						$tsAliases = V::get('alias', null, $tsParamOut);
+						if (false !== strpos($tsFormat, '<a') && !empty($tsValues) && !empty($tsAliases)) {
+							$tsAliasMap = array();
+							/* [values] => Array([ID] => 1467), [alias] => Array([1467] => ID) */
+							$bugTsColsNotVisible = array();
+							foreach ($tsValues as $kVarName => $vIdZasob) {
+								if (array_key_exists($vIdZasob, $tsAliases)) {
+									$tsAliasMap[$kVarName] = $tsAliases[$vIdZasob];
+								} else {
+									$bugTsColsNotVisible[] = "noAliasFor {$vIdZasob}";
+								}
+								if (!array_key_exists($vIdZasob, $visibleCols)) {
+									$bugTsColsNotVisible[] = $vIdZasob;
+								}
+							}
+							if (empty($bugTsColsNotVisible)) {
+								$tsSimpleLink = new stdClass();
+								$tsSimpleLink->format = $tsFormat;
+								$tsSimpleLink->aliasMap = $tsAliasMap;
+								$columnConfig->_tsSimpleLink = $tsSimpleLink;
+								$columnConfig->type = 'simpleLink';
+							} else {
+								$columnConfig->_tsSimpleLinkBug = $bugTsColsNotVisible;
+							}
+						}
+					}
+				}
+			}
+
+			if ($columnConfig->xsdType) {// fix fields type p5:typeSpecialSimpleLink (previously defined by Typespecial)
+				switch ($columnConfig->xsdType) {
+					case 'p5:typeSpecialSimpleLink': {
+						$columnConfig->type = 'simpleLink';
+						$columnConfig->_tsRetId = 0;
+						$columnConfig->_tsSimpleLink = new stdClass();
+						$columnConfig->_tsSimpleLink->format = $acl->getXsdFieldParam($col, 'format');
+						$columnConfig->_tsSimpleLink->aliasMap = $acl->getXsdFieldParam($col, 'aliasMap');
+					} break;
+				}
+			}
+
+			// @see ajaxHiddenColsSave
+			if (UserProfile::isHiddenColumn($acl->getID(), $fieldID)) {
+				$columnConfig->hidden = true;
+			}
+
+			$columnConfig->description = $acl->getFieldOpis($col);
+
+			$jsonData->cols->{$col} = $columnConfig;
+		}
+		$jsonData->rows = array();
+		$jsonData->total = $acl->getTotal($params);
+		if($DBG){echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">get_total (F.' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($jsonData->total);echo'</pre>';}
+		$items = $acl->getItems($params);
+		foreach ($items as $idx => $item) $items[$idx] = (array)$item;
+		if($DBG){echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">items (F.' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($items);echo'</pre>';}
+		// TODO: add virtual data by Typespecial
+		if (!empty($vCols) && !empty($items)) {
+			foreach ($vCols as $vColID => $vCol) {
+				$colType = $acl->getFieldTypeById($vColID);
+				if ($colType) continue;// pomin Typespecial dla realnych komorek w bazie danych
+
+				$typeSpecial = Typespecial::getInstance($vColID, $vCol);
+				if ($typeSpecial) {
+					$columnConfig = V::get($vCol, null, $jsonData->cols);
+					if ($columnConfig && !empty($columnConfig->_tsSimpleLink)) {
+						// pomin simple link values - mved to js render
+					} else {
+						if($DBG){echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">Typespecial('.$vColID.') (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($typeSpecial);echo'</pre>';}
+						$ids = array_keys($items);
+						$specialValues = $typeSpecial->getValuesByIds($this->_zasobID, $ids);
+						if($DBG){echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">Typespecial('.$vCol.') specialValues (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($specialValues);echo'</pre>';}
+						if (!empty($specialValues)) foreach ($specialValues as $kItemID => $vValues) {
+							$tsValue = implode('<br>', $vValues);
+							if($DBG){echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">Item['.$kItemID.'].'.$vCol.' specialValues (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($items[$kItemID]);echo'</pre>';}
+							if (!empty($items[$kItemID][$vCol]) && !empty($tsValue)) {
+								$items[$kItemID][$vCol] .= ": {$tsValue}";
+							} else {
+								$items[$kItemID][$vCol] = $tsValue;
+							}
+						}
+					}
+				}
+			}
+		}
+		{
+			if(V::get('DBG', '', $_GET)){$jsonData->__DBG__ = [];}
+			$p5Alias = [];
+			foreach ((array)$jsonData->cols as $fieldName => $conf) {
+				if ('p5:alias' == V::get('xsdType', '', $conf)) {
+					if(V::get('DBG', '', $_GET)){$jsonData->__DBG__[$fieldName] = $conf;}
+					$aliasType = $acl->getXsdFieldParam($fieldName, 'type');// 'type' => 'sql_table_alias',
+					if ('sql_table_alias' == $aliasType) {
+						$p5Alias[$fieldName] = (array)$conf;
+						$p5Alias[$fieldName]['type'] = 'sql_table_alias';
+						$p5Alias[$fieldName]['local_join_key'] = $acl->getXsdFieldParam($fieldName, 'local_join_key');// 'local_join_key' => 'ID',
+						$p5Alias[$fieldName]['remote_table_name'] = $acl->getXsdFieldParam($fieldName, 'remote_table_name');// 'remote_table_name' => 'CRM_LISTA_ZASOBOW_ORDERS_summary_view',
+						$p5Alias[$fieldName]['remote_join_key'] = $acl->getXsdFieldParam($fieldName, 'remote_join_key');// 'remote_join_key' => 'ID',
+						$p5Alias[$fieldName]['remote_column_value'] = $acl->getXsdFieldParam($fieldName, 'remote_column_value');// 'remote_column_value' => 'SUM_POS',
+					} else if ('sql_query_alias' == $aliasType) {
+						$p5Alias[$fieldName] = (array)$conf;
+						$p5Alias[$fieldName]['type'] = 'sql_query_alias';
+						$p5Alias[$fieldName]['local_join_key'] = $acl->getXsdFieldParam($fieldName, 'local_join_key');
+						$p5Alias[$fieldName]['join_query_format'] = $acl->getXsdFieldParam($fieldName, 'join_query_format');
+					}
+				}
+			}
+			if(V::get('DBG', '', $_GET)){$jsonData->__DBG__['$p5Alias'] = $p5Alias;}
+			if(V::get('DBG', '', $_GET)){$jsonData->__DBG__['$aliasValuesRaw'] = [];}
+			if ($p5Alias) {
+				$sqlLocalTableName = $acl->getRootTableName();
+				$primaryKeyField = $acl->getPrimaryKeyField();
+				foreach ($p5Alias as $fieldName => $conf) {
+					$aliasValuesRaw = [];
+
+					if ('sql_table_alias' == $conf['type']) {
+						$localKeys = [];
+						$localKeyName = V::get('local_join_key', '', $conf);
+						if ($localKeyName) {
+							if (!array_key_exists($localKeyName, $localKeys)) {
+								$localKeys[$localKeyName] = [];
+								foreach ($items as $item) {
+									$keyLocal = V::get($localKeyName, 0, $item);
+									if ($keyLocal) $localKeys[$localKeyName][] = DB::getPDO()->quote($keyLocal, PDO::PARAM_STR);
+								}
+							}
+							if(V::get('DBG', '', $_GET)){$jsonData->__DBG__['$localKeys'] = $localKeys;}
+							if (!empty($localKeys[$localKeyName])) {
+								$sqlRemoteTable = V::get('remote_table_name', '', $conf);
+								$sqlRemoteValueFieldName = V::get('remote_column_value', '', $conf);
+								$sqlRemoteKeyName = V::get('remote_join_key', '', $conf);
+								$sqlLocalKeyName = $acl->getSqlFieldName($localKeyName);
+								$aliasValuesRaw = DB::getPDO()->fetchAllByKey("
+									select r.{$sqlRemoteKeyName} as {$sqlLocalKeyName}, r.{$sqlRemoteValueFieldName} as remote_value
+									from {$sqlRemoteTable} r
+									where r.{$sqlRemoteKeyName} in(" . implode(",", $localKeys[$localKeyName]) . ")
+								", $sqlLocalKeyName);
+								if(V::get('DBG', '', $_GET)){$jsonData->__DBG__['$aliasValuesRaw'][$fieldName] = $aliasValuesRaw;}
+							}
+						}
+					} else if ('sql_query_alias' == $conf['type']) {
+						$localKeys = [];
+						$localKeyName = V::get('local_join_key', '', $conf);
+						$sqlFormat = V::get('join_query_format', '', $conf);
+						if ($localKeyName && $sqlFormat) {
+							if (!array_key_exists($localKeyName, $localKeys)) {
+								$localKeys[$localKeyName] = [];
+								foreach ($items as $item) {
+									$keyLocal = V::get($localKeyName, 0, $item);
+									if ($keyLocal) $localKeys[$localKeyName][] = DB::getPDO()->quote($keyLocal, PDO::PARAM_STR);
+								}
+							}
+						}
+						if (!empty($localKeys[$localKeyName])) {
+							$sql = str_replace('{sql_in_local_join_key}', implode(",", $localKeys[$localKeyName]), $sqlFormat);
+							$sqlLocalKeyName = $acl->getSqlFieldName($localKeyName);
+							$aliasValuesRaw = DB::getPDO()->fetchAllByKey($sql, $sqlLocalKeyName);
+						}
+					}
+					DBG::log($aliasValuesRaw, 'array', '$aliasValuesRaw');
+					if (!empty($aliasValuesRaw)) {
+						if(V::get('DBG_P5', '', $_GET)){echo '{ "$aliasValuesRaw": '.json_encode($aliasValuesRaw).', "dbg": [' . "\n";}
+						array_walk($items, function (&$item) use ($fieldName, $sqlLocalKeyName, $aliasValuesRaw) {
+							$sqlValue = V::get($sqlLocalKeyName, null, $item);
+							if(V::get('DBG_P5', '', $_GET)){echo '{ "sqlValue": "'.$sqlValue.'", "remote_value": "'.$aliasValuesRaw[$sqlValue]['remote_value'].'", "item": '.json_encode($item) . '},' . "\n";}
+							if ($sqlValue !== null && array_key_exists($sqlValue, $aliasValuesRaw)) {
+								if(V::get('DBG_P5', '', $_GET)){echo '{ "TODO_update_value": "'.$aliasValuesRaw[$sqlValue]['remote_value'].'"},' . "\n";}
+								$item[$fieldName] = $aliasValuesRaw[$sqlValue]['remote_value'];
+								if(V::get('DBG_P5', '', $_GET)){echo '{ "TODO_updated_value": "'.$item[$fieldName].'"},' . "\n";}
+							}
+						});
+						if(V::get('DBG_P5', '', $_GET)){echo "\n".'{}]}';die();}
+					}
+				}
+			}
+		}
+		foreach ($items as $item) {
+			// TODO: hide items without 'R'
+			foreach ($visibleCols as $fieldName) {
+				// TODO: ID default 'R'
+				if (!$acl->canReadObjectField($fieldName, $item)) $item[$fieldName] = '*****';
+
+				// null => empty string
+				if (!isset($item[$fieldName]) || (!$item[$fieldName] && $item[$fieldName] !== '0')) {
+					if($DBG){echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">isEmptyString['.$fieldName.'] (F.' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($item[$fieldName]);echo'</pre>';}
+					$item[$fieldName] = '';
+				}
+			}
+			$jsonData->rows[] = $item;
+		}
+		$jsonData->type = 'success';
+		$jsonData->msg = 'pobrano nowe dane';
+		return $jsonData;
+	}
+
+}

+ 1 - 0
SE/se-lib/Route/ViewTableAjax.php

@@ -172,6 +172,7 @@ class Route_ViewTableAjax extends RouteBase {
 			UI::startContainer();
 			UI::alert('danger', "<strong>Wystąpiły błędy!</strong> " . $e->getMessage());
 			UI::endContainer();
+			DBG::log($e);
 		}
 		UI::dol();
 	}

+ 4 - 0
SE/se-lib/Schema/SystemObjectFieldStorageAcl.php

@@ -218,6 +218,10 @@ class Schema_SystemObjectFieldStorageAcl extends Core_AclSimpleSchemaBase {
         'isActive' => 1
       ]);
     }
+    SchemaFactory::loadDefaultObject('SystemObject')->updateItem([
+      'namespace' => $item['namespace'],
+      'isStructInstalled' => 1
+    ]);
     DB::getPDO()->execSql("
       update `{$this->_rootTableName}` t
         join CRM_LISTA_ZASOBOW zp on(zp.PARENT_ID = {$item['idDatabase']} and zp.`DESC` = '{$item['_rootTableName']}')

+ 19 - 3
SE/se-lib/Schema/SystemObjectStorageAcl.php

@@ -298,7 +298,7 @@ class Schema_SystemObjectStorageAcl extends Core_AclSimpleSchemaBase {
       where t.`{$pkField}` = {$sqlPk}
     ");
     if (!$item) throw new Exception("Item '{$pk}' not exists - type '{$this->_namespace}'");
-    return $this->buildFeatureFromSqlRow($item);
+    return $this->buildFeatureFromSqlRow($item, $params);
   }
 
   public function getItems($params = []) {
@@ -327,15 +327,31 @@ class Schema_SystemObjectStorageAcl extends Core_AclSimpleSchemaBase {
       {$sqlWhere}
       {$sqlOrderBy}
       {$sqlLimit}
-    "));
+    "), $params);
   }
 
-  public function buildFeatureFromSqlRow($item) {
+  public function buildFeatureFromSqlRow($item, $params = []) {
+    DBG::log($params, 'array', "buildFeatureFromSqlRow...");
     $exNs = explode('/', $item['namespace']);
     $item['name'] = array_pop($exNs);
     $item['nsPrefix'] = implode('__x3A__', $exNs);
     $item['typeName'] = implode('__x3A__', $exNs) . ':' . $item['name'];
     $item['reinstallLink'] = Router::getRoute('Storage')->getLink('objectReinstall', [ 'namespace' => $item['namespace'] ]);
+    if (!empty($params['propertyName'])) {
+      if (is_string($params['propertyName'])) $params['propertyName'] = explode(',', $params['propertyName']);
+      if (!is_array($params['propertyName'])) throw new Exception("Wrong param propertyName - expected array or string");
+      foreach ($params['propertyName'] as $fetchField) {
+        if ('*' == $fetchField) continue;
+        if ('field' == $fetchField) {
+          $item['field'] = SchemaFactory::loadDefaultObject('SystemObjectField')->getItems([
+            '__backRef' => [
+              'namespace' => 'default_objects/SystemObject',
+              'primaryKey' => $item['namespace']
+            ]
+          ]);
+        }
+      }
+    }
     return $item;
   }