AclSimpleSchemaBase.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. <?php
  2. Lib::loadClass('Api_WfsNs');
  3. Lib::loadClass('Api_WfsException');
  4. Lib::loadClass('User');
  5. Lib::loadClass('Core_AclHelper');
  6. Lib::loadClass('Core_AclBase');
  7. // TODO: need PermProfile - array of perm profile list or just 'R,W,X,C,S', etc.
  8. class Core_AclSimpleSchemaBase extends Core_AclBase {
  9. /** simpleSchema - php structure:
  10. $simpleSchema = [
  11. 'root' => [
  12. '@namespace' => 'default_db/ZALICZKA/Zaliczka',
  13. '@primaryKey' => 'ID',
  14. 'ID' => 'xsd:integer', // short syntax - define only simpleType
  15. 'KWOTA' => [ // long syntax - define type with another params like restrictions
  16. '@type' => 'xsd:decimal'
  17. ],
  18. 'worker' => 'ref:Worker' // short syntax - define only type = ref
  19. 'pozycja' => [ // long syntax - define ref with maxOccurs, TODO: use more xsd attributes
  20. '@ref' => 'ZaliczkaPozycja',
  21. '@maxOccurs' => 'unbounded'
  22. ]
  23. ],
  24. 'Worker' => [
  25. '@namespace' => 'default_objects/AccessOwner',
  26. ...
  27. ],
  28. 'ZaliczkaPozycja' => [
  29. '@namespace' => 'default_db/ZALICZKA_POZYCJA/ZaliczkaPozycja',
  30. ...
  31. ]
  32. 'YT_LINK' => [ '@type' => 'p5:typeSpecialSimpleLink',
  33. '@label' => "Youtube link",
  34. '@@params' => [// $acl->getXsdFieldParam($col, 'format');
  35. 'format' => '<a href="https://www.youtube.com/watch?v={LINK}" target="_blank"></a>',
  36. 'aliasMap' => [
  37. 'LINK' => 'LINK'
  38. ]
  39. ]
  40. ],
  41. ]
  42. */
  43. public $_simpleSchema = array();
  44. public $_xsdTypes = null;// set by parseXsdTypes()
  45. public $_name = '';
  46. public $_namespace = '';
  47. public $_primaryKey = '';
  48. public $_rootTableName = '';
  49. public $_sourceNamespace = '';
  50. public $_xmlnsMap = [];
  51. public function __construct($simpleSchema = null) {
  52. if ($simpleSchema) $this->_simpleSchema = $simpleSchema;
  53. if (!$this->_simpleSchema) throw new Exception("Missing simpleSchema");
  54. if (empty($this->_simpleSchema['root'])) throw new Exception("Wrong simpleSchema syntax");
  55. if (empty($this->_simpleSchema['root']['@namespace'])) throw new Exception("Missing @namespace in simpleSchema");
  56. $this->_namespace = $this->_simpleSchema['root']['@namespace'];
  57. $ns = explode('/', $this->_namespace);
  58. $this->_name = end($ns);
  59. if (empty($this->_rootTableName)) {
  60. if (count($ns) < 3) throw new Exception("Wrong @namespace syntax in simpleSchema ns({$this->_simpleSchema['root']['@namespace']})");
  61. $this->_rootTableName = $ns[1];
  62. }
  63. {
  64. $this->_sourceNamespace = explode('/', $this->_namespace);
  65. array_pop($this->_sourceNamespace);// remove name
  66. $this->_sourceNamespace = implode('__x3A__', $this->_sourceNamespace);
  67. }
  68. if (empty($this->_simpleSchema['root']['@primaryKey'])) $this->_simpleSchema['root']['@primaryKey'] = 'ID';// TODO: throw new Exception("Missing @primaryKey in simpleSchema '{$this->_name}'");
  69. $this->_primaryKey = $this->_simpleSchema['root']['@primaryKey'];// TODO: check if field exists
  70. {// validate and fix _simpleSchema:
  71. // - convert field scalar to [ '@type' => ... ]
  72. // - check required @namespace attribute
  73. foreach ($this->_simpleSchema as $keySchema => $schema) {
  74. foreach ($schema as $fieldName => $params) {
  75. if ('@' == substr($fieldName, 0, 1)) continue;// skip params
  76. if (is_scalar($params)) {
  77. $this->_simpleSchema[ $keySchema ][ $fieldName ] = [ '@type' => $params ];
  78. } else if (!is_array($params)) {
  79. throw new Exception("Parse error - simpleSchema field type");
  80. }
  81. }
  82. if (empty($schema['@namespace'])) throw new Exception("Missing @namespace in schema for '{$keySchema}'");
  83. $ns = explode('/', $schema['@namespace']);
  84. $name = end($ns);
  85. if (count($ns) < 2) throw new Exception("Wrong @namespace syntax in schema for '{$keySchema}'");
  86. }
  87. }
  88. // $this->parseXsdTypes();// parse xsdTypes
  89. $this->_xsdTypes = array();
  90. {
  91. $generatedIdZasob = 10000;// fake zasob id
  92. foreach ($this->_simpleSchema['root'] as $key => $value) {
  93. if ('@' == substr($key, 0, 1)) continue;// skip attributes
  94. if (is_array($value)) {
  95. $fieldName = $key;
  96. $field = [ 'name' => $fieldName, 'perms' => '', 'idZasob' => $generatedIdZasob ];
  97. if (!empty($value['@label'])) $field['label'] = $value['@label'];
  98. if (!empty($value['@type'])) $field['xsdType'] = "{$value['@type']}";
  99. else if (!empty($value['@ref'])) {
  100. $field['xsdType'] = (false !== strpos($value['@ref'], '/'))
  101. ? "ref_uri:{$value['@ref']}"
  102. : "ref:{$value['@ref']}";
  103. }
  104. else throw new Exception("StorageAcl - field type not defined '{$key}'");
  105. if (!empty($value['@maxOccurs'])) $field['maxOccurs'] = $value['@maxOccurs'];
  106. $this->_xsdTypes[$fieldName] = $field;
  107. } else if (is_scalar($value)) {// short syntax: $fieldName => $xsdType
  108. $fieldName = $key;
  109. $field = [ 'name' => $fieldName, 'perms' => '', 'idZasob' => $generatedIdZasob ];
  110. $field['xsdType'] = $value;
  111. $this->_xsdTypes[$fieldName] = $field;
  112. } else {
  113. throw new Exception("StorageAcl - TODO: Unimplemented value type in simpleSchema: " . json_encode($value));
  114. }
  115. $generatedIdZasob++;
  116. }
  117. }
  118. // TODO: fix 'ref:*' types - use Core_AclHelper::parseNamespaceUrl($namespace)
  119. }
  120. public function __toString() {
  121. $out = "xsd @prefix(default_db__x3A__{$this->_rootTableName})" . "\n";
  122. $aliasRefUri = array();
  123. foreach ($this->_simpleSchema as $objectName => $schema) {
  124. if ('root' == $objectName) $objectName = $this->_name;
  125. $out .= "\t" . "{$objectName}";
  126. $out .= " @namespace({$schema['@namespace']})";
  127. $out .= "\n";
  128. foreach ($schema as $fieldName => $field) {
  129. if ('@' == substr($fieldName, 0, 1)) continue;// skip tags
  130. $out .= "\t\t" . "{$fieldName}";
  131. if (!empty($field['@type'])) {
  132. $out .= " @type({$field['@type']})";
  133. if (!empty($field['@alias'])) $out .= " @alias({$field['@alias']})";
  134. } else if (!empty($field['@ref'])) {
  135. $out .= " @ref({$field['@ref']})";
  136. if (false !== strpos($field['@ref'], '/')) $aliasRefUri[ $fieldName ] = $field['@ref'];
  137. } else {
  138. $out .= " @BUG('missing @type or @ref')";
  139. }
  140. // TODO: maxOccurs, nillable, etc.
  141. $out .= "\n";
  142. }
  143. foreach ($aliasRefUri as $fieldName => $nsUri) {
  144. $out .= "\t" . "{$objectName} @ref({$nsUri})" . "\n";
  145. // TODO: maxOccurs, nillable, etc.
  146. }
  147. }
  148. return $out;
  149. }
  150. public function hasSimpleSchema() { return true; }
  151. public function getSimpleSchema() { return $this->_simpleSchema; }
  152. public function getSimpleSchemaTree() {
  153. $tree = array();
  154. $tree['@namespace'] = $this->getNamespace();
  155. foreach ($this->getXsdTypes() as $fieldName => $field) {
  156. $tree[$fieldName] = $field;
  157. if (is_array($field) && 'ref_uri:' == substr($field['xsdType'], 0, 8)) {
  158. $tree[$fieldName] = $this->_getSimpleSchemaTreeRec(substr($field['xsdType'], 8), $this->getNamespace());
  159. }
  160. }
  161. return $tree;
  162. }
  163. public function _getSimpleSchemaTreeRec($ref, $namespacePath = '', $loopCount = 0) {
  164. if (++$loopCount > 100) die("Recurse limit in getSimpleSchemaTree for schema({$this->_namespace})");
  165. // echo "<p>DBG: F._getSimpleSchemaTreeRec({$ref}) \$namespacePath[$namespacePath]</p>";
  166. $tree = array();
  167. if (!empty($this->_simpleSchema[$ref])) {
  168. $tree = array();
  169. foreach ($this->_simpleSchema[$ref] as $fieldName => $field) {
  170. // echo "<p>DBG: F._getSimpleSchemaTreeRec({$ref}) \$namespacePath[$namespacePath] - 1/'{$fieldName}'</p>";
  171. $tree[$fieldName] = $field;
  172. if (is_array($field) && 'ref_uri:' == substr($field['xsdType'], 0, 8)) {
  173. $nsField = substr($field['xsdType'], 8);
  174. if (in_array($nsField, explode(',', $namespacePath))) {
  175. $tree[$fieldName]['@recurse_namespace'] = true;
  176. continue;
  177. }
  178. $tree[$fieldName] = $this->_getSimpleSchemaTreeRec($nsField, "{$namespacePath},{$nsField}", $loopCount);
  179. }
  180. }
  181. } else {
  182. $acl = Core_AclHelper::getAclByNamespace($ref);
  183. $tree['@namespace'] = $acl->getNamespace();
  184. foreach ($acl->getXsdTypes() as $fieldName => $field) {
  185. // echo "<p>DBG: F._getSimpleSchemaTreeRec({$ref}) \$namespacePath[$namespacePath] - 2/'{$fieldName}'</p>";
  186. $tree[$fieldName] = $field;
  187. if (is_array($field) && 'ref_uri:' == substr($field['xsdType'], 0, 8)) {
  188. $nsField = substr($field['xsdType'], 8);
  189. if (in_array($nsField, explode(',', $namespacePath))) {
  190. $tree[$fieldName]['@recurse_namespace'] = true;
  191. continue;
  192. }
  193. $tree[$fieldName] = $this->_getSimpleSchemaTreeRec($nsField, "{$namespacePath},{$nsField}", $loopCount);
  194. }
  195. }
  196. }
  197. // echo'<pre>F._getSimpleSchemaTreeRec('.$ref.') tree';print_r($tree);echo'</pre>';
  198. return $tree;
  199. }
  200. public function getName() { return $this->_name; }
  201. public function getRootTableName() { return $this->_rootTableName; }
  202. public function getXsdTypes() { return $this->_xsdTypes; }
  203. public function getNamespace() { return $this->_namespace; }
  204. public function getSourceName() { return $this->_sourceNamespace; }
  205. public function init($force = false) {}
  206. public function isInitialized() { return true; }
  207. public function getFieldListByIdZasob() { return $this->getRealFieldListByIdZasob(); }
  208. public function getVisibleFieldListByIdZasob() { return $this->getRealFieldListByIdZasob(); }
  209. public function getVirtualFieldListByIdZasob() { return array(); }
  210. public function getRealFieldListByIdZasob($force = false) {
  211. $cols = array();
  212. foreach ($this->getFields() as $idField => $field) {
  213. $cols[$idField] = $field['name'];
  214. }
  215. return $cols;
  216. }
  217. public function getFields() {// @returns array - $this->_fields
  218. $fieldsById = array();
  219. foreach ($this->getXsdTypes() as $fieldName => $field) {
  220. $field['name'] = $fieldName;
  221. $field['label'] = V::get('label', $fieldName, $field);
  222. if ('p5:www_link' == $field['xsdType']) $field['simpleType'] = 'p5:www_link';
  223. $fieldsById[ $field['idZasob'] ] = $field;
  224. }
  225. return $fieldsById;
  226. }
  227. public function getFieldType($fieldName) {
  228. foreach ($this->getFields() as $field) {
  229. if ($fieldName == $field['name']) return $field;
  230. }
  231. return null;
  232. }
  233. // TODO: replace legacy functions: isAllowed, hasFieldPerm, getFieldIdByName
  234. public function canCreateField($fieldName) { return false; }// TODO: perms from Procesy
  235. public function canReadField($fieldName) { return true; }// TODO: perms from Procesy
  236. public function canReadObjectField($fieldName, $record) { return true; }// TODO: perms from Procesy
  237. public function canWriteField($fieldName) { return false; }// TODO: perms from Procesy
  238. public function canWriteObjectField($fieldName, $record) { return false; }// TODO: perms from Procesy
  239. public function getTotal($params = array()) { throw new Exception("Unimplemented - TODO: " . get_class($this) . "::" . __FUNCTION__); }// TODO: use ParseOgcQuery
  240. public function getItem($primaryKey, $params = []) { throw new Exception("Unimplemented - TODO: " . get_class($this) . "::" . __FUNCTION__); }
  241. public function getItems($params = array()) { throw new Exception("Unimplemented - TODO: " . get_class($this) . "::" . __FUNCTION__); }// TODO: use ParseOgcQuery
  242. public function itemsFetchRefs(&$items) { throw new Exception("Unimplemented - TODO: " . get_class($this) . "::" . __FUNCTION__); }// TODO: , $fieldName = ''
  243. public function fetchItemFieldRefs($primaryKey, $fieldName) {
  244. $refTable = ACL::getRefTable($this->getNamespace(), $fieldName);
  245. $sqlPk = DB::getPDO()->quote($primaryKey, PDO::PARAM_STR);
  246. return array_map(
  247. function ($row) {
  248. return [
  249. 'xlink' => "{$row['REMOTE_TYPENAME']}.{$row['REMOTE_PRIMARY_KEY']}" // TODO:
  250. ];
  251. }
  252. , DB::getPDO()->fetchAll("
  253. select r.REMOTE_PRIMARY_KEY, r.REMOTE_TYPENAME
  254. from `{$refTable}` r
  255. where r.PRIMARY_KEY = {$sqlPk}
  256. and r.A_STATUS != 'DELETED'
  257. ")
  258. );
  259. }
  260. public function addItem($itemTodo) { throw new Exception("Unimplemented - TODO: " . get_class($this) . "::" . __FUNCTION__); }
  261. public function updateItem($itemPatch) { throw new Exception("Unimplemented - TODO: " . get_class($this) . "::" . __FUNCTION__); }
  262. public function getGeomFieldType($fieldName) { return null; }
  263. public function getPrimaryKeyField() { return $this->_primaryKey; }
  264. public function getSqlPrimaryKeyField() {
  265. return (!empty($this->_simpleSchema['root'][$this->_primaryKey]['@alias']))
  266. ? $this->_simpleSchema['root'][$this->_primaryKey]['@alias']
  267. : $this->_primaryKey;
  268. }
  269. public function getSqlFieldName($fieldName) {
  270. if (empty($this->_simpleSchema['root'][$fieldName])) throw new Exception("Missing field in schema '{$fieldName}'");
  271. return (!empty($this->_simpleSchema['root'][$fieldName]['@alias']))
  272. ? $this->_simpleSchema['root'][$fieldName]['@alias']
  273. : $fieldName;
  274. }
  275. public function getID() {
  276. list($databaseName) = explode('/', $this->_namespace);
  277. if ('default_db' == $databaseName) {
  278. $idZasobDatabase = (int)DB::getPDO($databaseName)->getZasobId();
  279. if ($idZasobDatabase <= 0) throw new Exception("Missing database id");
  280. $sqlNamespace = DB::getPDO()->quote($this->_namespace, PDO::PARAM_STR);
  281. $zasob = DB::getPDO()->fetchFirst("
  282. select z.ID, z.`DESC`, z.`TYPE`
  283. from CRM_LISTA_ZASOBOW z
  284. where z.`TYPE` = 'TABELA'
  285. and z.`DESC` = {$sqlNamespace}
  286. and z.PARENT_ID = {$idZasobDatabase}
  287. and z.A_STATUS not in('DELETED')
  288. ");
  289. if ($zasob) return $zasob['ID'];
  290. $id = DB::getPDO()->insert('CRM_LISTA_ZASOBOW', [
  291. 'PARENT_ID' => $idZasobDatabase,
  292. 'DESC' => $this->_namespace,
  293. 'TYPE' => "TABELA",
  294. 'A_RECORD_CREATE_AUTHOR' => User::getLogin(),
  295. 'A_RECORD_CREATE_DATE' => "NOW()"
  296. ]);
  297. DB::getPDO()->insert('CRM_LISTA_ZASOBOW_HIST', [
  298. 'ID_USERS2' => $id,
  299. 'PARENT_ID' => $idZasobDatabase,
  300. 'DESC' => $this->_namespace,
  301. 'TYPE' => "TABELA",
  302. 'A_RECORD_CREATE_AUTHOR' => User::getLogin(),
  303. 'A_RECORD_CREATE_DATE' => "NOW()"
  304. ]);
  305. }
  306. return 0;
  307. }
  308. public function getAttributesFromZasoby() { return array(); }
  309. public function isEnumerationField($fieldName) { return false; }
  310. public function getEnumerations($fieldName) { return null; }
  311. public function getXsdFieldType($fieldName) {
  312. $xsdTypes = $this->getXsdTypes();
  313. if (empty($xsdTypes[$fieldName])) throw new Exception("Field '{$fieldName}' not exists");
  314. return $xsdTypes[$fieldName]['xsdType'];
  315. }
  316. public function isGeomField($fieldName) {
  317. if ('File' == $fieldName) return false;
  318. if ('AccessGroupRead' == $fieldName) return false;
  319. if ('AccessGroupWrite' == $fieldName) return false;
  320. if ('AccessOwner' == $fieldName) return false;
  321. // if ('NestedObjectTest' == $fieldName) return false;
  322. // return $this->parentAcl->isGeomField($fieldName);
  323. }
  324. public function generateSqlSelectFromRootTable($prefix = 't') {
  325. $sqlSelect = [];
  326. foreach ($this->_simpleSchema['root'] as $key => $field) {
  327. if ('@' == substr($key, 0, 1)) continue;// skip attr
  328. if (!empty($field['@ref'])) continue;// skip ref
  329. if (empty($field['@type'])) continue;// skip wrong simpleType structure - BUG
  330. if ('xsd:' != substr($field['@type'], 0, 4)) continue;// skip non xsd types - eg. p5:typeSpecialSimpleLink
  331. $sqlField = (!empty($field['@alias'])) ? $field['@alias'] : $key;
  332. $sqlSelect[] = "{$prefix}.`{$sqlField}` as `$key`";
  333. }
  334. return implode("\n, ", $sqlSelect);
  335. }
  336. public function getXsdFieldParam($fieldName, $paramKey) {
  337. if (empty($this->_simpleSchema['root'][$fieldName])) return null;
  338. if (empty($this->_simpleSchema['root'][$fieldName]['@@params'])) return null;
  339. if (empty($this->_simpleSchema['root'][$fieldName]['@@params'][$paramKey])) return null;
  340. return $this->_simpleSchema['root'][$fieldName]['@@params'][$paramKey];
  341. }
  342. public function addP5Types(&$item) {
  343. DBG::_('DBG_ACL', '>1', "\$item", $item, __CLASS__, __FUNCTION__, __LINE__);
  344. $sqlSelect = [];
  345. foreach ($this->_simpleSchema['root'] as $key => $field) {
  346. if ('@' == substr($key, 0, 1)) continue;// skip attr
  347. if (empty($field['@type'])) continue;// skip ref
  348. if ('p5:' != substr($field['@type'], 0, 3)) continue;// skip non p5 types
  349. $fieldName = $key;
  350. $item[$fieldName] = '';
  351. switch ($field['@type']) {
  352. case 'p5:typeSpecialSimpleLink': {
  353. // '@@params' => [// $acl->getXsdFieldParam($col, 'format');
  354. // 'format' => '<a href="https://www.youtube.com/watch?v={LINK}" target="_blank"></a>',
  355. // 'aliasMap' => [
  356. // 'LINK' => 'LINK'
  357. // ]
  358. // ]
  359. $link = $this->getXsdFieldParam($fieldName, 'format');
  360. // $.each(_fieldProps._tsSimpleLink.aliasMap, function(i, v) {
  361. // //console.log('simpleLink aliasMap columnName:', columnName, 'i:', i, 'v:', v, 'props['+v+']', props[v], 'val', val, 'typeof val', typeof val);
  362. // if (undefined !== row[v]) {
  363. // valLink = valLink.replace(new RegExp('\{' + i + '\}', 'g'), row[v]);
  364. // }
  365. // });
  366. foreach ($this->getXsdFieldParam($fieldName, 'aliasMap') as $itemFieldName => $alias) {
  367. DBG::_('DBG_ACL', '>1', "aliasMap({$itemFieldName} => {$alias})", $item[$itemFieldName], __CLASS__, __FUNCTION__, __LINE__);
  368. $link = str_replace("{{$alias}}", $item[$itemFieldName], $link);
  369. }
  370. $item[$fieldName] = $link;
  371. } break;
  372. }
  373. }
  374. }
  375. public function getParamCols($params = []) {
  376. $filterCols = [];
  377. $cols = V::get('cols', [], $params);// wfs:propertyName
  378. if (empty($cols)) {// set default filter cols
  379. foreach ($this->_simpleSchema['root'] as $fieldName => $field) {
  380. if ('@' == substr($fieldName, 0, 1)) continue;
  381. if ('unbounded' == V::get('maxOccurs', '', $field)) continue;// TODO:?: default load only single value fields (skip maxOccurs="unbounded")?
  382. if (!empty($field['@type'])) {
  383. if ('xsd:' === substr($field['@type'], 0, 4)) $filterCols[$fieldName] = true;
  384. else if ('p5:' === substr($field['@type'], 0, 3)) $filterCols[$fieldName] = true;
  385. } else if (!empty($field['@ref'])) {
  386. $filterCols[$fieldName] = [];
  387. } else throw new Exception("Schema error for field '{$fieldName}' ns({$this->_namespace})");
  388. }
  389. } else {
  390. foreach ($cols as $fieldXPath) {
  391. if ('@' == substr($fieldXPath, 0, 1)) {// attr
  392. if ('@instance' === $fieldXPath) {
  393. $filterCols['@instance'] = true;
  394. } else {
  395. throw new Exception("Not implemented attribute name '{$fieldXPath}' in '{$this->_namespace}'");
  396. }
  397. } else if (false === strpos($fieldXPath, '/')) {// not xpath - field name
  398. if (!array_key_exists($fieldXPath, $this->_simpleSchema['root'])) throw new Exception("Field name '{$fieldXPath}' not exists in '{$this->_namespace}'");
  399. $field = $this->_simpleSchema['root'][$fieldXPath];
  400. if (!empty($field['@type'])) {
  401. $filterCols[$fieldXPath] = true;
  402. } else if (!empty($field['@ref'])) {
  403. $filterCols[$fieldXPath] = [];
  404. }
  405. } else {// is xpath
  406. list($fieldName, $subXPath) = explode('/', $fieldXPath, 2);// split only by first '/'
  407. $filterCols[$fieldXPath][$fieldName] = $subXPath;
  408. }
  409. }
  410. }
  411. return $filterCols;
  412. }
  413. public function buildFromSqlRow($row, $params = []) {
  414. $object = [];
  415. $filterCols = $this->getParamCols($params);
  416. // $object['_raw'] = $row;
  417. if (in_array('@instance', $filterCols)) {
  418. $instanceTable = Core_AclHelper::getInstanceTable($this->getRootTableName());
  419. $sqlPkFieldName = $this->getSqlPrimaryKeyField();
  420. if (empty($row[$sqlPkFieldName])) throw new Exception("Missing primary key in ({$this->_namespace})");
  421. $sqlPk = DB::getPDO()->quote($row[$sqlPkFieldName], PDO::PARAM_STR);
  422. $object['@instance'] = DB::getPDO()->fetchValue("
  423. select i.INSTANCE_NAME
  424. from `{$instanceTable}` i
  425. where i.PRIMARY_KEY = {$sqlPk}
  426. ");// TODO: where i.`INSTANCE_TYPE` = 'instance' -- (i.`INSTANCE_TYPE` != 'derived')
  427. }
  428. foreach ($this->_simpleSchema['root'] as $fieldName => $field) {
  429. if ('@' == substr($fieldName, 0, 1)) continue;
  430. if (!array_key_exists($fieldName, $filterCols)) continue;// only filter cols
  431. if (!empty($field['@type'])) {
  432. // UI::alert('warning', "TODO: field({$fieldName}) type({$field['@type']})");
  433. if ('xsd:' === substr($field['@type'], 0, 4)) {
  434. $sqlFieldName = (!empty($field['@alias'])) ? $field['@alias'] : $fieldName;
  435. $object[$fieldName] = V::get($sqlFieldName, '', $row);
  436. } else if ('p5:enum' === $field['@type']) {
  437. $sqlFieldName = (!empty($field['@alias'])) ? $field['@alias'] : $fieldName;
  438. $object[$fieldName] = V::get($sqlFieldName, '', $row);
  439. } else if ('p5:' === substr($field['@type'], 0, 3)) {
  440. $object[$fieldName] = "TODO: generate value for type {$field['@type']} - field '{$fieldName}' ns({$this->_namespace})";// TODO: single field method like addP5Types
  441. } else throw new Exception("Not Implemented type for field '{$fieldName}' ns({$this->_namespace})");
  442. } else if (!empty($field['@ref'])) {
  443. // $object[$fieldName] = $this->fetchItemFieldRefs($primaryKey, $fieldName);
  444. $refTable = ACL::getRefTable($this->getNamespace(), $fieldName);
  445. $primaryKey = $row['ID'];// TODO: get primary key
  446. $sqlPk = DB::getPDO()->quote($primaryKey, PDO::PARAM_STR);
  447. $remoteIds = array_map(
  448. function ($row) {
  449. return $row['REMOTE_PRIMARY_KEY'];
  450. }
  451. , DB::getPDO()->fetchAll("
  452. select r.REMOTE_PRIMARY_KEY, r.REMOTE_TYPENAME
  453. from `{$refTable}` r
  454. where r.PRIMARY_KEY = {$sqlPk}
  455. and r.A_STATUS != 'DELETED'
  456. ")
  457. );
  458. $object[$fieldName] = (!empty($remoteIds))
  459. ? array_values(ACL::getAclByNamespace($field['@ref'])->getItems(['@primaryKey' => $remoteIds]))
  460. : [];
  461. } else throw new Exception("Schema error for field '{$fieldName}' ns({$this->_namespace})");
  462. }
  463. return $object;
  464. }
  465. public function getChildHistTable($childName) {
  466. if (empty($childName)) throw new Exception("Missing childName ('{$this->_namespace}')");
  467. if (!array_key_exists($childName, $this->_simpleSchema['root'])) throw new Exception("Field '{$childName}' not found in shema for '{$this->_namespace}'");
  468. if ('@' == substr($childName, 0, 1)) throw new Exception("Child hist table for attribute '{$childName}' not supported ('{$this->_namespace}')");
  469. // TODO: allow child for '@instance'
  470. $childSchema = $this->_simpleSchema['root'][$childName];
  471. return Core_AclHelper::getChildHistTable($this->getRootTableName(), $childName, $childSchema);
  472. }
  473. public function getEnumValues($childName) {
  474. $childSchema = $this->_simpleSchema['root'][$childName];
  475. DBG::log([
  476. 'msg' => 'dbg acl',
  477. 'sql field name' => $this->getSqlFieldName($childName),
  478. 'xsdType' => $this->getXsdFieldType($childName),
  479. 'childSchema' => $childSchema,
  480. ]);
  481. if ('p5:enum' == $this->getXsdFieldType($childName)) {
  482. if (!empty($this->_simpleSchema['root'][$childName]['@aliasFieldValues'])) {
  483. $aliasConf = $this->_simpleSchema['root'][$childName]['@aliasFieldValues'];
  484. $acl = Core_AclHelper::getAclByNamespace($aliasConf['namespace']);
  485. $sqlFldName = DB::getPDO()->quote($acl->getSqlFieldName($aliasConf['childName']), PDO::PARAM_STR);
  486. $sqlTblName = DB::getPDO()->quote($acl->getRootTableName(), PDO::PARAM_STR);
  487. $sqlDbName = DB::getPDO()->quote(DB::getPDO()->getDatabaseName(), PDO::PARAM_STR);
  488. } else {
  489. $sqlFldName = DB::getPDO()->quote($this->getSqlFieldName($childName), PDO::PARAM_STR);
  490. $sqlTblName = DB::getPDO()->quote($this->getRootTableName(), PDO::PARAM_STR);
  491. $sqlDbName = DB::getPDO()->quote(DB::getPDO()->getDatabaseName(), PDO::PARAM_STR);
  492. }
  493. $optionsEnum = DB::getPDO()->fetchValue("
  494. select COLUMN_TYPE
  495. from information_schema.COLUMNS c
  496. where c.COLUMN_NAME = {$sqlFldName}
  497. and c.TABLE_SCHEMA = {$sqlDbName}
  498. and c.TABLE_NAME = {$sqlTblName}
  499. ");
  500. if (!empty($optionsEnum)) {
  501. $optionsEnum = substr($optionsEnum, strlen('enum'));
  502. $optionsEnum = trim($optionsEnum, "()'");
  503. DBG::log(['msg' => '$optionsEnum', '$optionsEnum' => $optionsEnum]);
  504. return explode("','", $optionsEnum);
  505. }
  506. DBG::log(['msg' => 'Error empty options from enum field', '$optionsEnum' => $optionsEnum]);
  507. return null;
  508. }
  509. return null;
  510. }
  511. }