Msgs.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  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. function handleAuth() {
  23. if (!User::logged()) {
  24. throw new HttpException('Unauthorized', 401);
  25. }
  26. }
  27. 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. function reinstallAction() {
  38. $this->reinstall();
  39. die('OK');
  40. }
  41. function reinstallFunctionsAction() {
  42. $this->reinstallFunctions();
  43. die('OK');
  44. }
  45. 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. function reinstallFunctions() {
  57. $sqlList = array();
  58. //$sqlList['RemoveTable_CRM_UI_MSGS__EXEC_LOG'] = "DROP TABLE IF EXISTS `CRM_UI_MSGS__EXEC_LOG`";
  59. $sqlList['CreateTable_CRM_UI_MSGS__EXEC_LOG'] = "
  60. CREATE TABLE IF NOT EXISTS `CRM_UI_MSGS__EXEC_LOG` (
  61. `ID` int(11) NOT NULL AUTO_INCREMENT
  62. , `_exec_time` datetime DEFAULT NULL
  63. , `author` VARCHAR(40) DEFAULT ''
  64. , `app_className` VARCHAR(255) DEFAULT ''
  65. , `msg` VARCHAR(255) DEFAULT ''
  66. , `uiTargetType` VARCHAR(255) DEFAULT ''
  67. , `uiTargetName` VARCHAR(255) DEFAULT ''
  68. , `userTargetType` VARCHAR(255) DEFAULT ''
  69. , `userTargetName` VARCHAR(255) DEFAULT ''
  70. , PRIMARY KEY (`ID`)
  71. ) ENGINE=MyISAM DEFAULT CHARSET=latin2
  72. ";
  73. $sqlList['RemoveProcedureMarkAsExecuted'] = "DROP PROCEDURE IF EXISTS `CRM_UI_MSGS__markAsExecuted`";
  74. $sqlList['InstallProcedureMarkAsExecuted'] = <<<SQL_QUERY
  75. CREATE DEFINER=`root`@`localhost` PROCEDURE `CRM_UI_MSGS__markAsExecuted`(
  76. IN author VARCHAR(40),
  77. IN app_className VARCHAR(255),
  78. IN msg VARCHAR(255),
  79. IN uiTargetType VARCHAR(255),
  80. IN uiTargetName VARCHAR(255),
  81. IN userTargetType VARCHAR(255),
  82. IN userTargetName VARCHAR(255)
  83. )
  84. BEGIN
  85. -- insert into `CRM_UI_MSGS__EXEC_LOG`
  86. -- (`author`, `app_className`, `msg`, `uiTargetType`, `uiTargetName`, `userTargetType`, `userTargetName`, `_exec_time`)
  87. -- values(author, app_className, msg, uiTargetType, uiTargetName, userTargetType, userTargetName, NOW());
  88. update `CRM_UI_MSGS` m
  89. set m.`A_STATUS`='OFF_HARD'
  90. , m.`actionExecutedTime`=NOW()
  91. , m.`A_RECORD_UPDATE_DATE`=NOW()
  92. , m.`A_RECORD_UPDATE_AUTHOR`=author
  93. where m.`A_STATUS`='WAITING' -- TODO: not in('DELETED', 'OFF_HARD')
  94. and m.`app_className`=app_className
  95. and m.`msg`=msg
  96. and m.`uiTargetType`=uiTargetType
  97. and m.`uiTargetName`=uiTargetName
  98. and m.`userTargetType`=userTargetType
  99. and m.`userTargetName`=userTargetName
  100. ;
  101. END
  102. SQL_QUERY;
  103. $sqlList['RemoveProcedureMarkTableEveryoneAsExecuted'] = "DROP PROCEDURE IF EXISTS `CRM_UI_MSGS__markTableEveryoneAsExecuted`";
  104. $sqlList['InstallProcedureMarkTableEveryoneAsExecuted'] = <<<SQL_QUERY
  105. CREATE DEFINER=`root`@`localhost` PROCEDURE `CRM_UI_MSGS__markTableEveryoneAsExecuted`(
  106. IN author VARCHAR(40),
  107. IN app_className VARCHAR(255),
  108. IN msg VARCHAR(255),
  109. IN uiTargetName VARCHAR(255)
  110. )
  111. BEGIN
  112. CALL CRM_UI_MSGS__markAsExecuted(author, app_className, msg, 'default_db_table', uiTargetName, 'everyone', '');
  113. END
  114. SQL_QUERY;
  115. $sqlList['RemoveProcedureAddUniqueMsg'] = "DROP PROCEDURE IF EXISTS `CRM_UI_MSGS__addUniqueMsg`";
  116. $sqlList['InstallProcedureAddUniqueMsg'] = <<<SQL_QUERY
  117. CREATE DEFINER=`root`@`localhost` PROCEDURE `CRM_UI_MSGS__addUniqueMsg`(
  118. IN author VARCHAR(40),
  119. IN app_className VARCHAR(255),
  120. IN msgType VARCHAR(16),
  121. IN msg VARCHAR(255),
  122. IN uiTargetType VARCHAR(255),
  123. IN uiTargetName VARCHAR(255),
  124. IN userTargetType VARCHAR(255),
  125. IN userTargetName VARCHAR(255)
  126. )
  127. BEGIN
  128. IF (select count(1)
  129. from `CRM_UI_MSGS`
  130. where `app_className`=app_className
  131. and `msg`=msg
  132. and `uiTargetType`=uiTargetType
  133. and `uiTargetName`=uiTargetName
  134. and `userTargetType`=userTargetType
  135. and `userTargetName`=userTargetName
  136. and `A_STATUS`='WAITING'
  137. ) = 0 THEN
  138. INSERT INTO `CRM_UI_MSGS` (`ID`
  139. , `app_className`, `msg`, `msgType`
  140. , `uiTargetType`, `uiTargetName`
  141. , `userTargetType`, `userTargetName`
  142. , `A_RECORD_CREATE_DATE`, `A_RECORD_CREATE_AUTHOR`)
  143. VALUES (NULL
  144. , app_className, msg, msgType
  145. , uiTargetType, uiTargetName
  146. , userTargetType, userTargetName
  147. , NOW(), author
  148. );
  149. END IF;
  150. END
  151. SQL_QUERY;
  152. $sqlList['RemoveProcedureAddTableEveryoneUniqueMsg'] = "DROP PROCEDURE IF EXISTS `CRM_UI_MSGS__addTableEveryoneUniqueMsg`";
  153. $sqlList['InstallProcedureAddTableEveryoneUniqueMsg'] = <<<SQL_QUERY
  154. CREATE DEFINER=`root`@`localhost` PROCEDURE `CRM_UI_MSGS__addTableEveryoneUniqueMsg`(
  155. IN author VARCHAR(40),
  156. IN app_className VARCHAR(255),
  157. IN msgType VARCHAR(16),
  158. IN msg VARCHAR(255),
  159. IN uiTargetName VARCHAR(255)
  160. )
  161. BEGIN
  162. CALL CRM_UI_MSGS__addUniqueMsg(author, app_className, msgType, msg, 'default_db_table', uiTargetName, 'everyone', '');
  163. END
  164. SQL_QUERY;
  165. $formFixZasobPath = <<<SQL_QUERY
  166. CALL CRM_UI_MSGS__addTableEveryoneUniqueMsg(NEW.A_RECORD_UPDATE_AUTHOR, 'FixZasobPath', 'danger', 'Update all paths', 'CRM_LISTA_ZASOBOW');
  167. IF (select count(1) from `CRM_UI_MSGS` where `app_className`='FixZasobPath'
  168. and `msg`='Update all paths'
  169. and `uiTargetType`='default_db_table'
  170. and `uiTargetName`='CRM_LISTA_ZASOBOW'
  171. and `A_STATUS`='WAITING'
  172. ) = 0 THEN
  173. INSERT INTO `CRM_UI_MSGS` (`ID`
  174. , `app_className`, `msg`, `msgType`
  175. , `uiTargetType`, `uiTargetName`
  176. , `userTargetType`
  177. , `A_RECORD_CREATE_DATE`, `A_RECORD_CREATE_AUTHOR`)
  178. VALUES (NULL
  179. , 'FixZasobPath', 'Update all paths', 'danger'
  180. , 'default_db_table', 'CRM_LISTA_ZASOBOW'
  181. , 'everyone'
  182. , NOW(), NEW.A_RECORD_UPDATE_AUTHOR
  183. );
  184. END IF;
  185. SQL_QUERY;
  186. $formFixProjectPath = <<<SQL_QUERY
  187. CALL CRM_UI_MSGS__addTableEveryoneUniqueMsg(NEW.A_RECORD_UPDATE_AUTHOR, 'FixProjectPath', 'danger', 'Update all paths', 'IN7_MK_BAZA_DYSTRYBUCJI');
  188. IF (select count(1) from `CRM_UI_MSGS` where `app_className`='FixProjectPath'
  189. and `msg`='Update all paths'
  190. and `uiTargetType`='default_db_table'
  191. and `uiTargetName`='IN7_MK_BAZA_DYSTRYBUCJI'
  192. and `A_STATUS`='WAITING'
  193. ) = 0 THEN
  194. INSERT INTO `CRM_UI_MSGS` (`ID`
  195. , `app_className`, `msg`, `msgType`
  196. , `uiTargetType`, `uiTargetName`
  197. , `userTargetType`
  198. , `A_RECORD_CREATE_DATE`, `A_RECORD_CREATE_AUTHOR`)
  199. VALUES (NULL
  200. , 'FixProjectPath', 'Update all paths', 'danger'
  201. , 'default_db_table', 'IN7_MK_BAZA_DYSTRYBUCJI'
  202. , 'everyone'
  203. , NOW(), NEW.A_RECORD_UPDATE_AUTHOR
  204. );
  205. END IF;
  206. SQL_QUERY;
  207. foreach ($sqlList as $sqlName => $sql) {
  208. DB::getPDO()->execSql($sql);
  209. }
  210. }
  211. function reinstall() {
  212. $sqlList = array();
  213. //$sqlList['RemoveTable'] = "DROP TABLE IF EXISTS `CRM_UI_MSGS`";// TODO: update struct (add cells, rm old keys) - not drop/create
  214. $sqlList['InstallTable'] = "
  215. CREATE TABLE IF NOT EXISTS `CRM_UI_MSGS` (
  216. `ID` int(11) NOT NULL AUTO_INCREMENT
  217. , `idReplyTo` int(11) NOT NULL DEFAULT 0
  218. , `idThread` int(11) NOT NULL DEFAULT 0
  219. -- app_className - for automatic msgs to search for msg text
  220. , `app_className` varchar(255) DEFAULT NULL
  221. -- msg - msg to show or to parse by app_className
  222. , `msg` varchar(1000) NOT NULL
  223. , `msgType` enum('info','danger','warning','success') NOT NULL DEFAULT 'info'
  224. -- uiTargetType - where to show this msg eg. 'default_db_table', 'after_login', 'everywhere'
  225. , `uiTargetType` enum('default_db_table','default_db_table_record','after_login','everywhere') NOT NULL
  226. -- uiTargetName - eg. database table name (if from default_db)
  227. , `uiTargetName` varchar(255) NOT NULL DEFAULT ''
  228. -- userTargetType - type of users allowed to see this msg eg. 'everyone', 'admin' (ADMIN_LEVEL=0?), 'user', 'group'
  229. , `userTargetType` enum('none','everyone','admin','user','group') NOT NULL DEFAULT 'none'
  230. -- userTargetName - login, group name
  231. , `userTargetName` varchar(255) NOT NULL DEFAULT ''
  232. -- actionExecutedTime - execution time if msg require user to run specific action (send msg.id in request)
  233. , `actionExecutedTime` datetime DEFAULT NULL
  234. -- actionNotes - notes/msgs/dbg for user actions executed from this msg
  235. , `actionNotes` varchar(255) NOT NULL DEFAULT ''
  236. , `A_STATUS` enum('WAITING','NORMAL','OFF_HARD','DELETED') NOT NULL DEFAULT 'WAITING'
  237. , `A_RECORD_CREATE_DATE` datetime DEFAULT NULL
  238. , `A_RECORD_CREATE_AUTHOR` varchar(40) NOT NULL DEFAULT ''
  239. , `A_RECORD_UPDATE_DATE` datetime DEFAULT NULL
  240. , `A_RECORD_UPDATE_AUTHOR` varchar(40) NOT NULL DEFAULT ''
  241. , `A_RECORD_DELETE_DATE` datetime DEFAULT NULL
  242. , `A_RECORD_DELETE_AUTHOR` varchar(40) NOT NULL DEFAULT ''
  243. , PRIMARY KEY (`ID`)
  244. , KEY `app_className` (`app_className`)
  245. ) ENGINE=MyISAM DEFAULT CHARSET=latin2
  246. ";
  247. foreach ($sqlList as $sqlName => $sql) {
  248. DB::getPDO()->execSql($sql);
  249. }
  250. $this->reinstallFunctions();
  251. }
  252. function getActiveMessagesForTable($tblName) {
  253. if (empty($tblName)) return;
  254. $sqlTableName = DB::getPDO()->quote($tblName);
  255. $usrLogin = User::getLogin();
  256. $msgs = [];
  257. $sql = "
  258. select m.*
  259. from `CRM_UI_MSGS` m
  260. where m.`uiTargetType`='default_db_table'
  261. and m.`A_STATUS`='WAITING'
  262. and m.`uiTargetName`={$sqlTableName}
  263. and (m.`userTargetType` in('everyone')
  264. or (m.`userTargetType`='user' and m.`userTargetName`='{$usrLogin}')
  265. -- TODO: use 'admin', 'group'
  266. )
  267. ";
  268. foreach (DB::getPDO()->fetchAll($sql) as $row) {
  269. $r = (object)$row;
  270. if ($msg = $this->parseMessage($r)) {
  271. $msg['link'] = 'index.php?_route=Msgs&_task=run&_msgId=' . $r->ID;
  272. $msg['linkType'] = 'ajax';
  273. $msgs[$r->ID] = $msg;
  274. }
  275. }
  276. return $msgs;
  277. }
  278. function getActiveMessagesForTableRecord($tblName, $id) {
  279. if (empty($tblName)) return;
  280. $usrLogin = User::getLogin();
  281. $msgs = [];
  282. $rawMsgs = DB::getPDO()->fetchAll("
  283. select m.*
  284. from `CRM_UI_MSGS` m
  285. where m.`uiTargetType` = 'default_db_table_record'
  286. and m.`uiTargetName` = :feature_id
  287. and (m.`userTargetType` in('everyone')
  288. or (m.`userTargetType` = 'user' and m.`userTargetName` = '{$usrLogin}')
  289. -- TODO: use group id
  290. )
  291. and m.`A_STATUS` = 'WAITING'
  292. order by m.`ID` DESC
  293. ", [
  294. ':feature_id' => "{$tblName}.{$id}",
  295. ]);
  296. foreach ($rawMsgs as $row) {
  297. $r = (object)$row;
  298. if ($msg = $this->parseMessage($r)) {
  299. $msg['link'] = 'index.php?_route=Msgs&_task=run&_msgId=' . $r->ID;
  300. $msg['linkType'] = 'ajax';
  301. $msgs[$r->ID] = $msg;
  302. }
  303. }
  304. return $msgs;
  305. }
  306. function parseMessage($r) {
  307. $msg = null;// ['type'=>'info', 'message'=>'...']
  308. // $r->app_className - for automatic msgs to search for msg text
  309. // $r->msg - for automatic msgs to search for msg text
  310. // $r->msgType - 'info','danger','warning','success'
  311. if (!empty($r->app_className)) {
  312. $route = Router::getRoute($r->app_className);
  313. $msg = array();
  314. $msg['message'] = $route->parseMessageFromMsgsSystem($r->msg);
  315. $msg['type'] = $r->msgType;
  316. $msg['_raw'] = $r;
  317. } else {
  318. $msg = array();
  319. $msg['message'] = $this->parseMessageFromMsgsSystem($r->msg);
  320. $msg['type'] = $r->msgType;
  321. $msg['_raw'] = $r;
  322. }
  323. return $msg;
  324. }
  325. function parseMessageFromMsgsSystem($msg) {
  326. return $msg;
  327. }
  328. function runByMessageId($id) {
  329. $msgRow = $this->getActiveMessage($id);
  330. $execNotes = '';
  331. if (!empty($msgRow->app_className)) {
  332. $route = Router::getRoute($msgRow->app_className);
  333. $route->runByMessageFromMsgsSystem($msgRow->msg, $execNotes);
  334. }
  335. $this->forceFinishMessage($id, $execNotes);
  336. }
  337. function getMessage($id) {
  338. if (empty($id)) return;
  339. $id = intval($id);
  340. if ($id <= 0) return;
  341. $msg = DB::getPDO()->fetchFirst("select * from `CRM_UI_MSGS` where `ID` = :id ", [ ':id' => $id ]);
  342. return ($msg) ? (object)$msg : null;
  343. }
  344. function getActiveMessage($id) {
  345. if (empty($id)) return;
  346. $id = intval($id);
  347. if ($id <= 0) return;
  348. $msg = DB::getPDO()->fetchFirst("
  349. select m.*
  350. from CRM_UI_MSGS m
  351. where m.ID = :id
  352. and m.A_STATUS = 'WAITING'
  353. ", [
  354. ':id' => $id,
  355. ]);
  356. if (!$msg) throw new HttpException("Message not found", 404);
  357. return $msg;
  358. }
  359. function forceFinishMessage($id, $execNotes) {
  360. if (empty($id)) return;
  361. $id = intval($id);
  362. if ($id <= 0) return;
  363. DB::getPDO()->update('CRM_UI_MSGS', 'ID', $id, [
  364. 'A_STATUS' => "OFF_HARD",
  365. 'actionExecutedTime' => "NOW()",
  366. 'actionNotes' => $execNotes,
  367. 'A_RECORD_UPDATE_DATE' => "NOW()",
  368. 'A_RECORD_UPDATE_AUTHOR' => User::getLogin(),
  369. ]);
  370. }
  371. function removeMessage($id) {
  372. if (empty($id)) return;
  373. $id = intval($id);
  374. if ($id <= 0) return;
  375. DB::getPDO()->execSql(" update `CRM_UI_MSGS` set `A_STATUS`='DELETED' where `ID` = :id ", [ ':id' => $id ]);
  376. }
  377. function removeTableRecordMsg($idMsg) {
  378. // IDEA: do kosza - add trigger to insert into `CRM_UI_MSGS__TRASH` after DELETE on `CRM_UI_MSGS`
  379. $idMsg = intval($idMsg);
  380. if ($idMsg <= 0) throw new Exception("Brak wiadomości!");
  381. $usrLogin = User::getLogin();
  382. $sqlTODO = "delete `CRM_UI_MSGS` where `ID`='{$idMsg}' ";
  383. DB::getPDO()->execSql("
  384. update `CRM_UI_MSGS`
  385. set `A_STATUS` = IF( :login = `A_RECORD_CREATE_AUTHOR`, 'DELETED', 'OFF_HARD')
  386. , `A_RECORD_DELETE_AUTHOR` = :login
  387. , `A_RECORD_DELETE_DATE` = NOW()
  388. where `ID` = :id
  389. ", [
  390. ':id' => $idMsg,
  391. ':login' => $usrLogin,
  392. ]);
  393. }
  394. function restoreTableRecordMsg($idMsg) {
  395. // IDEA: do kosza - add trigger to insert into `CRM_UI_MSGS__TRASH` after DELETE on `CRM_UI_MSGS`
  396. $idMsg = intval($idMsg);
  397. if ($idMsg <= 0) throw new Exception("Brak wiadomości!");
  398. $usrLogin = User::getLogin();
  399. $sqlTODO = "delete `CRM_UI_MSGS` where `ID`='{$idMsg}' ";
  400. DB::getPDO()->execSql("
  401. update `CRM_UI_MSGS`
  402. set `A_STATUS` = 'WAITING'
  403. where `ID` = :id
  404. ", [
  405. ':id' => $idMsg,
  406. ]);
  407. }
  408. }