Auth.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. <?php
  2. Lib::loadClass('RouteBase');
  3. Lib::loadClass('ProcesHelper');
  4. Lib::loadClass('Config');
  5. class Route_Auth extends RouteBase {
  6. public function handleAuth() {
  7. if (!User::logged()) {
  8. $_SESSION['ADM_ACCOUNT'] = 'Anonymous';
  9. // throw new HttpException('Unauthorized', 401);
  10. }
  11. }
  12. public function defaultAction() {
  13. SE_Layout::gora();
  14. if (!User::logged()) {
  15. ?>
  16. <div class="container">
  17. <h1>Unauthorized</h1>
  18. <ul>
  19. <li><a href="index.php?_route=Auth&_task=registerForm">Register</a></li>
  20. <li><a href="index.php?_route=Auth&_task=loginForm">Login</a></li>
  21. <li><a href="index.php?_route=Auth&_task=activateForm">Activate</a></li>
  22. </ul>
  23. </div>
  24. <?php
  25. } else {
  26. ?>
  27. <div class="container">
  28. <h1>Auth</h1>
  29. ...
  30. </div>
  31. <?php
  32. }
  33. SE_Layout::dol();
  34. }
  35. public function loginFormAction() {
  36. SE_Layout::gora();
  37. ?>
  38. TODO: login...
  39. <?php
  40. SE_Layout::dol();
  41. }
  42. public function activateFormAction() {
  43. SE_Layout::gora();
  44. ?>
  45. <div class="container">
  46. <div id="ACTIVATE_FRM_<?php echo $tableHash; ?>_DBG_LOG" style="border:1px solid red;max-height:300px;overflow:scroll"></div>
  47. <div id="ACTIVATE_FRM_<?php echo $tableHash; ?>_MSGS"></div>
  48. <form class="form-horizontal"
  49. action=""
  50. method="post"
  51. id="ACTIVATE_FRM_<?php echo $tableHash; ?>">
  52. <fieldset>
  53. <legend>Aktywacja konta</legend>
  54. <div class="form-group">
  55. <label class="col-xs-12 col-sm-3 col-md-2 control-label" for="activateHash">Kod aktywacyjny
  56. <i class="glyphicon glyphicon-info-sign frm-help"
  57. data-toggle="popover"
  58. data-trigger="hover"
  59. title=""
  60. data-content="Kod aktywacyjny otrzymany drogą mailową"
  61. data-original-title="Kod aktywacyjny"></i>
  62. </label>
  63. <div class="col-xs-12 col-sm-9 col-md-10">
  64. <input id="activateHash" name="activateHash" type="text" value="" tabindex="<?php echo (++$tabindex); ?>" maxlength="100" class="form-control">
  65. </div>
  66. </div>
  67. <div class="form-group">
  68. <div class="col-xs-offset-0 col-xs-12 col-sm-offset-3 col-sm-9 col-md-offset-2 col-md-10">
  69. <button type="submit" class="btn btn-primary" tabindex="<?php echo (++$tabindex); ?>">Aktywuj</button>
  70. </div>
  71. </div>
  72. </fieldset>
  73. </form>
  74. </div>
  75. <?php $this->_printFormFactoryJS(); ?>
  76. <script>
  77. jQuery(document).ready(function(){
  78. var formNode = $('#ACTIVATE_FRM_<?php echo $tableHash; ?>'),
  79. msgsNode = $('#ACTIVATE_FRM_<?php echo $tableHash; ?>_MSGS'),
  80. dbgNode = $('#ACTIVATE_FRM_<?php echo $tableHash; ?>_DBG_LOG'),
  81. form = FormFactory({
  82. formNode: formNode,
  83. msgsNode: msgsNode,
  84. dbgNode: dbgNode,
  85. url: 'index.php?_route=Auth&_task=activate'
  86. })
  87. ;
  88. jQuery('textarea').autosize();
  89. jQuery('.frm-help').popover({trigger:'hover'});
  90. formNode.on('submit', {form: form}, function(e) {
  91. if (e.data && e.data.form) {
  92. e.data.form.submit(e);
  93. }
  94. return false;
  95. });
  96. });
  97. </script>
  98. <?php
  99. SE_Layout::dol();
  100. }
  101. public function registerFormAction() {
  102. SE_Layout::gora();
  103. $tblAcl = $this->_getUsersTableAcl();
  104. $fieldsList = $this->_getRegisterValuesFromArray($_POST);
  105. DBG::_(true, true, "tblAcl", $tblAcl, __CLASS__, __FUNCTION__, __LINE__);
  106. DBG::_(true, true, "fieldsList", $fieldsList, __CLASS__, __FUNCTION__, __LINE__);
  107. $tableHash = 'routeAuthRegister';
  108. ?>
  109. <div class="container">
  110. <div id="CREATE_FRM_<?php echo $tableHash; ?>_DBG_LOG" style="border:1px solid red;max-height:300px;overflow:scroll"></div>
  111. <div id="CREATE_FRM_<?php echo $tableHash; ?>_MSGS"></div>
  112. <form class="form-horizontal"
  113. action=""
  114. method="post"
  115. id="CREATE_FRM_<?php echo $tableHash; ?>">
  116. <fieldset>
  117. <legend>Zarejestruj konto</legend>
  118. <?php $tabindex = 0; foreach ($fieldsList as $kID => $vCol) : ?>
  119. <?php if ($tblAcl->isAllowed($kID, 'C')) : ?>
  120. <div class="form-group">
  121. <label class="col-xs-12 col-sm-3 col-md-2 control-label" for="<?php echo "f{$kID}"; ?>"><?php echo $vCol['label']; ?>
  122. <i class="glyphicon glyphicon-info-sign frm-help" data-toggle="popover" data-trigger="hover" title="" data-content="<?php echo htmlspecialchars($vCol['opis']); ?>" data-original-title="<?php echo "[{$kID}] {$vCol['name']}"; ?>"></i>
  123. <?php $perms = $tblAcl->getFieldPerms($kID); SE_Layout::hotKeyDBG($perms); ?>
  124. </label>
  125. <div class="col-xs-12 col-sm-9 col-md-10">
  126. <?php
  127. $fieldParams = array('appendBack'=>true, 'tabindex'=>(++$tabindex), 'maxGrid'=>8);
  128. echo $tblAcl->showFormItem('C', $kID, "f{$kID}", $vCol['value'], $fieldParams);
  129. ?>
  130. </div>
  131. </div>
  132. <?php endif; ?>
  133. <?php endforeach; ?>
  134. <div class="form-group">
  135. <div class="col-xs-offset-0 col-xs-12 col-sm-offset-3 col-sm-9 col-md-offset-2 col-md-10">
  136. <button type="submit" class="btn btn-primary" tabindex="<?php echo (++$tabindex); ?>">Zarejestruj</button>
  137. </div>
  138. </div>
  139. </fieldset>
  140. </form>
  141. </div>
  142. <?php $this->_printFormFactoryJS(); ?>
  143. <script>
  144. jQuery(document).ready(function(){
  145. var formNode = $('#CREATE_FRM_<?php echo $tableHash; ?>'),
  146. msgsNode = $('#CREATE_FRM_<?php echo $tableHash; ?>_MSGS'),
  147. dbgNode = $('#CREATE_FRM_<?php echo $tableHash; ?>_DBG_LOG'),
  148. form = FormFactory({
  149. formNode: formNode,
  150. msgsNode: msgsNode,
  151. dbgNode: dbgNode,
  152. url: 'index.php?_route=Auth&_task=register'
  153. })
  154. ;
  155. jQuery('textarea').autosize();
  156. jQuery('.frm-help').popover({trigger:'hover'});
  157. formNode.on('submit', {form: form}, function(e) {
  158. if (e.data && e.data.form) {
  159. e.data.form.submit(e);
  160. }
  161. return false;
  162. });
  163. });
  164. </script>
  165. <?php
  166. SE_Layout::dol();
  167. }
  168. public function _printFormFactoryJS() {
  169. ?><script>
  170. var FormFactory = function(options) {
  171. var priv = {};
  172. priv.formNode = options.formNode;
  173. priv.msgsNode = options.msgsNode;
  174. priv.dbgNode = options.dbgNode;
  175. priv.url = options.url;
  176. priv.req = null;
  177. priv.reqCounter = 0;
  178. var showMsg = function(msg) {
  179. $('<div class="alert alert-info">' + msg + '</div>').appendTo(priv.msgsNode);
  180. }
  181. var log = function(msg, type) {
  182. var type = type || 'info';
  183. if (!priv.dbgNode) return false;
  184. $('<div class="alert alert-' + type + '">' + msg + '</div>').appendTo(priv.dbgNode);
  185. }
  186. var showSuccess = function(msg) {
  187. $('<div class="alert alert-success">' + msg + '</div>').appendTo(priv.msgsNode);
  188. }
  189. var showError = function(msg) {
  190. $('<div class="alert alert-danger">' + msg + '</div>').appendTo(priv.msgsNode);
  191. }
  192. var showWarning = function(msg) {
  193. $('<div class="alert alert-warning">' + msg + '</div>').appendTo(priv.msgsNode);
  194. }
  195. var showInfo = function(msg) {
  196. $('<div class="alert alert-info">' + msg + '</div>').appendTo(priv.msgsNode);
  197. }
  198. var clearMsgs = function(msg) {
  199. priv.msgsNode.empty();
  200. }
  201. var hideForm = function() {
  202. priv.formNode.hide();
  203. }
  204. var showForm = function() {
  205. priv.formNode.show();
  206. }
  207. var parseResponse = function(data) {
  208. var form = this,
  209. data = {
  210. type: ('type' in data)? data.type : 'error',
  211. msg: ('msg' in data)? data.msg : 'Nieznany błąd'
  212. }
  213. ;
  214. form.log('parseResponse... data:' + JSON.stringify(data), 'info');
  215. if (data.type == 'error' || data.type == 'warning') {
  216. form.showError('<h4>Wystąpiły błędy!</h4>' + data.msg);
  217. // TODO: show all fields errors
  218. }
  219. else if (data.type == 'success') {
  220. var msg = '';
  221. if (data.id && data.id > 0) {
  222. msg = 'Utworzono pomyślnie konto w systemie';
  223. } else if (data.msg) {
  224. msg = data.msg;
  225. } else {
  226. msg = 'OK';
  227. }
  228. form.showSuccess(msg);
  229. form.hideForm();
  230. } else {
  231. form.showError('<h4>Wystąpiły błędy!</h4>' + data.msg);
  232. }
  233. }
  234. var submit = function(e) {
  235. var data = priv.formNode.serialize(),
  236. form = this,
  237. reqCounter = 0 + priv.reqCounter
  238. ;
  239. e.preventDefault();
  240. priv.reqCounter++;
  241. form.clearMsgs();
  242. form.log('DBG:REQ('+reqCounter+'). sending form...', 'info');
  243. console.log('sending form... data:', data);
  244. if (priv.req) {
  245. form.log('DBG:REQ('+reqCounter+'). abort previous xhr', 'info');
  246. priv.req.abort();
  247. }
  248. priv.req = superagent
  249. .post(priv.url)
  250. .type('form')
  251. .send(data)
  252. .set('Accept', 'application/json')
  253. .end(function(err, res) {
  254. console.log('DBG:REQ('+reqCounter+') res:', res, 'res.body:', res.body);
  255. if (err || !res.ok) {
  256. form.log('DBG:REQ('+reqCounter+'). error', 'info');
  257. } else {
  258. //form.log('DBG:REQ('+reqCounter+'). res.status(' + res.status + ') body:' + JSON.stringify(res.body), 'info');
  259. if ('application/json' !== res.type) {
  260. form.showError('<h4>Wystąpiły błędy!</h4>' + res.text);
  261. return;
  262. }
  263. form.log('DBG:REQ('+reqCounter+'). res.status:' + res.status + '. res.body:' + JSON.stringify(res.body), 'info');
  264. form.parseResponse(res.body);
  265. }
  266. priv.req = null;
  267. });
  268. return false;
  269. }
  270. return {
  271. log: log,
  272. showMsg: showMsg,
  273. showSuccess: showSuccess,
  274. showError: showError,
  275. showWarning: showWarning,
  276. showInfo: showInfo,
  277. clearMsgs: clearMsgs,
  278. hideForm: hideForm,
  279. showForm: showForm,
  280. submit: submit,
  281. parseResponse: parseResponse
  282. };
  283. }
  284. </script>
  285. <?php
  286. }
  287. public function _getRegisterFormFields() {
  288. $formFields = array();
  289. $formFields[] = 'ADM_NAME';// Imię i nazwisko
  290. $formFields[] = 'EMAIL';// Adres e-mail
  291. $formFields[] = 'ADM_PASSWD';// Hasło
  292. // Potwierdź hasło
  293. // TODO: stanowisko - if allowed (TODO: in config? '.cnf--auth-{host}.ini.php')
  294. return $formFields;
  295. }
  296. public function _getUsersTableAcl() {
  297. static $_usersTblAcl = null;
  298. if (!$_usersTblAcl) $_usersTblAcl = $this->_fetchUsersTableAcl();
  299. if (!$_usersTblAcl) throw new Exception("Error No Table ACL!");
  300. return $_usersTblAcl;
  301. }
  302. public function _fetchUsersTableAcl() {
  303. $tableName = 'TEST_PERMS';//'ADMIN_USERS';
  304. $formFields = $this->_getRegisterFormFields();
  305. $overrideLabels = array();
  306. $overrideLabels['ADM_NAME'] = 'Imię i nazwisko';
  307. $overrideLabels['EMAIL'] = "Email";
  308. $overrideLabels['ADM_PASSWD'] = "Hasło";
  309. $idTable = ProcesHelper::getZasobTableID($tableName);
  310. if (!$idTable) throw new Exception("Brak id tabeli");
  311. //DBG::_(true, true, "idTable", $idTable, __CLASS__, __FUNCTION__, __LINE__);
  312. $userAcl = User::getAcl();
  313. //DBG::_(true, true, "userAcl", $userAcl, __CLASS__, __FUNCTION__, __LINE__);
  314. if (!$userAcl->hasTableAcl($idTable)) {
  315. // .cnf--auth-{host}.ini.php
  316. $conf = Config::getConfFile('auth');
  317. if (!$conf) throw new Exception("Config file for 'auth' not found!");
  318. $isRegisterAllowed = V::get('allow_register', false, $conf);
  319. //DBG::_(true, true, "conf (isRegisterAllowed={$isRegisterAllowed})", $conf, __CLASS__, __FUNCTION__, __LINE__);
  320. if (!$isRegisterAllowed) throw new Exception("Brak uprawnień do rejestracji");
  321. $zasobTblInfo = ProcesHelper::getZasobTableInfoByUri($tblUri = "default_db/{$tableName}");
  322. if (!$zasobTblInfo) throw new HttpException("Brak zasobu dla tabeli użytkowników", 404);
  323. //DBG::_(true, true, "zasobTblInfo", $zasobTblInfo, __CLASS__, __FUNCTION__, __LINE__);
  324. {
  325. $tableConfig = array();
  326. $tableConfig['ID_TABLE'] = $idTable;
  327. $tableConfig['db'] = $zasobTblInfo->P__ID;
  328. $tableConfig['name'] = $zasobTblInfo->DESC;
  329. $tableConfig['label'] = $zasobTblInfo->DESC_PL;
  330. $tableConfig['opis'] = $zasobTblInfo->OPIS;
  331. //DBG::_(true, true, "formFields", $formFields, __CLASS__, __FUNCTION__, __LINE__);
  332. //DBG::_(true, true, "tableConfig", $tableConfig, __CLASS__, __FUNCTION__, __LINE__);
  333. }
  334. {
  335. $fieldsConfig = array();
  336. $fldsInfo = ProcesHelper::getZasobTableFieldsInfo($idTable);
  337. //DBG::_(true, true, "fldsInfo", $fldsInfo, __CLASS__, __FUNCTION__, __LINE__);
  338. foreach ($fldsInfo as $fldInfo) {
  339. if (!in_array($fldInfo->DESC, $formFields)) continue;
  340. $fldConf = array();
  341. $fldConf['ID_CELL'] = $fldInfo->ID;
  342. $fldConf['CELL_NAME'] = $fldInfo->DESC;
  343. $fldConf['CELL_DESC'] = (array_key_exists($fldInfo->DESC, $overrideLabels))? $overrideLabels[$fldInfo->DESC] : $fldInfo->OPIS;
  344. $fldConf['CELL_LABEL'] = (array_key_exists($fldInfo->DESC, $overrideLabels))? $overrideLabels[$fldInfo->DESC] : $fldInfo->DESC_PL;
  345. $fldConf['SORT_PRIO'] = $fldInfo->SORT_PRIO;
  346. if ('ADM_PASSWD' == $fldInfo->DESC) {
  347. $fldConf['FORM_TREAT'] = 'WXC';
  348. } else {
  349. $fldConf['FORM_TREAT'] = 'RWXC';
  350. }
  351. $fieldsConfig[$fldInfo->ID] = $fldConf;
  352. }
  353. //DBG::_(true, true, "fieldsConfig", $fieldsConfig, __CLASS__, __FUNCTION__, __LINE__);
  354. }
  355. {// TODO: init and save in session default perms
  356. //DBG::_(true, true, "ses tbl cache[{$idTable}]", $_SESSION['TableAcl_cache'][$idTable], __CLASS__, __FUNCTION__, __LINE__);
  357. $tblAcl = TableAcl::buildInstance($idTable, $tableConfig);
  358. $tblAcl->initFieldsFromConfig($fieldsConfig);
  359. $tblAcl->save();
  360. //DBG::_(true, true, "ses tbl cache[{$idTable}]", $_SESSION['TableAcl_cache'][$idTable], __CLASS__, __FUNCTION__, __LINE__);
  361. }
  362. //throw new Exception("Brak uprawnień do tabeli ID={$idTable}");
  363. }
  364. //DBG::_(true, true, "_SESSION['UserAcl_cache']['foundTables']", $_SESSION['UserAcl_cache']['foundTables'], __CLASS__, __FUNCTION__, __LINE__);
  365. //$tblAcl = $userAcl->getTableAcl($idTable);
  366. if (!$tblAcl) throw new Exception("Brak tabeli");
  367. $tblAcl->init();
  368. //DBG::_(true, true, "tblAcl", $tblAcl, __CLASS__, __FUNCTION__, __LINE__);
  369. return $tblAcl;
  370. }
  371. public function _getRegisterValuesFromArray($args = array()) {
  372. $fieldsList = array();
  373. $tblAcl = $this->_getUsersTableAcl();
  374. $formFields = $this->_getRegisterFormFields();
  375. $fieldsListAll = $tblAcl->getFields();
  376. foreach ($formFields as $vColName) {
  377. $vColID = $tblAcl->getFieldIdByName($vColName);
  378. if (!isset($fieldsListAll[$vColID])) {
  379. throw new Exception("Brak uprawnień do pola '{$vColName}'");
  380. }
  381. $fieldsList[$vColID] = $fieldsListAll[$vColID];
  382. $value = '';
  383. $value = V::get("f{$vColID}", $value, $args);
  384. $fieldsList[$vColID]['value'] = $value;
  385. }
  386. return $fieldsList;
  387. }
  388. public function registerAction() {
  389. $args = $_POST;
  390. $resData = new stdClass();
  391. $resData->_args = $args;// TODO: DBG
  392. $resData->type = 'error';
  393. $resData->msg = 'Error';
  394. header('Content-type: application/json; charset="utf-8"');
  395. try {
  396. $itemData = array();
  397. $tblAcl = $this->_getUsersTableAcl();
  398. $fieldsList = $this->_getRegisterValuesFromArray($args);
  399. $ds = $tblAcl->getDataSource();
  400. foreach ($fieldsList as $idFld => $fldData) {
  401. $itemData[$fldData['name']] = $fldData['value'];
  402. }
  403. if (!filter_var($itemData['EMAIL'], FILTER_VALIDATE_EMAIL)) {
  404. throw new Exception("Proszę podać poprawny adres email");
  405. }
  406. $itemData['EMPLOYEE_TYPE'] = 'Anonymous';
  407. $itemData['A_STATUS'] = 'WAITING';// TODO: konto oczekuje aktywacji? z config
  408. //$itemData['ADM_ADMIN_LEVEL'] = '6';
  409. $itemData['A_AUTH_TOKEN'] = md5("ADMIN_USERS.auth_token." . time());
  410. $itemData['A_AUTH_TOKEN_VALID'] = date("Y-m-d H:i:s", mktime(6 + date('H'), date('i'), date('s'), date("m"), date("d"), date("Y")));
  411. $resData->userName = User::getName();
  412. $resData->id = $ds->addItem($itemData);
  413. {
  414. $resData->_fieldsList = $fieldsList;// TODO: DBG
  415. $resData->_tblAcl = $tblAcl;// TODO: DBG
  416. $resData->_itemData = $itemData;// TODO: DBG
  417. }
  418. if ($resData->id > 0) {
  419. $resData->type = 'success';
  420. $resData->msg = "Pomyślnie utworzono konto w systemie";
  421. // TODO: konto oczekuje aktywacji? z config
  422. }
  423. } catch (Exception $e) {
  424. $resData->type = 'error';
  425. $resData->msg = $e->getMessage();
  426. }
  427. echo json_encode($resData);
  428. }
  429. public function activateAction() {
  430. $args = $_REQUEST;
  431. $resData = new stdClass();
  432. $resData->_args = $args;// TODO: DBG
  433. $resData->type = 'error';
  434. $resData->msg = 'Error';
  435. header('Content-type: application/json; charset="utf-8"');
  436. try {
  437. $token = V::get('activateHash', '', $args);
  438. // e3360132c963ae5b21ff6de77bcbbed6
  439. if (32 != strlen($token)) throw new Exception("Wrong token");
  440. if (!preg_match('/^[a-fA-F0-9]+$/', $token)) throw new Exception("Wrong token");
  441. $tblAcl = $this->_getUsersTableAcl();
  442. $ds = $tblAcl->getDataSource();
  443. $tblName = $tblAcl->getName();
  444. $dbs = DB::getDataSource();
  445. $sqlToken = $dbs->_($token);
  446. $sql = "
  447. select t.`ID`
  448. from `{$tblName}` t
  449. where t.`A_AUTH_TOKEN`='{$sqlToken}'
  450. and t.`A_AUTH_TOKEN_VALID` is not null
  451. and t.`A_AUTH_TOKEN_VALID` > NOW()
  452. and t.`A_STATUS`='WAITING'
  453. ";
  454. //DBG::_(true, true, "sql", $sql, __CLASS__, __FUNCTION__, __LINE__);
  455. $items = $dbs->getListByQuery($sql);
  456. if (0 == count($items)) throw new Exception("Kod nieaktywny");
  457. $idUser = V::get('ID', 0, reset($items), 'int');
  458. if ($idUser <= 0) throw new Exception("Kod nieaktywny");
  459. //DBG::_(true, true, "items", $items, __CLASS__, __FUNCTION__, __LINE__);
  460. $itemData = array();
  461. $itemData['ID'] = $idUser;
  462. $itemData['A_STATUS'] = 'NORMAL';
  463. $itemData['A_AUTH_TOKEN_VALID'] = 'NULL';
  464. $ds->updateItem($itemData);
  465. {
  466. $resData->_fieldsList = $fieldsList;// TODO: DBG
  467. $resData->_tblAcl = $tblAcl;// TODO: DBG
  468. $resData->_itemData = $itemData;// TODO: DBG
  469. }
  470. $resData->type = 'success';
  471. $resData->msg = "Pomyślnie aktywowano konto w systemie";
  472. } catch (Exception $e) {
  473. $resData->type = 'error';
  474. $resData->msg = $e->getMessage();
  475. $resData->errorCode = $e->getLine();
  476. }
  477. echo json_encode($resData);
  478. }
  479. }