Msgs.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. <?php
  2. Lib::loadClass('RouteBase');
  3. /*
  4. ## Flash message system:
  5. System for automatic and custom messages for users.
  6. - `app_className` - for automatic msgs to search for msg text
  7. - `msg` - msg to show or to parse by `app_className`
  8. - `uiTargetType` - where to show this msg eg. 'default_db_table', 'after_login', 'everywhere'
  9. - `uiTargetName` - eg. database table name (if from default_db)
  10. - `userTargetType` - type of users allowed to see this msg eg. 'everyone', 'admin' (ADMIN_LEVEL=0?), 'user', 'group'
  11. - `userTargetName` - login, group name
  12. - `actionExecuted` - execution time if msg require user to run specific action (send msg.id in request)
  13. - `actionNotes` - notes/msgs/dbg for user actions executed from this msg
  14. Messages created by db triggers must define `app_className` that should parse `msg`. For example to use in FixProjectPath and FixZasobPath to keep correct paths.
  15. ## `A_STATUS`
  16. - 'WAITING' - msg sent by `A_RECORD_CREATE_AUTHOR` at `A_RECORD_CREATE_DATE`
  17. - 'NORMAL' - msg read by `A_RECORD_UPDATE_AUTHOR` at `A_RECORD_UPDATE_DATE`
  18. - 'OFF_HARD' - msg deleted by `A_RECORD_DELETE_AUTHOR` if != `A_RECORD_CREATE_AUTHOR` at `A_RECORD_DELETE_DATE`
  19. - 'DELETED' - msg deleted by `A_RECORD_CREATE_AUTHOR` at `A_RECORD_DELETE_DATE`
  20. */
  21. class Route_Msgs extends RouteBase {
  22. public function handleAuth() {
  23. if (!User::logged()) {
  24. throw new HttpException('Unauthorized', 401);
  25. }
  26. }
  27. public function defaultAction() {
  28. SE_Layout::gora();
  29. ?>
  30. <div class="container">
  31. <h1>Messages system</h1>
  32. ...
  33. </div>
  34. <?php
  35. SE_Layout::dol();
  36. }
  37. public function reinstallAction() {
  38. $this->reinstall();
  39. die('OK');
  40. }
  41. public function reinstallFunctionsAction() {
  42. $this->reinstallFunctions();
  43. die('OK');
  44. }
  45. public function runAction() {
  46. $msgId = V::get('_msgId', 0, $_REQUEST, 'int');
  47. if ($msgId > 0) {
  48. $this->runByMessageId($msgId);
  49. }
  50. $jsonData = new stdClass();
  51. $jsonData->type = 'success';
  52. $jsonData->msg = 'Gotowe';
  53. echo json_encode($jsonData);
  54. exit;
  55. }
  56. public function addTestMsgAction() {
  57. $sql = "INSERT INTO `CRM_UI_MSGS` (`ID`, `app_className`, `msg`, `msgType`, `uiTargetType`, `uiTargetName`, `userTargetType`, `userTargetName`, `actionExecutedTime`, `actionNotes`, `A_RECORD_CREATE_DATE`, `A_RECORD_CREATE_AUTHOR`, `A_RECORD_UPDATE_DATE`, `A_RECORD_UPDATE_AUTHOR`)
  58. VALUES (NULL, 'FixZasobPath', 'Update all paths', 'danger', 'default_db_table', 'CRM_LISTA_ZASOBOW', 'user', 'plabudda', NULL, '', NULL, 'plabudda', NULL, '')";
  59. $db = DB::getDB();
  60. $db->query($sql);
  61. die('OK');
  62. }
  63. public function reinstallFunctions() {
  64. $sqlList = array();
  65. //$sqlList['RemoveTable_CRM_UI_MSGS__EXEC_LOG'] = "DROP TABLE IF EXISTS `CRM_UI_MSGS__EXEC_LOG`";
  66. $sqlList['CreateTable_CRM_UI_MSGS__EXEC_LOG'] = "
  67. CREATE TABLE IF NOT EXISTS `CRM_UI_MSGS__EXEC_LOG` (
  68. `ID` int(11) NOT NULL AUTO_INCREMENT
  69. , `_exec_time` datetime DEFAULT NULL
  70. , `author` VARCHAR(40) DEFAULT ''
  71. , `app_className` VARCHAR(255) DEFAULT ''
  72. , `msg` VARCHAR(255) DEFAULT ''
  73. , `uiTargetType` VARCHAR(255) DEFAULT ''
  74. , `uiTargetName` VARCHAR(255) DEFAULT ''
  75. , `userTargetType` VARCHAR(255) DEFAULT ''
  76. , `userTargetName` VARCHAR(255) DEFAULT ''
  77. , PRIMARY KEY (`ID`)
  78. ) ENGINE=MyISAM DEFAULT CHARSET=latin2
  79. ";
  80. $sqlList['RemoveProcedureMarkAsExecuted'] = "DROP PROCEDURE IF EXISTS `CRM_UI_MSGS__markAsExecuted`";
  81. $sqlList['InstallProcedureMarkAsExecuted'] = <<<SQL_QUERY
  82. CREATE DEFINER=`root`@`localhost` PROCEDURE `CRM_UI_MSGS__markAsExecuted`(
  83. IN author VARCHAR(40),
  84. IN app_className VARCHAR(255),
  85. IN msg VARCHAR(255),
  86. IN uiTargetType VARCHAR(255),
  87. IN uiTargetName VARCHAR(255),
  88. IN userTargetType VARCHAR(255),
  89. IN userTargetName VARCHAR(255)
  90. )
  91. BEGIN
  92. -- insert into `CRM_UI_MSGS__EXEC_LOG`
  93. -- (`author`, `app_className`, `msg`, `uiTargetType`, `uiTargetName`, `userTargetType`, `userTargetName`, `_exec_time`)
  94. -- values(author, app_className, msg, uiTargetType, uiTargetName, userTargetType, userTargetName, NOW());
  95. update `CRM_UI_MSGS` m
  96. set m.`A_STATUS`='OFF_HARD'
  97. , m.`actionExecutedTime`=NOW()
  98. , m.`A_RECORD_UPDATE_DATE`=NOW()
  99. , m.`A_RECORD_UPDATE_AUTHOR`=author
  100. where m.`A_STATUS`='WAITING' -- TODO: not in('DELETED', 'OFF_HARD')
  101. and m.`app_className`=app_className
  102. and m.`msg`=msg
  103. and m.`uiTargetType`=uiTargetType
  104. and m.`uiTargetName`=uiTargetName
  105. and m.`userTargetType`=userTargetType
  106. and m.`userTargetName`=userTargetName
  107. ;
  108. END
  109. SQL_QUERY;
  110. $sqlList['RemoveProcedureMarkTableEveryoneAsExecuted'] = "DROP PROCEDURE IF EXISTS `CRM_UI_MSGS__markTableEveryoneAsExecuted`";
  111. $sqlList['InstallProcedureMarkTableEveryoneAsExecuted'] = <<<SQL_QUERY
  112. CREATE DEFINER=`root`@`localhost` PROCEDURE `CRM_UI_MSGS__markTableEveryoneAsExecuted`(
  113. IN author VARCHAR(40),
  114. IN app_className VARCHAR(255),
  115. IN msg VARCHAR(255),
  116. IN uiTargetName VARCHAR(255)
  117. )
  118. BEGIN
  119. CALL CRM_UI_MSGS__markAsExecuted(author, app_className, msg, 'default_db_table', uiTargetName, 'everyone', '');
  120. END
  121. SQL_QUERY;
  122. $sqlList['RemoveProcedureAddUniqueMsg'] = "DROP PROCEDURE IF EXISTS `CRM_UI_MSGS__addUniqueMsg`";
  123. $sqlList['InstallProcedureAddUniqueMsg'] = <<<SQL_QUERY
  124. CREATE DEFINER=`root`@`localhost` PROCEDURE `CRM_UI_MSGS__addUniqueMsg`(
  125. IN author VARCHAR(40),
  126. IN app_className VARCHAR(255),
  127. IN msgType VARCHAR(16),
  128. IN msg VARCHAR(255),
  129. IN uiTargetType VARCHAR(255),
  130. IN uiTargetName VARCHAR(255),
  131. IN userTargetType VARCHAR(255),
  132. IN userTargetName VARCHAR(255)
  133. )
  134. BEGIN
  135. IF (select count(1)
  136. from `CRM_UI_MSGS`
  137. where `app_className`=app_className
  138. and `msg`=msg
  139. and `uiTargetType`=uiTargetType
  140. and `uiTargetName`=uiTargetName
  141. and `userTargetType`=userTargetType
  142. and `userTargetName`=userTargetName
  143. and `A_STATUS`='WAITING'
  144. ) = 0 THEN
  145. INSERT INTO `CRM_UI_MSGS` (`ID`
  146. , `app_className`, `msg`, `msgType`
  147. , `uiTargetType`, `uiTargetName`
  148. , `userTargetType`, `userTargetName`
  149. , `A_RECORD_CREATE_DATE`, `A_RECORD_CREATE_AUTHOR`)
  150. VALUES (NULL
  151. , app_className, msg, msgType
  152. , uiTargetType, uiTargetName
  153. , userTargetType, userTargetName
  154. , NOW(), author
  155. );
  156. END IF;
  157. END
  158. SQL_QUERY;
  159. $sqlList['RemoveProcedureAddTableEveryoneUniqueMsg'] = "DROP PROCEDURE IF EXISTS `CRM_UI_MSGS__addTableEveryoneUniqueMsg`";
  160. $sqlList['InstallProcedureAddTableEveryoneUniqueMsg'] = <<<SQL_QUERY
  161. CREATE DEFINER=`root`@`localhost` PROCEDURE `CRM_UI_MSGS__addTableEveryoneUniqueMsg`(
  162. IN author VARCHAR(40),
  163. IN app_className VARCHAR(255),
  164. IN msgType VARCHAR(16),
  165. IN msg VARCHAR(255),
  166. IN uiTargetName VARCHAR(255)
  167. )
  168. BEGIN
  169. CALL CRM_UI_MSGS__addUniqueMsg(author, app_className, msgType, msg, 'default_db_table', uiTargetName, 'everyone', '');
  170. END
  171. SQL_QUERY;
  172. $formFixZasobPath = <<<SQL_QUERY
  173. CALL CRM_UI_MSGS__addTableEveryoneUniqueMsg(NEW.A_RECORD_UPDATE_AUTHOR, 'FixZasobPath', 'danger', 'Update all paths', 'CRM_LISTA_ZASOBOW');
  174. IF (select count(1) from `CRM_UI_MSGS` where `app_className`='FixZasobPath'
  175. and `msg`='Update all paths'
  176. and `uiTargetType`='default_db_table'
  177. and `uiTargetName`='CRM_LISTA_ZASOBOW'
  178. and `A_STATUS`='WAITING'
  179. ) = 0 THEN
  180. INSERT INTO `CRM_UI_MSGS` (`ID`
  181. , `app_className`, `msg`, `msgType`
  182. , `uiTargetType`, `uiTargetName`
  183. , `userTargetType`
  184. , `A_RECORD_CREATE_DATE`, `A_RECORD_CREATE_AUTHOR`)
  185. VALUES (NULL
  186. , 'FixZasobPath', 'Update all paths', 'danger'
  187. , 'default_db_table', 'CRM_LISTA_ZASOBOW'
  188. , 'everyone'
  189. , NOW(), NEW.A_RECORD_UPDATE_AUTHOR
  190. );
  191. END IF;
  192. SQL_QUERY;
  193. $formFixProjectPath = <<<SQL_QUERY
  194. CALL CRM_UI_MSGS__addTableEveryoneUniqueMsg(NEW.A_RECORD_UPDATE_AUTHOR, 'FixProjectPath', 'danger', 'Update all paths', 'IN7_MK_BAZA_DYSTRYBUCJI');
  195. IF (select count(1) from `CRM_UI_MSGS` where `app_className`='FixProjectPath'
  196. and `msg`='Update all paths'
  197. and `uiTargetType`='default_db_table'
  198. and `uiTargetName`='IN7_MK_BAZA_DYSTRYBUCJI'
  199. and `A_STATUS`='WAITING'
  200. ) = 0 THEN
  201. INSERT INTO `CRM_UI_MSGS` (`ID`
  202. , `app_className`, `msg`, `msgType`
  203. , `uiTargetType`, `uiTargetName`
  204. , `userTargetType`
  205. , `A_RECORD_CREATE_DATE`, `A_RECORD_CREATE_AUTHOR`)
  206. VALUES (NULL
  207. , 'FixProjectPath', 'Update all paths', 'danger'
  208. , 'default_db_table', 'IN7_MK_BAZA_DYSTRYBUCJI'
  209. , 'everyone'
  210. , NOW(), NEW.A_RECORD_UPDATE_AUTHOR
  211. );
  212. END IF;
  213. SQL_QUERY;
  214. $db = DB::getDB();
  215. if ($db->has_errors()) {
  216. throw new Exception("DB Errors: " . implode("\n<br>", $db->get_errors()));
  217. }
  218. foreach ($sqlList as $sqlName => $sql) {
  219. $res = $db->query($sql);
  220. if ($db->has_errors()) {
  221. throw new Exception("DB Errors at sql '{$sqlName}': " . implode("\n<br>", $db->get_errors()));
  222. }
  223. }
  224. }
  225. public function reinstall() {
  226. $sqlList = array();
  227. //$sqlList['RemoveTable'] = "DROP TABLE IF EXISTS `CRM_UI_MSGS`";// TODO: update struct (add cells, rm old keys) - not drop/create
  228. $sqlList['InstallTable'] = "
  229. CREATE TABLE IF NOT EXISTS `CRM_UI_MSGS` (
  230. `ID` int(11) NOT NULL AUTO_INCREMENT
  231. , `idReplyTo` int(11) NOT NULL DEFAULT 0
  232. , `idThread` int(11) NOT NULL DEFAULT 0
  233. -- app_className - for automatic msgs to search for msg text
  234. , `app_className` varchar(255) DEFAULT NULL
  235. -- msg - msg to show or to parse by app_className
  236. , `msg` varchar(1000) NOT NULL
  237. , `msgType` enum('info','danger','warning','success') NOT NULL DEFAULT 'info'
  238. -- uiTargetType - where to show this msg eg. 'default_db_table', 'after_login', 'everywhere'
  239. , `uiTargetType` enum('default_db_table','default_db_table_record','after_login','everywhere') NOT NULL
  240. -- uiTargetName - eg. database table name (if from default_db)
  241. , `uiTargetName` varchar(255) NOT NULL DEFAULT ''
  242. -- userTargetType - type of users allowed to see this msg eg. 'everyone', 'admin' (ADMIN_LEVEL=0?), 'user', 'group'
  243. , `userTargetType` enum('none','everyone','admin','user','group') NOT NULL DEFAULT 'none'
  244. -- userTargetName - login, group name
  245. , `userTargetName` varchar(255) NOT NULL DEFAULT ''
  246. -- actionExecutedTime - execution time if msg require user to run specific action (send msg.id in request)
  247. , `actionExecutedTime` datetime DEFAULT NULL
  248. -- actionNotes - notes/msgs/dbg for user actions executed from this msg
  249. , `actionNotes` varchar(255) NOT NULL DEFAULT ''
  250. , `A_STATUS` enum('WAITING','NORMAL','OFF_HARD','DELETED') NOT NULL DEFAULT 'WAITING'
  251. , `A_RECORD_CREATE_DATE` datetime DEFAULT NULL
  252. , `A_RECORD_CREATE_AUTHOR` varchar(40) NOT NULL DEFAULT ''
  253. , `A_RECORD_UPDATE_DATE` datetime DEFAULT NULL
  254. , `A_RECORD_UPDATE_AUTHOR` varchar(40) NOT NULL DEFAULT ''
  255. , `A_RECORD_DELETE_DATE` datetime DEFAULT NULL
  256. , `A_RECORD_DELETE_AUTHOR` varchar(40) NOT NULL DEFAULT ''
  257. , PRIMARY KEY (`ID`)
  258. , KEY `app_className` (`app_className`)
  259. ) ENGINE=MyISAM DEFAULT CHARSET=latin2
  260. ";
  261. $db = DB::getDB();
  262. if ($db->has_errors()) {
  263. throw new Exception("DB Errors: " . implode("\n<br>", $db->get_errors()));
  264. }
  265. foreach ($sqlList as $sqlName => $sql) {
  266. $res = $db->query($sql);
  267. if ($db->has_errors()) {
  268. throw new Exception("DB Errors at sql '{$sqlName}': " . implode("\n<br>", $db->get_errors()));
  269. }
  270. }
  271. $this->reinstallFunctions();
  272. }
  273. public function getActiveMessagesForTable($tblName) {
  274. if (empty($tblName)) return;
  275. $sqlTableName = DB::getPDO()->quote($tblName);
  276. $usrLogin = User::getLogin();
  277. $msgs = [];
  278. $sql = "
  279. select m.*
  280. from `CRM_UI_MSGS` m
  281. where m.`uiTargetType`='default_db_table'
  282. and m.`A_STATUS`='WAITING'
  283. and m.`uiTargetName`={$sqlTableName}
  284. and (m.`userTargetType` in('everyone')
  285. or (m.`userTargetType`='user' and m.`userTargetName`='{$usrLogin}')
  286. -- TODO: use 'admin', 'group'
  287. )
  288. ";
  289. foreach (DB::getPDO()->fetchAll($sql) as $row) {
  290. $r = (object)$row;
  291. if ($msg = $this->parseMessage($r)) {
  292. $msg['link'] = 'index.php?_route=Msgs&_task=run&_msgId=' . $r->ID;
  293. $msg['linkType'] = 'ajax';
  294. $msgs[$r->ID] = $msg;
  295. }
  296. }
  297. return $msgs;
  298. }
  299. public function getActiveMessagesForTableRecord($tblName, $id) {
  300. if (empty($tblName)) return;
  301. $db = DB::getDB();
  302. $tblName = $db->_($tblName);
  303. $usrLogin = User::getLogin();
  304. $msgs = null;
  305. $sql = "select m.*
  306. from `CRM_UI_MSGS` m
  307. where m.`uiTargetType`='default_db_table_record'
  308. and m.`uiTargetName`='{$tblName}.{$id}'
  309. and (m.`userTargetType` in('everyone')
  310. or (m.`userTargetType`='user' and m.`userTargetName`='{$usrLogin}')
  311. -- TODO: use group id
  312. )
  313. and m.`A_STATUS`='WAITING'
  314. order by m.`ID` DESC
  315. ";
  316. $db = DB::getDB();
  317. $res = $db->query($sql);
  318. while ($r = $db->fetch($res)) {
  319. if ($msg = $this->parseMessage($r)) {
  320. $msg['link'] = 'index.php?_route=Msgs&_task=run&_msgId=' . $r->ID;
  321. $msg['linkType'] = 'ajax';
  322. $msgs[$r->ID] = $msg;
  323. }
  324. }
  325. return $msgs;
  326. }
  327. public function parseMessage($r) {
  328. $msg = null;// ['type'=>'info', 'message'=>'...']
  329. // $r->app_className - for automatic msgs to search for msg text
  330. // $r->msg - for automatic msgs to search for msg text
  331. // $r->msgType - 'info','danger','warning','success'
  332. if (!empty($r->app_className)) {
  333. $route = Router::getRoute($r->app_className);
  334. $msg = array();
  335. $msg['message'] = $route->parseMessageFromMsgsSystem($r->msg);
  336. $msg['type'] = $r->msgType;
  337. $msg['_raw'] = $r;
  338. } else {
  339. $msg = array();
  340. $msg['message'] = $this->parseMessageFromMsgsSystem($r->msg);
  341. $msg['type'] = $r->msgType;
  342. $msg['_raw'] = $r;
  343. }
  344. return $msg;
  345. }
  346. public function parseMessageFromMsgsSystem($msg) {
  347. return $msg;
  348. }
  349. public function runByMessageId($id) {
  350. $msgRow = $this->getActiveMessage($id);
  351. $execNotes = '';
  352. if (!empty($msgRow->app_className)) {
  353. $route = Router::getRoute($msgRow->app_className);
  354. $route->runByMessageFromMsgsSystem($msgRow->msg, $execNotes);
  355. }
  356. $this->forceFinishMessage($id, $execNotes);
  357. }
  358. public function getMessage($id) {
  359. if (empty($id)) return;
  360. $id = intval($id);
  361. if ($id <= 0) return;
  362. $msg = null;
  363. $sql = "select * from `CRM_UI_MSGS` where `ID`='{$id}' ";
  364. $db = DB::getDB();
  365. $res = $db->query($sql);
  366. if ($r = $db->fetch($res)) {
  367. $msg = $r;
  368. }
  369. return $msg;
  370. }
  371. public function getActiveMessage($id) {
  372. if (empty($id)) return;
  373. $id = intval($id);
  374. if ($id <= 0) return;
  375. $msg = null;
  376. $sql = "select m.*
  377. from `CRM_UI_MSGS` m
  378. where m.`ID`='{$id}'
  379. and m.`A_STATUS`='WAITING'
  380. ";
  381. $db = DB::getDB();
  382. $res = $db->query($sql);
  383. if ($r = $db->fetch($res)) {
  384. $msg = $r;
  385. }
  386. if (!$msg) {
  387. throw new HttpException("Message not found", 404);
  388. }
  389. return $msg;
  390. }
  391. public function forceFinishMessage($id, $execNotes) {
  392. if (empty($id)) return;
  393. $id = intval($id);
  394. if ($id <= 0) return;
  395. $usrLogin = User::getLogin();
  396. $db = DB::getDB();
  397. $execNotes = $db->_($execNotes);
  398. $sql = "update `CRM_UI_MSGS`
  399. set `A_STATUS`='OFF_HARD'
  400. , `actionExecutedTime`=NOW()
  401. , `actionNotes`='{$execNotes}'
  402. , `A_RECORD_UPDATE_DATE`=NOW()
  403. , `A_RECORD_UPDATE_AUTHOR`='{$usrLogin}'
  404. where `ID`='{$id}'
  405. ";
  406. $db->query($sql);
  407. return;
  408. }
  409. public function removeMessage($id) {
  410. if (empty($id)) return;
  411. $id = intval($id);
  412. if ($id <= 0) return;
  413. $sql = "update `CRM_UI_MSGS` set `A_STATUS`='DELETED' where `ID`='{$id}' ";
  414. $db = DB::getDB();
  415. $db->query($sql);
  416. }
  417. public function removeTableRecordMsg($idMsg) {
  418. // IDEA: do kosza - add trigger to insert into `CRM_UI_MSGS__TRASH` after DELETE on `CRM_UI_MSGS`
  419. $idMsg = intval($idMsg);
  420. if ($idMsg <= 0) throw new Exception("Brak wiadomości!");
  421. $usrLogin = User::getLogin();
  422. $db = DB::getDB();
  423. if (!$db) throw new Exception("Brak dazy danych!");
  424. if ($db->has_errors()) throw new Exception("DB Errors: " . implode("\n<br>", $db->get_errors()));
  425. $sqlTODO = "delete `CRM_UI_MSGS` where `ID`='{$idMsg}' ";
  426. $sql = "update `CRM_UI_MSGS`
  427. set `A_STATUS`=IF('{$usrLogin}'=`A_RECORD_CREATE_AUTHOR`, 'DELETED', 'OFF_SOFT')
  428. , `A_RECORD_DELETE_AUTHOR`='{$usrLogin}'
  429. , `A_RECORD_DELETE_DATE`=NOW()
  430. where `ID`='{$idMsg}'
  431. ";
  432. DBG::_('DBG_MSGS', '>1', "sql", $sql, __CLASS__, __FUNCTION__, __LINE__);
  433. $res = $db->query($sql);
  434. if (!$res || $db->has_errors()) throw new Exception("Wystąpiły błędy podczas próby zapisu wiadomości: " . implode("\n<br>", $db->get_errors()));
  435. }
  436. }