AclReinstall.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. <?php
  2. Lib::loadClass('RouteBase');
  3. Lib::loadClass('Router');
  4. Lib::loadClass('Response');
  5. Lib::loadClass('UI');
  6. Lib::loadClass('SchemaFactory');
  7. class Route_Storage_AclReinstall extends RouteBase {
  8. public function handleAuth() {
  9. if (!User::logged()) {
  10. User::authByRequest();
  11. }
  12. }
  13. public function defaultAction() {
  14. UI::gora();
  15. UI::startContainer();
  16. try {
  17. $namespace = V::get('namespace', '', $_GET);
  18. if (empty($namespace)) throw new Exception("Missing param namespace");
  19. echo UI::h('h3', [], $namespace);
  20. echo UI::h('p', [], [
  21. UI::h('a', [
  22. 'href' => Router::getRoute('Storage_AclStruct')->getLink('', [ 'namespace' => $namespace ]),
  23. 'class' => "btn btn-md btn-link",
  24. ], "<i class=\"glyphicon glyphicon-arrow-left\"></i> Wróć do struktury"),
  25. " | ",
  26. UI::h('a', [
  27. 'href' => $this->getLink('viewXsdSource', [ 'namespace' => $namespace ]),
  28. 'class' => "btn btn-md btn-link",
  29. 'target' => "_blank",
  30. ], "Otwórz plik xsd (źródłowy)"),
  31. ]);
  32. if ('reinstall' == V::get('_postTask', '', $_POST)) {
  33. Lib::loadClass('Schema_SystemObjectFieldStorageAcl');
  34. $objFieldAcl = new Schema_SystemObjectFieldStorageAcl();
  35. $objFieldAcl->updateCache($namespace);
  36. try {
  37. $dbgInfo = [
  38. 'idInstance' => ACL::getInstanceId($namespace),
  39. 'rootInstance' => ACL::getRootNamespace($namespace),
  40. 'conf' => ACL::fetchInstanceConfig($namespace),
  41. // 'table' => ACL::getInstanceTable($namespace), // Object structure not installed 'default_db/{tableName}'
  42. ];
  43. DBG::nicePrint($dbgInfo, "dbg");
  44. } catch (Exception $e) {
  45. UI::alert('warning', $e->getMessage());
  46. }
  47. $item = SchemaFactory::loadDefaultObject('SystemObject')->getItem($namespace, [ 'propertyName' => '*,field' ]);
  48. $childRefList = ACL::getChildRefFullList($namespace);
  49. DBG::nicePrint($childRefList, '$childRefList');
  50. DBG::nicePrint($item, '$item');
  51. $activeFields = array_filter($item['field'], function ($field) {
  52. return ($field['isActive'] > 0);
  53. });
  54. $fieldNsList = array_map(function ($field) {
  55. return $field['fieldNamespace'];
  56. }, $activeFields);
  57. DBG::nicePrint($fieldNsList, '$fieldNsList');
  58. if ('AntAcl' === $item['_type']) {
  59. foreach ($childRefList as $childRef) { // [ namespace, A_STATUS ]
  60. if ($childRef['A_STATUS'] !== 'DELETED' && !in_array($childRef['namespace'], $fieldNsList)) {
  61. UI::alert('danger', "remove ref config for '{$childRef['namespace']}' ...");
  62. if (!$childRef['ID']) throw new Exception("Missing ref config ID");
  63. DB::getPDO()->update('CRM_REF_CONFIG', 'ID', $childRef['ID'], [
  64. 'A_STATUS' => 'DELETED',
  65. 'A_LAST_ACTION_DATE' => 'NOW()',
  66. ]);
  67. }
  68. else if ($childRef['A_STATUS'] !== 'DELETED' && in_array($childRef['namespace'], $fieldNsList)) {
  69. UI::alert('info', "ref config for '{$childRef['namespace']}' active - OK");
  70. }
  71. else if ($childRef['A_STATUS'] === 'DELETED' && in_array($childRef['namespace'], $fieldNsList)) {
  72. UI::alert('warning', "activate ref config for '{$childRef['namespace']}' ...");
  73. if (!$childRef['ID']) throw new Exception("Missing ref config ID");
  74. DB::getPDO()->update('CRM_REF_CONFIG', 'ID', $childRef['ID'], [
  75. 'A_STATUS' => 'NORMAL',
  76. 'A_LAST_ACTION_DATE' => 'NOW()',
  77. ]);
  78. }
  79. else if ($childRef['A_STATUS'] === 'DELETED' && !in_array($childRef['namespace'], $fieldNsList)) {
  80. UI::alert('info', "ref config for '{$childRef['namespace']}' removed - OK");
  81. }
  82. else {
  83. UI::alert('danger', "Not implemented action for '{$childRef['namespace']}'");
  84. }
  85. }
  86. // TODO: create missing refConfig - field is not in $childRefList
  87. }
  88. if ('AntAcl' === $item['_type']) { // fix ref config by appInfo
  89. $refFields = array_filter($item['field'], function ($field) {
  90. return ('ref:' === substr($field['xsdType'], 0, 4));
  91. });
  92. DBG::log($refFields, 'array', "DBG \$refFields");
  93. // analyze
  94. $toMakeRefEvaluateByBackRef = false;
  95. $toMakeRefAsView = false;
  96. foreach ($refFields as $field) {
  97. $fieldName = $field['fieldNamespace'];
  98. $appInfo = ACL::decodeAppInfoJson($field['appInfo']);
  99. DBG::log($appInfo, 'array', "DBG field({$fieldName}) \$appInfo");
  100. // $appInfo['flat_relation_cache'] = [ ... ]
  101. // $appInfo['flat_relation_cache'] = [ ... ]
  102. // $appInfo['flat_relation_cache']['@backref_evaluate'] = 'true'
  103. // $appInfo['flat_relation_cache']['source'] = [ ... ]
  104. // $appInfo['flat_relation_cache']['source']['@name'] => 'krs'
  105. // $appInfo['flat_relation_cache']['source']['@xpath'] => 'default_db__x3A__BI_audit_KRS:BI_audit_KRS/krs'
  106. // $appInfo['flat_relation_cache']['source']['@ref_engine'] => 'view'
  107. if (!empty($appInfo['flat_relation_cache'])) {
  108. $flatCache = $appInfo['flat_relation_cache'];
  109. if ('true' === V::get('@backref_evaluate', '', $flatCache)) {
  110. $toMakeRefEvaluateByBackRef = true;
  111. }
  112. if (!empty($appInfo['flat_relation_cache']['source'])) {
  113. $sourceInfo = $appInfo['flat_relation_cache']['source'];
  114. if ('view' === V::get('@ref_engine', '', $sourceInfo)) {
  115. $toMakeRefAsView = true;
  116. }
  117. }
  118. }
  119. }
  120. // update ref config
  121. if ($toMakeRefAsView && !$toMakeRefEvaluateByBackRef) {
  122. UI::alert('danger', "TODO: set ref config to 'view' - field({$fieldName})");
  123. }
  124. if ($toMakeRefEvaluateByBackRef) { // $toMakeRefAsView nie ma znaczenia - czyta wg configa dla backRef
  125. try {
  126. $fieldRefConfRow = array_filter($childRefList, function ($childRef) use ($fieldName) {
  127. return ($fieldName === $childRef['namespace']);
  128. });
  129. if (!$fieldRefConfRow) throw new Exception("Nie znaleziono konfiguracji dla powiązania {$fieldName}");
  130. if (count($fieldRefConfRow) > 1) throw new Exception("BUG Znaleziono za dużo konfiguracji dla powiązania {$fieldName}");
  131. $fieldRefConfRow = $fieldRefConfRow[0];
  132. DBG::nicePrint($fieldRefConfRow, "\$fieldRefConfRow - TODO field({$fieldName})");
  133. // [namespace] => default_db__x3A__BI_audit_KRS:BI_audit_KRS
  134. // [A_STATUS] => NORMAL
  135. // [ID] => 118
  136. // [SOURCE] => view
  137. $refTable = ACL::getRefTable($item['namespace'], $fieldName); // updates ref_config_table if needed
  138. try {
  139. $refSelect = ACL::generateRefSelectSqlByBackRef($item['namespace'], $fieldName);
  140. ACL::setRefSource($item['namespace'], $fieldName, 'backRef', $refSelect);
  141. } catch (Exception $e) {
  142. DBG::log($e);
  143. UI::alert('danger', $e->getMessage());
  144. }
  145. if ('backRef' !== $fieldRefConfRow['SOURCE']) {
  146. ACL::updateChildRefSource($fieldRefConfRow['ID'], 'backRef');
  147. }
  148. } catch (Exception $e) {
  149. UI::alert('danger', $e->getMessage());
  150. }
  151. }
  152. }
  153. {
  154. if ('AntAcl' === $item['_type']) {
  155. $dbName = DB::getPDO()->getDatabaseName();
  156. $sqlFunBody = ACL::generateIsInstanceFunctionBody($namespace, $item);
  157. DBG::nicePrint($sqlFunBody, "\$sqlFunBody");
  158. $idInstance = ACL::getInstanceId($namespace);
  159. DB::getPDO()->execSql(" DROP FUNCTION IF EXISTS `{$dbName}`.`isInstance_{$idInstance}` ");
  160. // CREATE
  161. // [DEFINER = { user | CURRENT_USER }]
  162. // FUNCTION sp_name ([func_parameter[,...]])
  163. // RETURNS type
  164. // [characteristic ...] routine_body
  165. DB::getPDO()->execSql("
  166. CREATE DEFINER=`root`@`localhost`
  167. FUNCTION `{$dbName}`.`isInstance_{$idInstance}` ( pk INT(11) )
  168. RETURNS TINYINT(1)
  169. {$sqlFunBody}
  170. ");
  171. }
  172. }
  173. return;
  174. }
  175. echo UI::hButtonPost("Reinstall", [
  176. 'data' => [
  177. '_postTask' => 'reinstall'
  178. ],
  179. 'class' => 'btn btn-md btn-danger',
  180. 'title' => "Reinstall structure"
  181. ]);
  182. echo '<hr>';
  183. try {
  184. $this->printReinstallPreview($namespace);
  185. } catch (Exception $e) {
  186. DBG::log($e);
  187. UI::alert('danger', $e->getMessage());
  188. }
  189. } catch (Exception $e) {
  190. UI::alert('danger', "Error #" . $e->getCode() . "|" . $e->getLine() . ": " . $e->getMessage());
  191. DBG::log($e);
  192. }
  193. UI::endContainer();
  194. UI::dol();
  195. }
  196. public function printReinstallPreview($namespace) {
  197. $objectItem = SchemaFactory::loadDefaultObject('SystemObject')->getItem($namespace, [ 'propertyName' => "*,field" ]);
  198. DBG::log($objectItem, 'array', '$objectItem preview');
  199. switch ($objectItem['_type']) {
  200. case 'AntAcl': $this->printReinstallAntAclPreview($objectItem); break;
  201. case 'TableAcl': $this->printReinstallTableAclPreview($objectItem); break;
  202. case 'StorageAcl': $this->printReinstallStorageAclPreview($objectItem); break;
  203. default: throw new Exception("TODO: Not Implemented type '{$objectItem['_type']}'");
  204. }
  205. }
  206. public function printReinstallAntAclPreview($item) {
  207. Lib::loadClass('Schema_SystemObjectFieldStorageAcl');
  208. $antAclPath = Schema_SystemObjectFieldStorageAcl::getAntAclXsdBasePath($item['typeName']);
  209. if (!file_exists("{$antAclPath}/build.xml")) throw new Exception("Ant build file not exists");
  210. Lib::loadClass('XML');
  211. $xsdType = XML::getXsdTypeFromXsdSchema("{$antAclPath}/{$item['name']}.xsd", $namespace = $item['namespace'], $name = $item['name']);
  212. DBG::nicePrint($item, '$item');
  213. DBG::nicePrint($xsdType, '$xsdType');
  214. echo '<hr>';
  215. echo UI::h('h3', [], "Lista zmian:");
  216. echo ($item['primaryKey'] != $xsdType['primaryKey'])
  217. ? UI::h('p', [ 'style' => "" ], "@primaryKey - zmiana z '{$item['primaryKey']}' na '{$xsdType['primaryKey']}'")
  218. : UI::h('p', [ 'style' => "font-style:italic; color:silver" ], "@primaryKey - bez zmian");
  219. if (empty($xsdType['struct'])) throw new Exception("Field list not found for '{$item['namespace']}'");
  220. foreach ($xsdType['struct'] as $fieldName => $x) {
  221. $listEnum = [];
  222. if (!empty($x['restrictions']['enumeration'])) {
  223. $listEnum = $x['restrictions']['enumeration'];
  224. unset($x['restrictions']['enumeration']);
  225. }
  226. if (!empty($listEnum)) {
  227. DBG::log($listEnum, 'array', "\$listEnum for field '{$fieldName}'");
  228. }
  229. }
  230. $old = [
  231. 'fields' => array_map(function ($field) { return $field['fieldNamespace']; }, $item['field']),
  232. ];
  233. $new = [
  234. 'fields' => array_keys($xsdType['struct']),
  235. ];
  236. sort($old['fields']);
  237. sort($new['fields']);
  238. $diffFieldsToCreate = array_diff($new['fields'], $old['fields']);
  239. $diffFieldsToRemove = array_diff($old['fields'], $new['fields']);
  240. $sameFields = array_intersect($new['fields'], $old['fields']);
  241. echo (!empty($diffFieldsToCreate))
  242. ? UI::h('details', [ 'open' => "open" ], [
  243. UI::h('summary', [], "Pola do dodania (".count($diffFieldsToCreate)."):"),
  244. UI::h('ul', [], array_map(function ($fieldName) {
  245. return UI::h('li', [], $fieldName);
  246. }, $diffFieldsToCreate)),
  247. ])
  248. : UI::h('p', [ 'style' => "font-style:italic; color:silver" ], "Brak pól do dodania");
  249. echo (!empty($diffFieldsToRemove))
  250. ? UI::h('details', [ 'open' => "open", 'style' => "margin:4px 0; color:#8a6d3b; background-color:#fcf8e3; border:1px solid #faebcc;" ], [
  251. UI::h('summary', [ 'style' => "padding:4px; outline:none; cursor:pointer" ], "Pola do usunięcia (".count($diffFieldsToRemove)."):"),
  252. UI::h('ul', [], array_map(function ($fieldName) {
  253. return UI::h('li', [], $fieldName);
  254. }, $diffFieldsToRemove)),
  255. ])
  256. : UI::h('p', [ 'style' => "font-style:italic; color:silver" ], "Brak pól do usunięcia");
  257. foreach ($sameFields as $fieldName) {
  258. // UI::alert('warning', "TODO: is field changed? '{$fieldName}'");
  259. $oldField = array_filter($item['field'], function ($field) use ($fieldName) { return ( $fieldName === $field['fieldNamespace'] ); });
  260. $oldField = ($oldField) ? reset($oldField) : null;
  261. // DBG::nicePrint($oldField, "\$oldField '$fieldName'");
  262. $newField = $xsdType['struct'][$fieldName];
  263. // DBG::nicePrint($newField, "\$newField '$fieldName'");
  264. $fieldDiff = [];
  265. if ($newField['type'] !== $oldField['xsdType']) $fieldDiff[] = 'xsdType';
  266. if ($newField['minOccurs'] != $oldField['minOccurs']) $fieldDiff[] = 'minOccurs';
  267. if ($newField['maxOccurs'] != $oldField['maxOccurs']) $fieldDiff[] = 'maxOccurs';
  268. if (json_encode($newField['restrictions']) !== $oldField['xsdRestrictions']) $fieldDiff[] = 'xsdRestrictions';
  269. if (json_encode($newField['appInfo']) !== $oldField['appInfo']) $fieldDiff[] = 'appInfo';
  270. echo (!empty($fieldDiff))
  271. ? UI::h('p', [ 'style' => "" ], "Pole '{$fieldName}' - zmiany: " . implode(", ", $fieldDiff))
  272. : UI::h('p', [ 'style' => "font-style:italic; color:silver" ], "Pole '{$fieldName}' - bez zmian");
  273. }
  274. }
  275. public function printReinstallTableAclPreview($item) {
  276. throw new Exception("TODO: Podgląd zmian dla tabeli {$item['namespace']} ...");
  277. }
  278. public function printReinstallStorageAclPreview($item) {
  279. DBG::nicePrint($item, '$item');
  280. $acl = SchemaFactory::loadDefaultObject($item['name']);
  281. DBG::nicePrint($acl, '$acl');
  282. $xsdType = [
  283. 'primaryKey' => $acl->getPrimaryKeyField(),
  284. 'struct' => $acl->getFieldsWithXsdTypes()
  285. ];
  286. DBG::nicePrint($xsdType, '$xsdType');
  287. echo '<hr>';
  288. echo UI::h('h3', [], "Lista zmian:");
  289. echo ($item['primaryKey'] != $xsdType['primaryKey'])
  290. ? UI::h('p', [ 'style' => "" ], "@primaryKey - zmiana z '{$item['primaryKey']}' na '{$xsdType['primaryKey']}'")
  291. : UI::h('p', [ 'style' => "font-style:italic; color:silver" ], "@primaryKey - bez zmian");
  292. if (empty($xsdType['struct'])) throw new Exception("Field list not found for '{$item['namespace']}'");
  293. foreach ($xsdType['struct'] as $fieldName => $x) {
  294. $listEnum = [];
  295. if (!empty($x['restrictions']['enumeration'])) {
  296. $listEnum = $x['restrictions']['enumeration'];
  297. unset($x['restrictions']['enumeration']);
  298. }
  299. if (!empty($listEnum)) {
  300. DBG::log($listEnum, 'array', "\$listEnum for field '{$fieldName}'");
  301. }
  302. }
  303. $old = [
  304. 'fields' => array_map(function ($field) { return $field['fieldNamespace']; }, $item['field']),
  305. ];
  306. $new = [
  307. 'fields' => array_keys($xsdType['struct']),
  308. ];
  309. sort($old['fields']);
  310. sort($new['fields']);
  311. $diffFieldsToCreate = array_diff($new['fields'], $old['fields']);
  312. $diffFieldsToRemove = array_diff($old['fields'], $new['fields']);
  313. $sameFields = array_intersect($new['fields'], $old['fields']);
  314. echo (!empty($diffFieldsToCreate))
  315. ? UI::h('details', [ 'open' => "open" ], [
  316. UI::h('summary', [], "Pola do dodania (".count($diffFieldsToCreate)."):"),
  317. UI::h('ul', [], array_map(function ($fieldName) {
  318. return UI::h('li', [], $fieldName);
  319. }, $diffFieldsToCreate)),
  320. ])
  321. : UI::h('p', [ 'style' => "font-style:italic; color:silver" ], "Brak pól do dodania");
  322. echo (!empty($diffFieldsToRemove))
  323. ? UI::h('details', [ 'open' => "open", 'style' => "margin:4px 0; color:#8a6d3b; background-color:#fcf8e3; border:1px solid #faebcc;" ], [
  324. UI::h('summary', [ 'style' => "padding:4px; outline:none; cursor:pointer" ], "Pola do usunięcia (".count($diffFieldsToRemove)."):"),
  325. UI::h('ul', [], array_map(function ($fieldName) {
  326. return UI::h('li', [], $fieldName);
  327. }, $diffFieldsToRemove)),
  328. ])
  329. : UI::h('p', [ 'style' => "font-style:italic; color:silver" ], "Brak pól do usunięcia");
  330. foreach ($sameFields as $fieldName) {
  331. // UI::alert('warning', "TODO: is field changed? '{$fieldName}'");
  332. $oldField = array_filter($item['field'], function ($field) use ($fieldName) { return ( $fieldName === $field['fieldNamespace'] ); });
  333. $oldField = ($oldField) ? reset($oldField) : null;
  334. // DBG::nicePrint($oldField, "\$oldField '$fieldName'");
  335. $newField = $xsdType['struct'][$fieldName];
  336. // DBG::nicePrint($newField, "\$newField '$fieldName'");
  337. $fieldDiff = [];
  338. if ($newField['type'] !== $oldField['xsdType']) $fieldDiff[] = 'xsdType';
  339. if ($newField['minOccurs'] != $oldField['minOccurs']) $fieldDiff[] = 'minOccurs';
  340. if ($newField['maxOccurs'] != $oldField['maxOccurs']) $fieldDiff[] = 'maxOccurs';
  341. if (json_encode($newField['restrictions']) !== $oldField['xsdRestrictions']) $fieldDiff[] = 'xsdRestrictions';
  342. if (json_encode($newField['appInfo']) !== $oldField['appInfo']) $fieldDiff[] = 'appInfo';
  343. echo (!empty($fieldDiff))
  344. ? UI::h('p', [ 'style' => "" ], "Pole '{$fieldName}' - zmiany: " . implode(", ", $fieldDiff))
  345. : UI::h('p', [ 'style' => "font-style:italic; color:silver" ], "Pole '{$fieldName}' - bez zmian");
  346. }
  347. }
  348. public function viewXsdSourceAction() {
  349. try {
  350. $namespace = V::get('namespace', '', $_GET);
  351. if (empty($namespace)) throw new Exception("Missing param namespace");
  352. $objectItem = SchemaFactory::loadDefaultObject('SystemObject')->getItem($namespace, [ 'propertyName' => "*,field" ]);
  353. switch ($objectItem['_type']) {
  354. case 'AntAcl': $this->viewXsdSource($objectItem); break;
  355. // case 'TableAcl': $this->viewXsdSource($objectItem); break;
  356. default: throw new Exception("TODO: Not Implemented type '{$objectItem['_type']}'");
  357. }
  358. } catch (Exception $e) {
  359. DBG::log($e);
  360. echo "Error #" . $e->getCode() . "|" . $e->getLine() . ": " . $e->getMessage();
  361. }
  362. }
  363. function viewXsdSource($objectItem) {
  364. if (empty($objectItem)) throw new Exception("Missing objectItem in viewXsdSource");
  365. DBG::log($objectItem, "viewXsdSource \$objectItem");
  366. Lib::loadClass('Schema_SystemObjectFieldStorageAcl');
  367. $antAclPath = Schema_SystemObjectFieldStorageAcl::getAntAclXsdBasePath($objectItem['typeName']);
  368. if (!file_exists("{$antAclPath}/build.xml")) throw new Exception("Ant build file not exists");
  369. DBG::log(str_replace(APP_PATH_ROOT, '~', $antAclPath), "viewXsdSource \$antAclPath");
  370. $xsdFile = "{$antAclPath}/{$objectItem['name']}.xsd";
  371. if (!file_exists($xsdFile)) throw new Exception("Xsd file not exists");
  372. DBG::log(str_replace(APP_PATH_ROOT, '~', $xsdFile), "viewXsdSource \$xsdFile");
  373. header('Content-Type: application/xml; charset=utf-8');
  374. $fd = fopen($xsdFile, 'r');
  375. fpassthru($fd);
  376. exit;
  377. }
  378. }