Ant.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. <?php
  2. Lib::loadClass('RouteBase');
  3. Lib::loadClass('UI');
  4. Lib::loadClass('DBG');
  5. Lib::loadClass('Request');
  6. Lib::loadClass('Response');
  7. Lib::loadClass('Crypt');
  8. Lib::loadClass('Route_Ant');
  9. class Route_UrlAction_Ant extends Route_Ant {// @doc @see Route_Ant
  10. public function handleAuth() {
  11. if (!User::logged()) {
  12. User::authByRequest();
  13. }
  14. session_write_close();
  15. }
  16. public function defaultAction() {
  17. UI::gora();
  18. UI::menu();
  19. UI::startContainer();
  20. echo UI::h('div', [ 'class' => "pull-right" ], [
  21. UI::h('a', [
  22. 'href' => $this->getLink('listLastJobs'),
  23. 'class' => "btn btn-link",
  24. ], "Ostatnio uruchione"),
  25. UI::hButtonPost("Odśwież", [
  26. 'class' => "btn btn-link",
  27. // 'form.class' => "pull-right",
  28. 'title' => "Wczytaj ponownie listę dostępnych funkcji",
  29. 'data' => [
  30. 'forceUpdate' => '1',
  31. ]
  32. ]),
  33. ]);
  34. echo UI::h('h1', [], 'Druki');
  35. UI::tryCatchView([ $this, 'listAntView' ]);
  36. UI::endContainer();
  37. UI::dol();
  38. }
  39. function listAntView() {
  40. $taskList = $this->getAntUrlActionList();
  41. // $featureID = V::get('featureID', '', $_GET);
  42. $primaryKey = V::get('primaryKey', '', $_GET, 'word');
  43. $typeName = V::get('typeName', '', $_GET, 'word');
  44. if (empty($typeName)) throw new Exception("Missing typeName");
  45. if (empty($primaryKey)) throw new Exception("Missing primaryKey");
  46. $parsedInfo = Core_AclHelper::parseTypeName($typeName);
  47. DBG::log($parsedInfo, '', "Ant parseTypeName({$typeName})");
  48. $acl = ACL::getAclByNamespace("{$parsedInfo['url']}/{$parsedInfo['name']}");
  49. $pkField = $acl->getPrimaryKeyField();
  50. echo UI::h('p', [ 'class' => "text-muted" ], [
  51. "Druki dla rekordu '{$primaryKey}' w tabeli ",
  52. UI::h('a', [
  53. 'href' => Router::getRoute('ViewTableAjax')->getLink('', [ 'namespace' => $acl->getNamespace() ])
  54. ], $acl->getRawLabel()),
  55. ]);
  56. UI::inlineJS( __FILE__ . '.async.js', [ // p5UI_runAntAsyncJob
  57. 'RUN_ANT_JOB_URL' => $this->getLink('startAntJobAjax'),
  58. 'RUN_TEST_ANT_JOB_URL' => $this->getLink('startTestAntJobAjax'),
  59. ]);
  60. // echo UI::h('div', [ 'style' => "padding-bottom:8px; border-bottom:1px solid #eee;" ], [
  61. // UI::h('select', [ 'class' => "form-control" ], array_map(function ($label, $path) {
  62. // return UI::h('option', [ 'value' => $path ], $label);
  63. // }, $taskList, array_keys($taskList))),
  64. // ]);
  65. UI::startTag('ul');
  66. foreach ($taskList as $path => $label) {
  67. echo UI::h('li', [], [
  68. UI::h('a', [
  69. 'class' => 'btn btn-md btn-link',
  70. 'href' => $this->getLink('ant', [
  71. 'path' => $path,
  72. 'typeName' => $typeName,
  73. 'primaryKey' => $primaryKey,
  74. 'primaryKeyField' => $pkField
  75. ])
  76. ], "Uruchom '{$label}'")
  77. ]);
  78. $tmpls = $this->getAntUrlActionTemplates($path);
  79. if (!empty($tmpls)) {
  80. foreach ($tmpls as $tmpl => $labelTmpl) {
  81. echo UI::h('div', [ 'style' => "padding-left: 50px" ], [
  82. UI::h('a', [
  83. 'href' => $this->getLink('ant', [
  84. 'path' => $path,
  85. 'template' => $tmpl,
  86. 'typeName' => $typeName,
  87. 'primaryKey' => $primaryKey,
  88. 'primaryKeyField' => $pkField
  89. ]),
  90. 'class' => 'btn btn-sm btn-link',
  91. ], '<i class="glyphicon glyphicon-arrow-right"></i> ' . $labelTmpl),
  92. " ",
  93. UI::h('a', [
  94. 'href' => $this->getLink('ant', [
  95. 'path' => $path,
  96. 'template' => $tmpl,
  97. 'typeName' => $typeName,
  98. 'primaryKey' => $primaryKey,
  99. 'primaryKeyField' => $pkField
  100. ]),
  101. 'onclick' => 'return p5UI_runAntAsyncJob(event, this)',
  102. 'data-namespace' => $acl->getNamespace(),
  103. 'data-primaryKey' => $primaryKey,
  104. 'data-ant_path' => $path,
  105. 'data-ant_template' => $tmpl,
  106. 'class' => 'btn btn-xs btn-default',
  107. 'title' => "Uruchom asynchronicznie",
  108. ], "uruchom"),
  109. " ",
  110. // UI::h('a', [
  111. // 'href' => $this->getLink('ant', [
  112. // 'path' => $path,
  113. // 'template' => $tmpl,
  114. // 'typeName' => $typeName,
  115. // 'primaryKey' => $primaryKey,
  116. // 'primaryKeyField' => $pkField
  117. // ]),
  118. // 'onclick' => 'return p5UI_runAntAsyncJob(event, this, { TEST: 1 })',
  119. // 'data-namespace' => $acl->getNamespace(),
  120. // 'data-primaryKey' => $primaryKey,
  121. // 'data-ant_path' => $path,
  122. // 'data-ant_template' => $tmpl,
  123. // 'class' => 'btn btn-xs btn-link',
  124. // 'title' => "TEST Uruchom asynchronicznie",
  125. // ], "test"),
  126. ]);
  127. }
  128. }
  129. }
  130. UI::endTag('ul');
  131. }
  132. function listLastJobsAction() { UI::layout([ get_called_class(), 'listLastJobsView' ]); }
  133. static function listLastJobsView() {
  134. echo UI::h('ol', [ 'class' => "breadcrumb" ], [
  135. UI::h('li', [], [
  136. UI::h('a', [ 'href' => self::link() ], [
  137. UI::h('i', [ 'class' => "glyphicon glyphicon-home", 'style' => "margin-right:6px" ]), // TODO: mv to h('p5:icon', [ 'type' => "home" ])
  138. " ",
  139. "Druki",
  140. ]),
  141. ]),
  142. UI::h('li', [], [
  143. "Ostatnio uruchomione zadania",
  144. ]),
  145. ]);
  146. echo UI::h('h1', [], "Ostatnio uruchomione zadania");
  147. $postTask = V::get('postTask', '', $_POST);
  148. if ('asyncJobStart' === $postTask) UI::tryCatchView([ get_called_class(), 'asyncJobStartPostTask' ], [ 'args' => $_POST ]);
  149. UI::table([
  150. 'disable_lp' => true,
  151. 'rows' => array_map(function ($item) {
  152. return array_merge(
  153. [
  154. '#' => '',
  155. 'Nr' => '',
  156. 'Status' => '',
  157. ],
  158. $item,
  159. [
  160. '#' => self::jobListItem_Akcje($item),
  161. 'Nr' => $item['ID'],
  162. 'Status' => self::jobListItem_Status($item['A_STATUS'], $item),
  163. ]
  164. );
  165. }, self::fetchUserLastAsyncJobs()),
  166. ]);
  167. throw new Exception("TODO: list last jobs");
  168. }
  169. static function fetchUserLastAsyncJobs() {
  170. return DB::getPDO()->fetchAll("
  171. select j.*
  172. from CRM_ASYNC_JOB_LOG j
  173. where j.USER = :user
  174. order by ID DESC
  175. ", [
  176. ':user' => User::getLogin(),
  177. ]);
  178. }
  179. static function jobListItem_Akcje($item) {
  180. return UI::h('div', [ 'class' => "btn-group" ], [
  181. UI::h('button', [
  182. // 'type' => "button",
  183. 'class' => "btn btn-xs btn-default dropdown-toggle",
  184. 'data-toggle' => "dropdown",
  185. ], [
  186. // UI::h('i', [ 'class' => "glyphicon glyphicon-hamburder" ])
  187. 'Akcje ',
  188. UI::h('span', [ 'class' => "caret" ]),
  189. ]),
  190. UI::h('ul', [ 'class' => "dropdown-menu" ], [
  191. UI::h('li', [], [
  192. UI::h('a', [
  193. 'href' => self::link('asyncJobDebug', [ 'id' => $item['ID'] ]),
  194. ], "Debug Info '{$item['ID']}'"),
  195. ]),
  196. UI::h('li', [], [
  197. UI::hButtonPost("Uruchom zadanie '{$item['ID']}'", [
  198. 'class' => "p5-hover",
  199. 'style' => "padding:3px 20px",
  200. // 'form.class' => "pull-right",
  201. // 'title' => "",
  202. 'data' => [
  203. 'postTask' => 'asyncJobStart',
  204. 'id' => $item['ID'],
  205. ]
  206. ]),
  207. ]),
  208. // <li role="separator" class="divider"></li>
  209. ]),
  210. ]);
  211. }
  212. static function jobListItem_Status($status, $item) {
  213. switch ($status) {
  214. case 'WAITING': return UI::h(null, null, [
  215. "Oczekujący",
  216. ]);
  217. case 'NORMAL': return UI::h(null, null, [
  218. "Uruchomiony",
  219. ]);
  220. case 'OFF_HARD': return UI::h(null, null, [
  221. "Zakończony",
  222. ]);
  223. default: return UI::h(null, null, $status);
  224. }
  225. }
  226. static function asyncJobStartPostTask($args) {
  227. DBG::nicePrint($args, 'DBG: $args');
  228. $idJob = V::get('id', 0, $args, 'int');
  229. if ($idJob <= 0) throw new Exception("Missing id");
  230. Lib::loadClass('Core_AsyncJobs');
  231. Core_AsyncJobs::startAsyncJob($idJob);
  232. throw new Exception("TODO: asyncJobStartPostTask");
  233. }
  234. function asyncJobDebugAction() { UI::layout([ $this, 'asyncJobDebugView' ]); }
  235. function asyncJobDebugView() {
  236. echo UI::h('ul', [ 'class' => "breadcrumb" ], [
  237. UI::h('li', [], [
  238. UI::h('a', [ 'href' => $this->getLink('listLastJobs') ], "Ostatnio uruchomione zadania"),
  239. ]),
  240. UI::h('li', [ 'class' => "active" ], [
  241. "Zadanie - Debug",
  242. ]),
  243. ]);
  244. $idJob = V::get('id', 0, $_GET, 'int');
  245. if ($idJob <= 0) throw new Exception("Missing job ID");
  246. $item = DB::getPDO()->fetchFirst("
  247. select j.ID
  248. -- , j.ID_FUNCTION
  249. -- , j.ID_SOURCE_JOB
  250. -- , j.P_ID
  251. , j.DATE -- 2020-02-26
  252. -- , j.VERSION
  253. , j.JOB_NAME -- Ant|default_db/IN7_DZIENNIK_KORESP|default_db.in7_dziennik_koresp/test-async|test-loop
  254. , j.LOCK_VALUE -- 66263
  255. -- , j.USER
  256. , j.A_STATUS
  257. , j.A_RECORD_CREATE_DATE
  258. , j.A_RECORD_CREATE_AUTHOR
  259. , j.A_RECORD_UPDATE_DATE
  260. , j.A_RECORD_UPDATE_AUTHOR
  261. from CRM_ASYNC_JOB_LOG j
  262. where j.USER = :user
  263. and j.ID = :id
  264. ", [
  265. ':id' => $idJob,
  266. ':user' => User::getLogin(),
  267. ]);
  268. if (!$item) throw new Exception("Zadanie nie istnieje");
  269. DBG::nicePrint($item, "Zadanie '{$idJob}'");
  270. // TODO: files list in task folder
  271. Lib::loadClass('Core_AsyncJobsFiles');
  272. $basePath = Core_AsyncJobsFiles::basePath($item['ID'], $item['DATE']);
  273. $propertyFile = Core_AsyncJobsFiles::propertyFile($item['ID'], $item['DATE']);
  274. $startScript = Core_AsyncJobsFiles::startScript($item['ID'], $item['DATE']);
  275. $outLog = Core_AsyncJobsFiles::outLog($item['ID'], $item['DATE']);
  276. $errorLog = Core_AsyncJobsFiles::errorLog($item['ID'], $item['DATE']);
  277. DBG::nicePrint([
  278. 'basePath' => $basePath,
  279. 'propertyFile' => $propertyFile,
  280. 'startScript' => $startScript,
  281. 'outLog' => $outLog,
  282. 'errorLog' => $errorLog,
  283. ], "DBG:job files");
  284. DBG::nicePrint(shell_exec("ls -l '{$basePath}'"), 'DBG: basePath');
  285. DBG::nicePrint(shell_exec("cat '{$propertyFile}'"), 'DBG: propertyFile');
  286. DBG::nicePrint(shell_exec("cat '{$startScript}'"), 'DBG: startScript');
  287. DBG::nicePrint(shell_exec("cat '{$outLog}'"), 'DBG: outLog');
  288. DBG::nicePrint(shell_exec("cat '{$errorLog}'"), 'DBG: errorLog');
  289. throw new Exception("TODO: asyncJobDebug");
  290. }
  291. public function outputAction() {// index.php?_route=UrlAction_Ant&_task=output & path={$path} & file={$file}";
  292. $path = V::get('path', '', $_GET);
  293. if (!$path) throw new Exception("Missing Ant path!");
  294. $file = V::get('file', '', $_GET);
  295. if (!$file) throw new Exception("Missing Ant file!");
  296. // TODO: allow auth by token?
  297. throw new Exception("TODO: return output file '$file' from ant task '{$path}'");
  298. }
  299. function startTestAntJobAjaxAction() { Response::sendTryCatchJson([ $this, 'startTestAntJobAjax' ], $args = 'JSON_FROM_REQUEST_BODY'); }
  300. function startTestAntJobAjax($args) {
  301. sleep(2);
  302. $ret = rand(0, 1);
  303. switch ($ret) {
  304. case 1: return [
  305. 'type' => "success",
  306. 'msg' => "Dodano zadanie",
  307. 'body' => [
  308. 'id_job' => 123,
  309. ],
  310. ];
  311. // default: throw new Exception("TODO:startAntJobAjax");
  312. default: return [
  313. 'type' => "error",
  314. 'msg' => "Wystąpił błąd podczas dodawania zadania",
  315. ];
  316. }
  317. }
  318. function startAntJobAjaxAction() { Response::sendTryCatchJson([ $this, 'startAntJobAjax' ], $args = 'JSON_FROM_REQUEST_BODY'); }
  319. function startAntJobAjax($args) {
  320. // $args = body: JSON.stringify({
  321. // namespace: "default_db/IN7_DZIENNIK_KORESP"
  322. // primaryKey: "66263"
  323. // ant_path: "default_db.in7_dziennik_koresp/test-bash"
  324. // ant_template: "test-loop"
  325. // })
  326. $namespace = V::get('namespace', '', $args);
  327. $primaryKey = V::get('primaryKey', '', $args);
  328. $ant_path = V::get('ant_path', '', $args);
  329. $ant_template = V::get('ant_template', '', $args);
  330. if (empty($namespace)) throw new Exception("Missing namespace");
  331. if (empty($primaryKey)) throw new Exception("Missing primaryKey");
  332. if (empty($ant_path)) throw new Exception("Missing ant_path");
  333. if (empty($ant_template)) throw new Exception("Missing ant_template");
  334. // TODO: convert antAction to async mode
  335. // - register async job or get jobID
  336. // - if exists: show last call info (result, when, etc.) + run again btn
  337. // - if not exists: create new folder with base files, create exec script, run this script in pm2 start
  338. // - parse result into AsyncJob/output and AsyncJob/output_type
  339. Lib::loadClass('Core_AsyncJobs');
  340. $idJob = Core_AsyncJobs::registerNewAntTask([
  341. 'namespace' => $namespace,
  342. 'primaryKey' => $primaryKey,
  343. 'ant_path' => $ant_path,
  344. 'ant_template' => $ant_template,
  345. ]);
  346. if (!$idJob) throw new Exception("Wystąpił błąd podczas dodawania zadania");
  347. return [
  348. 'type' => "success",
  349. 'msg' => "Dodano zadanie",
  350. '__DBG__args' => $args,
  351. ];
  352. }
  353. public function antAction() {
  354. UI::gora();
  355. UI::startContainer();
  356. try {
  357. $path = V::get('path', '', $_GET);
  358. if (!$path) throw new Exception("Missing Ant path!");
  359. $typeName = V::get('typeName', '', $_GET);
  360. if (!$typeName) throw new Exception("Missing typeName");
  361. $primaryKey = V::get('primaryKey', '', $_GET);
  362. if (!$primaryKey) throw new Exception("Missing primaryKey");
  363. $pkField = V::get('primaryKeyField', '', $_GET);
  364. if (!$pkField) throw new Exception("Missing primaryKeyField");
  365. // list($nsPrefix, $objectName) = explode(':', $typeName);// TODO: get wfs prefix from typeName
  366. $parsedInfo = Core_AclHelper::parseTypeName($typeName);
  367. DBG::log($parsedInfo, '', "Ant parseTypeName({$typeName})");
  368. $nsPrefix = $parsedInfo['prefix'];
  369. $objectName = $parsedInfo['name'];
  370. $template = V::get('template', '', $_GET);
  371. $confirmAntfile = V::get('confirmAntfile', '', $_GET);
  372. $confirmAntfileTarget = V::get('confirmAntfileTarget', '', $_GET);
  373. // TODO: validate missing ...
  374. DBG::log($_GET, 'array', '$_GET');
  375. DBG::log([ 'msg'=>'$path', '$path'=>$path]);
  376. DBG::log([ 'msg'=>'$primaryKey', '$primaryKey'=>$primaryKey]);
  377. DBG::log([ 'msg'=>'$typeName', '$typeName'=>$typeName]);
  378. $taskList = $this->getAntUrlActionList();
  379. if (!array_key_exists($path, $taskList)) throw new Exception("Ant path not exists! '{$path}'");
  380. echo UI::h('h3', [], [
  381. "Druk",
  382. '<br>',
  383. UI::h('small', [], $taskList[$path])
  384. ]);
  385. echo UI::h('hr');
  386. $webRootUrl = Request::getPathUri() . "schema/ant-url_action/{$path}";// TODO: security - only for test
  387. $outputFunctionUrl = $this->getLink('output') . "&path={$path}&file=";
  388. $antFunctionUrl = $this->getLink('ant') . "&path={$path}&file={$file}&template={$template}&typeName={$typeName}&primaryKey={$primaryKey}&primaryKeyField={$pkField}"; // &confirmAntfile={$confirmAntfile}&confirmAntfileTarget={$confirmAntfileTarget} sdo confirmacji potrzebne - wzglednie przetwarzac z tresci output URL_TASK - ale potrzebne parametry analogiczne aby chodzily
  389. DBG::log([ 'msg'=>'$webRootUrl', '$webRootUrl'=>$webRootUrl]);
  390. DBG::log([ 'msg'=>'$outputFunctionUrl', '$outputFunctionUrl'=>$outputFunctionUrl]);
  391. $testUrl = Request::getPathUri() . "wfs-data.php/default_db/?SERVICE=WFS&VERSION=1.0.0&TYPENAME={$typeName}&SRSNAME=EPSG:3003&featureID={$objectName}.{$primaryKey}";// &REQUEST=GetFeature
  392. // $testUrl = Request::getPathUri() . "wfs-data.php" ;// &REQUEST=GetFeature
  393. $cryptedPass = base64_encode(User::getLogin() . ":" . Crypt::decrypt($_SESSION['ADM_PASS_HASH']));
  394. $uniqID = date("Y-m-d-H_i_s") . substr(md5(time()), 0, 6);// TODO: uniq id for every request
  395. // $cmd = "{$this->pathUrlActions}/{$path}/do_build.sh";
  396. $cmd = "cd {$this->pathUrlActions}/{$path} && ant -S";
  397. // ant -silent, -S print nothing but task outputs and build failures
  398. // ant -D<property>=<value> use value for given property
  399. // ant -propertyfile <name> load all properties from file with -D properties taking precedence
  400. // TODO: mv to build.properties file @see https://dzone.com/tutorials/java/ant/ant-properties-file.html
  401. // ant -propertyfile build.properties
  402. $cmd .= " -DoutputFunctionUrl='{$outputFunctionUrl}'";
  403. $cmd .= " -DantFunctionUrl='{$antFunctionUrl}'";
  404. if( strlen($confirmAntfile) > 0 ) $cmd .= " -DconfirmAntfile={$confirmAntfile}";
  405. if( strlen($confirmAntfileTarget) > 0 ) $cmd .= " -DconfirmAntfileTarget={$confirmAntfileTarget}";
  406. $cmd .= " -DwebRootUrl='{$webRootUrl}'";
  407. // $cmd .= " -Durl='{$testUrl}'"; nie mozna tego uzywac, bo nadpisuje property w funkcji build_get_wfs.xml
  408. $cmd .= " -DpasswordBase64Basic=\"{$cryptedPass}\"";
  409. $cmd .= " -Duuid=\"{$uniqID}\"";
  410. $cmd .= " -Dphp_session_id=\"" . session_id() . "\"";
  411. $cmd .= " -Dxpath={$pkField}";
  412. $cmd .= " -Dxpath_value={$primaryKey}";
  413. $cmd .= " -Dtemplate={$template}";
  414. $cmd .= " -Dapi_url=".Request::getPathUri()."wfs-data.php"; //potrzebuje ten parametr do dzialania w AMS itp
  415. $cmd .= " -DtypeName=\"{$typeName}\"";
  416. if ($template) $cmd .= " {$template} ";
  417. $cmd .= " 2>&1";
  418. $pathCmd = [];
  419. $pathCmd[] = '/opt/local/bin/ant';
  420. $pathCmd[] = '/bin';
  421. $pathCmd[] = '/usr/bin';
  422. $pathCmd[] = '/usr/local/bin';
  423. $pathCmd[] = '/opt/local/bin';
  424. $pathCmd[] = '/sbin';
  425. $pathCmd[] = '/usr/sbin';
  426. $pathCmd[] = '/opt/local/sbin/skrypty';
  427. $pathCmd[] = '/opt/local/var/macports/software';
  428. $pathCmd[] = '/Applications/Server.app/Contents/ServerRoot/usr/bin';
  429. $pathCmd[] = '/Applications/Server.app/Contents/ServerRoot/usr/sbin';
  430. $cmd = 'export PATH=' . implode(':', $pathCmd) . "\n" .
  431. 'JAVA_HOME="/usr/bin/java"' . "\n" .
  432. 'MAVEN_OPTS="-Xms256m -Xmx512m"' . "\n" .
  433. $cmd;
  434. $useShellExec = true;
  435. if (User::isAdmin()) {
  436. echo UI::h('details', [], [
  437. UI::h('summary', [ 'style' => "padding:3px 8px;border:1px solid #ccc;background-color:#282c34;color:#de6b74;font-size:small" ], "DBG: command"),
  438. UI::h('div', [ 'style' => "padding:12px; background:#282c34;" ], [
  439. UI::h('pre', [], $cmd),
  440. ]),
  441. ]);
  442. }
  443. if ($useShellExec) $out = shell_exec($cmd);
  444. else V::exec($cmd, $out, $ret);
  445. DBG::log([ 'msg'=>"cmd and returns({$ret})", 'output'=>$out, 'cmd'=>str_replace(APP_PATH_ROOT, 'SE', $cmd) ]);
  446. if (empty($out)) throw new Exception("Empty output");
  447. if ($useShellExec) $out = explode("\n", $out);
  448. $outputType = 'HTML';
  449. $html = []; $startRead = false;
  450. foreach ($out as $line) {
  451. if (!$startRead) {
  452. if ('OUTPUT__TYPE__XML' == $line) $outputType = 'XML';
  453. if ('OUTPUT__TYPE__HTML' == $line) $outputType = 'HTML';
  454. if ('OUTPUT__START' == $line) {
  455. $startRead = true;
  456. continue;
  457. }
  458. } else {
  459. if ('<!DOCTYPE' == substr($line, 0, strlen('<!DOCTYPE'))) continue;
  460. if ('OUTPUT__END' == $line) {
  461. break;
  462. }
  463. $html[]= $line;
  464. }
  465. }
  466. if (empty($html)) UI::alert('danger', "Empty output!");
  467. else {
  468. if ('XML' == $outputType) {
  469. echo UI::h('pre', [], htmlspecialchars(implode("\n", $html)));
  470. } else if ('HTML' == $outputType) {
  471. echo UI::h('div', ['class'=>"container", 'style'=>"padding:12px; border:1px solid #ddd"], $html);
  472. }
  473. }
  474. } catch (Exception $e) {
  475. UI::alert('danger', $e->getMessage());
  476. DBG::log($e);
  477. }
  478. UI::endContainer();
  479. UI::dol();
  480. }
  481. }