ACL.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. <?php
  2. Lib::loadClass('Core_AclHelper');
  3. Lib::loadClass('AntAclBase');
  4. class ACL {
  5. public static $REF_TABLE_VERSION = 1;
  6. /**
  7. * Ids List of Proces Init for given tabel (skip filters)
  8. */
  9. public static function getTableProcesInitIds($idTable) {
  10. $procesInitList = self::getTableProcesInitList($idTable);
  11. return array_keys($procesInitList);
  12. }
  13. /**
  14. * List of Proces Init for given table (skip filters)
  15. */
  16. public static function getTableProcesInitList($idTable) {
  17. $tableProcesInitList = array();
  18. $sqlIdProcesListSql = <<<SQL
  19. select tpv.`ID_PROCES`
  20. from `CRM_PROCES_idx_TABLE_TO_PROCES_VIEW` tpv
  21. where tpv.`ID_TABLE`='{$idTable}'
  22. SQL;
  23. $fetchTableProcesInitListSql = <<<SQL
  24. -- time ~0.07 -- no goto and return
  25. select p.`ID`, p.`DESC`
  26. from `CRM_PROCES` p
  27. where p.`ID` in(
  28. select i.`idx_PROCES_INIT_ID`
  29. from `CRM_PROCES_idx` i
  30. where i.`ID_PROCES` in({$sqlIdProcesListSql})
  31. )
  32. and p.`TYPE`='PROCES_INIT'
  33. order by p.`SORT_PRIO`
  34. SQL;
  35. /*
  36. SELECT p.`ID` , p.`DESC`
  37. FROM `CRM_PROCES` p
  38. WHERE p.`ID`
  39. IN (
  40. SELECT i.`idx_PROCES_INIT_ID`
  41. FROM `CRM_PROCES_idx` i
  42. WHERE i.`ID_PROCES`
  43. IN (
  44. SELECT tpv.`ID_PROCES`
  45. FROM `CRM_PROCES_idx_TABLE_TO_PROCES_VIEW` tpv
  46. WHERE tpv.`ID_TABLE` = '13051'
  47. )
  48. )
  49. AND p.`TYPE` = 'PROCES_INIT'
  50. order by p.`SORT_PRIO`
  51. */
  52. $fetchTableProcesInitListSql = <<<SQL
  53. -- time ~0.15s
  54. select p.`ID`, p.`DESC`
  55. from `CRM_PROCES` p
  56. where p.`ID` in(
  57. select i.`idx_PROCES_INIT_ID`
  58. from `CRM_PROCES_idx` i
  59. where i.`ID_PROCES` in({$sqlIdProcesListSql})
  60. union
  61. select ig.`idx_PROCES_INIT_ID`
  62. from `CRM_PROCES_idx` i
  63. join `CRM_PROCES_idx` ig on(ig.`ID_PROCES`=i.`idx_PROCES_WITH_GROUPS_ID`)
  64. where i.`ID_PROCES` in({$sqlIdProcesListSql})
  65. )
  66. and p.`TYPE`='PROCES_INIT'
  67. order by p.`SORT_PRIO`
  68. SQL;
  69. $fetchTableProcesInitListSql = <<<SQL
  70. -- time ~0.14
  71. select p.`ID`, p.`DESC`
  72. from `CRM_PROCES` p
  73. where p.`ID` in(
  74. select i.`idx_PROCES_INIT_ID`
  75. from `CRM_PROCES_idx` i
  76. where i.`ID_PROCES` in({$sqlIdProcesListSql})
  77. or i.`ID_PROCES` in(
  78. select ig.`idx_PROCES_WITH_GROUPS_ID`
  79. from `CRM_PROCES_idx` ig
  80. where ig.`ID_PROCES` in({$sqlIdProcesListSql})
  81. )
  82. )
  83. and p.`TYPE`='PROCES_INIT'
  84. order by p.`SORT_PRIO`
  85. SQL;
  86. //echo'<pre>$fetchTableProcesInitListSql('.$idTable.') ';print_r($fetchTableProcesInitListSql);echo'</pre>';
  87. $tableProcesInitList = array();
  88. $db = DB::getDB();
  89. $res = $db->query($fetchTableProcesInitListSql);
  90. while ($r = $db->fetch($res)) {
  91. $tableProcesInitList[$r->ID] = $r->DESC;
  92. }
  93. return $tableProcesInitList;
  94. }
  95. public static function getProcesInitMapTreeOnlyIds($ids) {
  96. $mapTree = array();
  97. $map = self::getProcesInitMapOnlyIds($ids);
  98. foreach ($map as $r) {
  99. if ('PROCES_INIT' == $r->TYPE) {
  100. $mapTree[$r->ID_PROCES] = array();
  101. }
  102. }
  103. foreach ($map as $r) {
  104. if ('GOTO_AND_RETURN' == $r->TYPE) {
  105. $mapTree[$r->idx_MAIN_PROCES_INIT_ID][$r->ID_PROCES] = array();
  106. }
  107. }
  108. foreach ($map as $r) {
  109. if ('GOTO_AND_RETURN_LVL2' == $r->TYPE) {
  110. $mapTree[$r->idx_MAIN_PROCES_INIT_ID][$r->idx_GOTO_LVL2_INIT_ID][$r->ID_PROCES] = true;
  111. }
  112. }
  113. return $mapTree;
  114. }
  115. public static function getProcesInitMapOnlyIds($ids) {
  116. $map = array();
  117. $sqlIds = V::filter($ids, array('V', 'filterPositiveInteger'));
  118. $sqlIds = implode(',', $sqlIds);
  119. if (empty($sqlIds)) return $map;
  120. $sql = <<<SQL
  121. select i.`ID_PROCES`
  122. , i.`PARENT_ID`
  123. , i.`TYPE`
  124. , i.`idx_PROCES_INIT_ID`
  125. , i.`idx_MAIN_PROCES_INIT_ID`
  126. , i.`idx_PROCES_WITH_GROUPS_ID`
  127. , IF(i.`TYPE`='GOTO_AND_RETURN_LVL2'
  128. , (select ig.`idx_PROCES_INIT_ID`
  129. from `CRM_PROCES_idx` ig
  130. where ig.`ID_PROCES`=i.`PARENT_ID`
  131. limit 1)
  132. , 0
  133. ) as idx_GOTO_LVL2_INIT_ID
  134. from `CRM_PROCES_idx` i
  135. where i.`ID_PROCES` in({$sqlIds})
  136. and i.`idx_MAIN_PROCES_INIT_ID` in({$sqlIds})
  137. SQL;
  138. DBG::_('DBG_MAP', '1', "MAP SQL", $sql, __CLASS__, __FUNCTION__, __LINE__);
  139. $db = DB::getDB();
  140. $res = $db->query($sql);
  141. while ($r = $db->fetch($res)) {
  142. $map[] = $r;
  143. }
  144. //DBG::table("MAP", $map, __CLASS__, __FUNCTION__, __LINE__);
  145. return $map;
  146. }
  147. public static function canGroupViewProces($idGroup, $idProcesInit) {
  148. $isAllowed = false;
  149. $idProcesInit = (int)$idProcesInit;
  150. if (!$idProcesInit) return false;
  151. $checkProcesAccessSql = <<<SQL
  152. select count(*) as cnt
  153. from `CRM_PROCES_idx_GROUP_to_INIT_VIEW` giv
  154. where giv.`ID_GROUP` = '{$idGroup}'
  155. and giv.`ID_PROCES_INIT` = '{$idProcesInit}'
  156. SQL;
  157. $db = DB::getDB();
  158. $res = $db->query($checkProcesAccessSql);
  159. if ($r = $db->fetch($res)) {
  160. if ($r->cnt > 0) {
  161. $isAllowed = true;
  162. }
  163. }
  164. return $isAllowed;
  165. }
  166. public static function getStorageByNamespace($namespace, $forceTblAclInit = false) {
  167. Lib::loadClass('Core_AclHelper');
  168. Lib::loadClass('SchemaFactory');
  169. $ns = Core_AclHelper::parseNamespaceUrl($namespace);
  170. DBG::log($ns, 'array', "parseNamespaceUrl({$namespace})");
  171. if ('default_db' == $ns['prefix']) {
  172. $acl = User::getAcl()->getObjectAcl($ns['prefix'], $ns['name']);
  173. } else if ('objects' == $ns['prefix']) {
  174. $acl = SchemaFactory::loadDefaultObject($ns['name']);
  175. } else if ('default_objects' == $ns['prefix']) {
  176. $acl = SchemaFactory::loadDefaultObject($ns['name']);
  177. } else if ('default_db__x3A__' == substr($ns['prefix'], 0, 17)) {
  178. $rootTableName = strtolower(substr($ns['prefix'], 17));
  179. $acl = SchemaFactory::loadTableObject($rootTableName, $ns['name']);
  180. } else {
  181. throw new HttpException("Not Implemented", 501);
  182. }
  183. $acl->init($forceTblAclInit);
  184. return $acl;
  185. }
  186. public static function getAclByNamespace($namespace, $forceTblAclInit = false) {
  187. return Core_AclHelper::getAclByNamespace($namespace, $forceTblAclInit);
  188. }
  189. public static function getAclByTypeName($typeName, $forceTblAclInit = false) {
  190. return Core_AclHelper::getAclByNamespace(str_replace(':', '/', $typeName), $forceTblAclInit);
  191. }
  192. public static function getNamespaceFromId($idZasob) {
  193. $sqlIdZasob = DB::getPDO()->quote($idZasob, PDO::PARAM_INT);
  194. $zasob = DB::getPDO()->fetchFirst("
  195. select z.ID, z.DESC, z.PARENT_ID
  196. from CRM_LISTA_ZASOBOW z
  197. where z.ID = {$sqlIdZasob}
  198. and z.`TYPE` = 'TABELA'
  199. and z.A_STATUS != 'DELETED'
  200. ");
  201. if (!$zasob) throw new Exception("Object not exists '{$idZasob}'");
  202. if ($zasob['PARENT_ID'] != DB::getPDO()->getZasobId()) {
  203. throw new Exception("TODO: getNamespaceFromId for remote database");
  204. }
  205. return ('default_db/' === substr($zasob['DESC'], 0, strlen('default_db/')))
  206. ? $zasob['DESC']
  207. : "default_db/{$zasob['DESC']}"
  208. ;
  209. }
  210. public static function parseNamespaceUrl($namespace) {// returns assoc array: [ 'name', 'url', 'prefix', 'sourceName' ]
  211. return Core_AclHelper::parseNamespaceUrl($namespace);
  212. }
  213. public static function getRefTable($rootObjectNamespace, $childName) { // CRM_REF_CONFIG
  214. static $cacheRefTables = array();
  215. DBG::log("DBG get ref table ({$rootObjectNamespace}, {$childName}) ...");
  216. $cacheKey = "{$rootObjectNamespace}/{$childName}";
  217. if (array_key_exists($cacheKey, $cacheRefTables)) return $cacheRefTables[$cacheKey];
  218. $rootAcl = self::getAclByNamespace($rootObjectNamespace);
  219. $childXsdType = $rootAcl->getXsdFieldType($childName);
  220. list($typePrefix, $childNamespace) = explode(':', $childXsdType, 2);
  221. DBG::log(['$childXsdType' => $childXsdType, '$typePrefix' => $typePrefix, '$childNamespace' => $childNamespace], 'array', "DBG get ref table ...");
  222. switch ($typePrefix) {
  223. case 'ref_uri': $childAcl = self::getAclByNamespace($childNamespace); break;
  224. case 'ref': $childAcl = self::getAclByTypeName($childNamespace); break;
  225. default: throw new Exception("Expected ref type for field '{$childName}' in object '{$rootObjectNamespace}'");
  226. }
  227. $refInfo = self::getRefConfig($rootObjectNamespace, $childName, $childNamespace);
  228. if ('view' === $refInfo['SOURCE']) {
  229. $refTableName = "CRM__#REF_TABLE__{$refInfo['ID']}_VIEW"; // view created by ACL::generateRefSelectSqlByFlatRelationCache
  230. } else {
  231. $refTableName = "CRM__#REF_TABLE__{$refInfo['ID']}";
  232. if ('WAITING' == $refInfo['A_STATUS']) {
  233. DB::getPDO()->execSql("
  234. CREATE TABLE IF NOT EXISTS `{$refTableName}` (
  235. `PRIMARY_KEY` int(11) NOT NULL
  236. , `REMOTE_PRIMARY_KEY` int(11) NOT NULL
  237. , `REMOTE_TYPENAME` varchar(255) NOT NULL DEFAULT ''
  238. , `A_STATUS` enum('WAITING', 'NORMAL', 'DELETED') NOT NULL DEFAULT 'WAITING'
  239. , `TRANSACTION_ID` int(11) NOT NULL
  240. , `A_LAST_ACTION_DATE` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
  241. , KEY `PRIMARY_KEY` (`PRIMARY_KEY`)
  242. , KEY `REMOTE_PRIMARY_KEY` (`REMOTE_PRIMARY_KEY`)
  243. ) ENGINE=MyISAM DEFAULT CHARSET=latin2 COMMENT='{$rootObjectNamespace} #REF $childName ({$childNamespace})';
  244. ");
  245. $refInfo['A_STATUS'] = "NORMAL";
  246. $refInfo['VERSION'] = self::$REF_TABLE_VERSION;
  247. $affected = DB::getPDO()->update("CRM_REF_CONFIG", 'ID', $refInfo['ID'], [
  248. 'A_STATUS' => $refInfo['A_STATUS'],
  249. 'VERSION' => $refInfo['VERSION']
  250. ]);
  251. }
  252. }
  253. if ($refInfo['VERSION'] < self::$REF_TABLE_VERSION) throw new Exception("TODO: ref table {$refInfo['ID']} require upgrade - field '{$childName}' in object '{$rootObjectNamespace}'");
  254. $cacheRefTables[$cacheKey] = $refTableName;
  255. return $refTableName;
  256. }
  257. public static function getRefSource($rootObjectNamespace, $childName) { // CRM_REF_CONFIG
  258. $refInfo = self::getRefConfig($rootObjectNamespace, $childName);
  259. return V::get('SOURCE', 'table', $refInfo);
  260. }
  261. public static function generateRefSelectSqlByFlatRelationCache($rootObjectNamespace, $childName) { // CRM_REF_CONFIG
  262. $appInfo = DB::getPDO()->fetchValue("
  263. select f.appInfo
  264. from `CRM_#CACHE_ACL_OBJECT_FIELD` f
  265. where f.objectNamespace = '{$rootObjectNamespace}'
  266. and f.fieldNamespace = '{$childName}'
  267. ");
  268. if (!$appInfo) throw new Exception("Missing app:info for field '{$rootObjectNamespace}/{$childName}'");
  269. $appInfo = @json_decode($appInfo, $assoc = true);
  270. if (empty($appInfo)) throw new Exception("Empty app:info for field '{$rootObjectNamespace}/{$childName}'");
  271. DBG::log(['$appInfo'=>$appInfo, '$rootObjectNamespace'=>$rootObjectNamespace, '$childName'=>$childName], 'array', "\$appInfo");
  272. $rootAcl = self::getAclByNamespace($rootObjectNamespace);
  273. $childXsdType = $rootAcl->getXsdFieldType($childName);
  274. list($typePrefix, $childNamespace) = explode(':', $childXsdType, 2);
  275. switch ($typePrefix) {
  276. case 'ref_uri': $childAcl = self::getAclByNamespace($childNamespace); break;
  277. case 'ref': $childAcl = self::getAclByTypeName($childNamespace); break;
  278. default: throw new Exception("Expected ref type for field '{$childName}' in object '{$rootObjectNamespace}'");
  279. }
  280. $lastActionDateField = "NULL"; // , IF(l.A_RECORD_UPDATE_DATE > r.A_RECORD_UPDATE_DATE, l.A_RECORD_UPDATE_DATE, r.A_RECORD_UPDATE_DATE) as A_LAST_ACTION_DATE
  281. $rootPrimaryKeyField = $rootAcl->getPrimaryKeyField();
  282. $childPrimaryKeyField = $childAcl->getPrimaryKeyField();
  283. $rootTableName = $rootAcl->getRootTableName();
  284. $childTableName = $childAcl->getRootTableName();
  285. // '$appInfo' => [
  286. // 'flat_relation_cache' => [
  287. // 'source' => [
  288. // '@name' => 'ID',
  289. // '@xpath' => 'default_db__x3A__CRM_WSKAZNIK:CRM_WSKAZNIK/ID_PROCES',
  290. // ),
  291. // ),
  292. // ),
  293. // '$rootObjectNamespace' => 'default_db/CRM_PROCES/PROCES',
  294. // '$childName' => 'default_db__x3A__CRM_WSKAZNIK:CRM_WSKAZNIK',
  295. // '$appInfo' => [
  296. // 'flat_relation_cache' => [
  297. // 'source' => [
  298. // '@name' => 'ID',
  299. // '@xpath' => 'default_db__x3A__CRM_PROCES:PROCES/PARENT_ID',
  300. // ),
  301. // ),
  302. // ),
  303. // '$rootObjectNamespace' => 'default_db/CRM_PROCES/PROCES',
  304. // '$childName' => 'default_db__x3A__CRM_PROCES:PROCES',
  305. $appInfoRootFieldName = null;
  306. $appInfoChildFieldName = null;
  307. {
  308. if (empty($appInfo['flat_relation_cache']['source']['@name'])) throw new Exception("Missing flat_relation_cache/source/@name");
  309. if (empty($appInfo['flat_relation_cache']['source']['@xpath'])) throw new Exception("Missing flat_relation_cache/source/@xpath");
  310. $appInfoName = $appInfo['flat_relation_cache']['source']['@name'];
  311. $appInfoXpath = $appInfo['flat_relation_cache']['source']['@xpath'];
  312. // $rootNs = $rootAcl->getNamespace()
  313. if ("{$childName}/" === substr($appInfoXpath, 0, strlen("{$childName}/"))) {
  314. $appInfoRootFieldName = substr($appInfoXpath, strlen("{$childName}/"));
  315. $appInfoChildFieldName = $appInfoName;
  316. } else {
  317. throw new Exception("TODO parse flat_relation_cache");
  318. }
  319. }
  320. if (!$appInfoRootFieldName || !$appInfoChildFieldName) throw new Exception("Error Processing flat_relation_cache");
  321. $sqlWhereFromRestrictions = [];
  322. DBG::log(['root'=>$rootAcl->getFields(), 'child'=>$childAcl->getFields()], 'array', "rootAcl and childAcl fields - xsdRestrictions");
  323. if ($rootAcl instanceof AntAclBase && $childAcl instanceof AntAclBase) {
  324. $rootLocalFieldsWithRestrictions = array_filter($rootAcl->getFields(), function ($field) {
  325. if (!$field['isLocal']) return false;
  326. if (empty($field['xsdRestrictions'])) return false;
  327. if ('[]' == $field['xsdRestrictions']) return false;
  328. return true;
  329. });
  330. $childLocalFieldsWithRestrictions = array_filter($childAcl->getFields(), function ($field) {
  331. if (!$field['isLocal']) return false;
  332. if (empty($field['xsdRestrictions'])) return false;
  333. if ('[]' == $field['xsdRestrictions']) return false;
  334. return true;
  335. });
  336. DBG::log(['root'=>$rootLocalFieldsWithRestrictions, 'child'=>$childLocalFieldsWithRestrictions], 'array', "root and child fields with xsdRestrictions");
  337. if (!empty($rootLocalFieldsWithRestrictions)) {
  338. $sqlTablePrefix = 'root';
  339. $sqlWhereFromRestrictions = array_reduce(
  340. array_map(function ($field) use ($sqlTablePrefix) {
  341. $sqlRestrictions = [];
  342. // 'xsdRestrictions' => '{"enumeration":{"PROCES":"PROCES"}}',
  343. $restrictions = @json_decode($field['xsdRestrictions'], $assoc = true);
  344. if (!empty($restrictions)) {
  345. if (!empty($restrictions['enumeration'])) {
  346. $sqlRestrictions[] = "{$sqlTablePrefix}.`{$field['fieldNamespace']}` in (" . implode(",", array_map([DB::getPDO(), 'quote'], array_keys($restrictions['enumeration']))) . ")";
  347. }
  348. }
  349. return $sqlRestrictions;
  350. }, $rootLocalFieldsWithRestrictions),
  351. function ($ret, $cur) {
  352. return array_merge($ret, array_filter($cur, ['V', 'filterNotEmpty']));
  353. },
  354. $sqlWhereFromRestrictions
  355. );
  356. }
  357. if (!empty($childLocalFieldsWithRestrictions)) {
  358. $sqlTablePrefix = 'child';
  359. $sqlWhereFromRestrictions = array_reduce(
  360. array_map(function ($field) use ($sqlTablePrefix) {
  361. $sqlRestrictions = [];
  362. // 'xsdRestrictions' => '{"enumeration":{"PROCES":"PROCES"}}',
  363. $restrictions = @json_decode($field['xsdRestrictions'], $assoc = true);
  364. if (!empty($restrictions)) {
  365. if (!empty($restrictions['enumeration'])) {
  366. $sqlRestrictions[] = "{$sqlTablePrefix}.`{$field['fieldNamespace']}` in (" . implode(",", array_map([DB::getPDO(), 'quote'], array_keys($restrictions['enumeration']))) . ")";
  367. }
  368. }
  369. return $sqlRestrictions;
  370. }, $childLocalFieldsWithRestrictions),
  371. function ($ret, $cur) {
  372. return array_merge($ret, array_filter($cur, ['V', 'filterNotEmpty']));
  373. },
  374. $sqlWhereFromRestrictions
  375. );
  376. }
  377. }
  378. $sqlWhereFromRestrictions = (!empty($sqlWhereFromRestrictions)) ? implode(" and ", $sqlWhereFromRestrictions) : "1=1";
  379. $sql = "
  380. select root.{$rootPrimaryKeyField} as PRIMARY_KEY
  381. , child.{$childPrimaryKeyField} as REMOTE_PRIMARY_KEY
  382. , '' as REMOTE_TYPENAME
  383. , 'WAITING' as A_STATUS
  384. , 0 as TRANSACTION_ID
  385. , {$lastActionDateField} as A_LAST_ACTION_DATE
  386. from `{$rootTableName}` root
  387. join `{$childTableName}` child on(child.{$appInfoRootFieldName} = root.{$appInfoChildFieldName})
  388. where {$sqlWhereFromRestrictions}
  389. ";
  390. DBG::log($sql, 'sql', "generateRefSelectSqlByFlatRelationCache");
  391. return $sql;
  392. }
  393. public static function setRefSource($rootObjectNamespace, $childName, $source, $viewSelectSql = null) { // CRM_REF_CONFIG
  394. if (!in_array($source, ['view', 'table'])) throw new Exception("Wrong param source - expected 'table' or 'view'");
  395. if ('view' === $source && !$viewSelectSql) throw new Exception("Missing create view sql");
  396. $refInfo = self::getRefConfig($rootObjectNamespace, $childName);
  397. if ($source != $refInfo['SOURCE']) {
  398. if ('view' === $source) {
  399. $refTableName = "CRM__#REF_TABLE__{$refInfo['ID']}_VIEW";
  400. DB::getPDO()->execSql(" CREATE OR REPLACE DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `{$refTableName}` AS {$viewSelectSql} ");
  401. }
  402. $affected = DB::getPDO()->update("CRM_REF_CONFIG", 'ID', $refInfo['ID'], [
  403. 'SOURCE' => $source,
  404. ]);
  405. }
  406. }
  407. public static function getRefConfig($rootObjectNamespace, $childName, $childNamespace = null) { // CRM_REF_CONFIG
  408. if (!$childNamespace) {
  409. $rootAcl = self::getAclByNamespace($rootObjectNamespace);
  410. $childXsdType = $rootAcl->getXsdFieldType($childName);
  411. list($typePrefix, $childNamespace) = explode(':', $childXsdType, 2);
  412. DBG::log(['$childXsdType' => $childXsdType, '$typePrefix' => $typePrefix, '$childNamespace' => $childNamespace], 'array', "DBG get ref table ...");
  413. switch ($typePrefix) {
  414. case 'ref_uri': $childAcl = self::getAclByNamespace($childNamespace); break;
  415. case 'ref': $childAcl = self::getAclByTypeName($childNamespace); break;
  416. default: throw new Exception("Expected ref type for field '{$childName}' in object '{$rootObjectNamespace}'");
  417. }
  418. }
  419. $refInfo = [];// define $refInfo = [ ID, A_STATUS, VERSION ]
  420. try {// check that ref config table exists
  421. $sqlRootTableNs = DB::getPDO()->quote($rootObjectNamespace, PDO::PARAM_STR);
  422. $sqlChildName = DB::getPDO()->quote($childName, PDO::PARAM_STR);
  423. $sqlChildNamespace = DB::getPDO()->quote($childNamespace, PDO::PARAM_STR);
  424. $refInfo = DB::getPDO()->fetchFirst("
  425. select c.ID, c.A_STATUS, c.VERSION, c.SOURCE
  426. from `CRM_REF_CONFIG` c
  427. where c.ROOT_OBJECT_NS = {$sqlRootTableNs}
  428. and c.CHILD_NAME = {$sqlChildName}
  429. and c.CHILD_NS = {$sqlChildNamespace}
  430. ");
  431. } catch (Exception $e) {
  432. DB::getPDO()->execSql("
  433. CREATE TABLE IF NOT EXISTS `CRM_REF_CONFIG` (
  434. `ID` INT NOT NULL AUTO_INCREMENT
  435. , `ROOT_OBJECT_NS` VARCHAR(255) NOT NULL
  436. , `CHILD_NAME` VARCHAR(255) NOT NULL
  437. , `CHILD_NS` VARCHAR(255) NOT NULL
  438. , `A_STATUS` enum('WAITING', 'NORMAL', 'DELETED') NOT NULL DEFAULT 'WAITING'
  439. , `VERSION` int(11) NOT NULL DEFAULT 0
  440. , `A_LAST_ACTION_DATE` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
  441. , PRIMARY KEY (`ID`)
  442. ) ENGINE = MyISAM DEFAULT CHARSET=latin2;
  443. ");
  444. try {
  445. DB::getPDO()->execSql(" ALTER TABLE `CRM_REF_CONFIG` ADD `SOURCE` enum('table', 'view') not null default 'table' ");
  446. } catch (Exception $e) {
  447. DBG::log($e);
  448. }
  449. }
  450. if (empty($refInfo)) {
  451. $refInfo = [ 'ID' => 0, 'A_STATUS' => 'WAITING', 'VERSION' => 0, 'SOURCE' => 'table' ];
  452. $refInfo['ID'] = DB::getPDO()->insert("CRM_REF_CONFIG", [
  453. 'ROOT_OBJECT_NS' => $rootObjectNamespace,
  454. 'CHILD_NAME' => $childName,
  455. 'CHILD_NS' => $childNamespace
  456. ]);
  457. }
  458. if (!$refInfo['ID']) throw new Exception("Ref table not found in ref config table for field '{$childName}' in object '{$rootObjectNamespace}'");
  459. return $refInfo;
  460. }
  461. public static function getInstanceId($namespace) { // CRM_INSTANCE_CONFIG
  462. $conf = self::getInstanceConfig($namespace);
  463. return $conf['id'];
  464. }
  465. public static function getInstanceConfig($namespace) { // CRM_INSTANCE_CONFIG
  466. try {
  467. $conf = self::fetchInstanceConfig($namespace);
  468. } catch (Exception $e) {
  469. DB::getPDO()->execSql("
  470. create table if not exists `CRM_INSTANCE_CONFIG` (
  471. `id` int(11) not null AUTO_INCREMENT,
  472. `namespace` varchar(255) NOT NULL DEFAULT '',
  473. `rootNamespace` varchar(255) NOT NULL DEFAULT '',
  474. `idInstanceBase` int(11) NOT NULL DEFAULT 0,
  475. `_createdAt` datetime NOT NULL,
  476. UNIQUE KEY `namespace` (`namespace`),
  477. KEY `rootNamespace` (`rootNamespace`),
  478. PRIMARY KEY (`id`)
  479. ) ENGINE=MyISAM DEFAULT CHARSET=latin2
  480. ");
  481. // TODO:?: `_tableInstalled` tinyint(1) not null default 0,
  482. $conf = self::fetchInstanceConfig($namespace);
  483. }
  484. if (!$conf) {
  485. $id = DB::getPDO()->insert("CRM_INSTANCE_CONFIG", [
  486. 'namespace' => $namespace,
  487. 'rootNamespace' => self::getRootNamespace($namespace),
  488. '_createdAt' => 'NOW()',
  489. ]);
  490. $conf = self::fetchInstanceConfig($namespace);
  491. }
  492. if (!$conf) throw new Exception("Instance not found in config table '{$namespace}'");
  493. return $conf;
  494. }
  495. public static function fetchInstanceConfig($namespace) {
  496. return DB::getPDO()->fetchFirst("
  497. select c.*
  498. from `CRM_INSTANCE_CONFIG` c
  499. where c.namespace = '{$namespace}'
  500. ");
  501. }
  502. public static function getRootNamespace($namespace) { // TODO: works only for relative urls! - mv to Acl->getRootNamespace
  503. Lib::loadClass('SchemaFactory');
  504. try {
  505. $objectItem = SchemaFactory::loadDefaultObject('SystemObject')->getItem($namespace);
  506. } catch (Exception $e) {
  507. throw new Exception("Object not installed '{$namespace}'");
  508. }
  509. if (!$objectItem['isStructInstalled']) throw new Exception("Object structure not installed '{$namespace}'");
  510. if ($objectItem['idDatabase'] != DB::getPDO()->getZasobId()) throw new Exception("Only default_db supported"); // TODO: support more Sources
  511. return "default_db/{$objectItem['_rootTableName']}";
  512. }
  513. public static function getNamespaceSiblings($namespace) {
  514. return array_map(function ($row) {
  515. return $row['namespace'];
  516. }, DB::getPDO()->fetchAll("
  517. select s.namespace
  518. from CRM_INSTANCE_CONFIG c
  519. join CRM_INSTANCE_CONFIG s on ( s.rootNamespace = c.rootNamespace and s.namespace != c.rootNamespace )
  520. where c.namespace = :namespace
  521. ", [
  522. 'namespace' => $namespace
  523. ]));
  524. }
  525. public static function getFeatureNamespaces($namespace, $pk) {
  526. $instanceTable = self::getInstanceTable($namespace);
  527. return array_map(function ($row) {
  528. return $row['namespace'];
  529. }, DB::getPDO()->fetchAll("
  530. select c.namespace
  531. from `{$instanceTable}` i
  532. join `CRM_INSTANCE_CONFIG` c on ( c.id = i.idInstance )
  533. where i.pk = :pk
  534. ", [
  535. 'pk' => $pk,
  536. ]));
  537. }
  538. public static function getInstanceTable($namespace) {
  539. $conf = self::getInstanceConfig($namespace);
  540. if (!empty($conf['idInstanceBase'])) return "CRM__#INSTANCE_TABLE__{$conf['idInstanceBase']}";
  541. $rootNs = $conf['rootNamespace'];
  542. $rootConf = self::getInstanceConfig($rootNs);
  543. $instanceTableName = "CRM__#INSTANCE_TABLE__{$rootConf['id']}";
  544. if (!empty($rootConf['idInstance'])) {
  545. $affected = DB::getPDO()->update("CRM_INSTANCE_CONFIG", 'rootNamespace', $rootNs, [
  546. 'idInstanceBase' => $rootConf['id']
  547. ]);
  548. return $instanceTableName;
  549. }
  550. // TODO: fetch primaryKeyType - TODO: store primaryKey and primaryKeyType in SystemObject item
  551. $pkType = 'int';
  552. DB::getPDO()->exec("
  553. CREATE TABLE IF NOT EXISTS `{$instanceTableName}` (
  554. `pk` int(11) NOT NULL COMMENT 'primary key'
  555. , `idInstance` int(11) NOT NULL
  556. , `_createdAt` datetime NOT NULL
  557. , KEY `pk` (`pk`)
  558. , KEY `idInstance` (`idInstance`)
  559. ) ENGINE=MyISAM DEFAULT CHARSET=latin2 COMMENT='{$rootNs} #INSTANCE';
  560. ");
  561. $affected = DB::getPDO()->update("CRM_INSTANCE_CONFIG", 'rootNamespace', $rootNs, [
  562. 'idInstanceBase' => $rootConf['id']
  563. ]);
  564. return $instanceTableName;
  565. }
  566. // @params $from - ( ACL | tableName | namespace | etc... - only ACL)
  567. public static function query($from, $prefix = 't') {
  568. Lib::loadClass('AclQueryBuilder');
  569. $query = new AclQueryBuilder();
  570. $query->from($from, $prefix);
  571. return $query;
  572. }
  573. /**
  574. * @param mixed $object - Core_AclBase or string - namespace
  575. * @return Core_AclFields
  576. */
  577. public static function getObjectFields($object) {
  578. // TODO: try to get structure from `CRM_#CACHE_ACL_OBJECT_FIELD`
  579. // if ($object is instance Core_AclBase) {
  580. // if ($object->isStructInstalled) then get structure from `CRM_#CACHE_ACL_OBJECT_FIELD` and put into Core_AclFields
  581. // else get from $object->getFields() and put into Core_AclFields
  582. }
  583. public static function canUserReadObject($idUser, $aclOrIdZasob) {
  584. throw new Exception("TODO: canUserReadObjec({$idUser}, {$aclOrIdZasob})");
  585. }
  586. public static function canUserCreateObject($idUser, $aclOrIdZasob) {
  587. throw new Exception("TODO: canUserCreateObjec({$idUser}, {$aclOrIdZasob})");
  588. }
  589. public static function canUserWriteObject($idUser, $aclOrIdZasob) {
  590. throw new Exception("TODO: canUserWriteObjec({$idUser}, {$aclOrIdZasob})");
  591. }
  592. public static function canUserReadObjectField($idUser, $aclOrIdZasob, $fieldNameOrXPath) {
  593. throw new Exception("TODO: canUserReadObjectFiel({$idUser}, {$aclOrIdZasob}, {$fieldNameOrXPath})");
  594. }
  595. public static function canUserCreateObjectField($idUser, $aclOrIdZasob, $fieldNameOrXPath) {
  596. throw new Exception("TODO: canUserCreateObjectFiel({$idUser}, {$aclOrIdZasob}, {$fieldNameOrXPath})");
  597. }
  598. public static function canUserWriteObjectField($idUser, $aclOrIdZasob, $fieldNameOrXPath) {
  599. throw new Exception("TODO: canUserWriteObjectFiel({$idUser}, {$aclOrIdZasob}, {$fieldNameOrXPath})");
  600. }
  601. // TODO: replace below:
  602. // AclBase->canCreateField
  603. // AclBase->canReadField
  604. // AclBase->canReadObjectField
  605. // AclBase->canWriteField
  606. // AclBase->canWriteObjectField
  607. // AclBase->canWriteRecord
  608. // AclBase->canReadRecord
  609. }