RteList.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Common functions for generating lists of Routines, Triggers and Events.
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. namespace PhpMyAdmin\Rte;
  9. use PhpMyAdmin\Response;
  10. use PhpMyAdmin\Rte\Words;
  11. use PhpMyAdmin\SqlParser\Parser;
  12. use PhpMyAdmin\SqlParser\Statements\CreateStatement;
  13. use PhpMyAdmin\SqlParser\Utils\Routine;
  14. use PhpMyAdmin\Template;
  15. use PhpMyAdmin\Url;
  16. use PhpMyAdmin\Util;
  17. /**
  18. * PhpMyAdmin\Rte\RteList class
  19. *
  20. * @package PhpMyAdmin
  21. */
  22. class RteList
  23. {
  24. /**
  25. * Creates a list of items containing the relevant
  26. * information and some action links.
  27. *
  28. * @param string $type One of ['routine'|'trigger'|'event']
  29. * @param array $items An array of items
  30. *
  31. * @return string HTML code of the list of items
  32. */
  33. public static function get($type, array $items)
  34. {
  35. global $table;
  36. /**
  37. * Conditional classes switch the list on or off
  38. */
  39. $class1 = 'hide';
  40. $class2 = '';
  41. if (! $items) {
  42. $class1 = '';
  43. $class2 = ' hide';
  44. }
  45. /**
  46. * Generate output
  47. */
  48. $retval = "<!-- LIST OF " . Words::get('docu') . " START -->\n";
  49. $retval .= '<form id="rteListForm" class="ajax" action="';
  50. switch ($type) {
  51. case 'routine':
  52. $retval .= 'db_routines.php';
  53. break;
  54. case 'trigger':
  55. if (! empty($table)) {
  56. $retval .= 'tbl_triggers.php';
  57. } else {
  58. $retval .= 'db_triggers.php';
  59. }
  60. break;
  61. case 'event':
  62. $retval .= 'db_events.php';
  63. break;
  64. default:
  65. break;
  66. }
  67. $retval .= '">';
  68. $retval .= Url::getHiddenInputs($GLOBALS['db'], $GLOBALS['table']);
  69. $retval .= "<fieldset>\n";
  70. $retval .= " <legend>\n";
  71. $retval .= " " . Words::get('title') . "\n";
  72. $retval .= " "
  73. . Util::showMySQLDocu(Words::get('docu')) . "\n";
  74. $retval .= " </legend>\n";
  75. $retval .= " <div class='$class1' id='nothing2display'>\n";
  76. $retval .= " " . Words::get('nothing') . "\n";
  77. $retval .= " </div>\n";
  78. $retval .= " <table class='data$class2'>\n";
  79. $retval .= " <!-- TABLE HEADERS -->\n";
  80. $retval .= " <tr>\n";
  81. // th cells with a colspan need corresponding td cells, according to W3C
  82. switch ($type) {
  83. case 'routine':
  84. $retval .= " <th></th>\n";
  85. $retval .= " <th>" . __('Name') . "</th>\n";
  86. $retval .= " <th colspan='4'>" . __('Action') . "</th>\n";
  87. $retval .= " <th>" . __('Type') . "</th>\n";
  88. $retval .= " <th>" . __('Returns') . "</th>\n";
  89. $retval .= " </tr>\n";
  90. $retval .= " <tr class='hide'>\n"; // see comment above
  91. for ($i = 0; $i < 7; $i++) {
  92. $retval .= " <td></td>\n";
  93. }
  94. break;
  95. case 'trigger':
  96. $retval .= " <th></th>\n";
  97. $retval .= " <th>" . __('Name') . "</th>\n";
  98. if (empty($table)) {
  99. $retval .= " <th>" . __('Table') . "</th>\n";
  100. }
  101. $retval .= " <th colspan='3'>" . __('Action') . "</th>\n";
  102. $retval .= " <th>" . __('Time') . "</th>\n";
  103. $retval .= " <th>" . __('Event') . "</th>\n";
  104. $retval .= " </tr>\n";
  105. $retval .= " <tr class='hide'>\n"; // see comment above
  106. for ($i = 0; $i < (empty($table) ? 7 : 6); $i++) {
  107. $retval .= " <td></td>\n";
  108. }
  109. break;
  110. case 'event':
  111. $retval .= " <th></th>\n";
  112. $retval .= " <th>" . __('Name') . "</th>\n";
  113. $retval .= " <th>" . __('Status') . "</th>\n";
  114. $retval .= " <th colspan='3'>" . __('Action') . "</th>\n";
  115. $retval .= " <th>" . __('Type') . "</th>\n";
  116. $retval .= " </tr>\n";
  117. $retval .= " <tr class='hide'>\n"; // see comment above
  118. for ($i = 0; $i < 6; $i++) {
  119. $retval .= " <td></td>\n";
  120. }
  121. break;
  122. default:
  123. break;
  124. }
  125. $retval .= " </tr>\n";
  126. $retval .= " <!-- TABLE DATA -->\n";
  127. $count = 0;
  128. $response = Response::getInstance();
  129. foreach ($items as $item) {
  130. if ($response->isAjax() && empty($_REQUEST['ajax_page_request'])) {
  131. $rowclass = 'ajaxInsert hide';
  132. } else {
  133. $rowclass = '';
  134. }
  135. // Get each row from the correct function
  136. switch ($type) {
  137. case 'routine':
  138. $retval .= self::getRoutineRow($item, $rowclass);
  139. break;
  140. case 'trigger':
  141. $retval .= self::getTriggerRow($item, $rowclass);
  142. break;
  143. case 'event':
  144. $retval .= self::getEventRow($item, $rowclass);
  145. break;
  146. default:
  147. break;
  148. }
  149. $count++;
  150. }
  151. $retval .= " </table>\n";
  152. if (count($items)) {
  153. $retval .= '<div class="withSelected">';
  154. $retval .= Template::get('select_all')
  155. ->render(
  156. array(
  157. 'pma_theme_image' => $GLOBALS['pmaThemeImage'],
  158. 'text_dir' => $GLOBALS['text_dir'],
  159. 'form_name' => 'rteListForm',
  160. )
  161. );
  162. $retval .= Util::getButtonOrImage(
  163. 'submit_mult', 'mult_submit',
  164. __('Export'), 'b_export', 'export'
  165. );
  166. $retval .= Util::getButtonOrImage(
  167. 'submit_mult', 'mult_submit',
  168. __('Drop'), 'b_drop', 'drop'
  169. );
  170. $retval .= '</div>';
  171. }
  172. $retval .= "</fieldset>\n";
  173. $retval .= "</form>\n";
  174. $retval .= "<!-- LIST OF " . Words::get('docu') . " END -->\n";
  175. return $retval;
  176. } // end self::get()
  177. /**
  178. * Creates the contents for a row in the list of routines
  179. *
  180. * @param array $routine An array of routine data
  181. * @param string $rowclass Additional class
  182. *
  183. * @return string HTML code of a row for the list of routines
  184. */
  185. public static function getRoutineRow(array $routine, $rowclass = '')
  186. {
  187. global $url_query, $url_params, $db, $titles;
  188. $sql_drop = sprintf(
  189. 'DROP %s IF EXISTS %s',
  190. $routine['type'],
  191. Util::backquote($routine['name'])
  192. );
  193. $type_link = "item_type={$routine['type']}";
  194. $retval = " <tr class='$rowclass'>\n";
  195. $retval .= " <td>\n";
  196. $retval .= ' <input type="checkbox"'
  197. . ' class="checkall" name="item_name[]"'
  198. . ' value="' . htmlspecialchars($routine['name']) . '" />';
  199. $retval .= " </td>\n";
  200. $retval .= " <td>\n";
  201. $retval .= " <span class='drop_sql hide'>"
  202. . htmlspecialchars($sql_drop) . "</span>\n";
  203. $retval .= " <strong>\n";
  204. $retval .= " "
  205. . htmlspecialchars($routine['name']) . "\n";
  206. $retval .= " </strong>\n";
  207. $retval .= " </td>\n";
  208. $retval .= " <td>\n";
  209. // this is for our purpose to decide whether to
  210. // show the edit link or not, so we need the DEFINER for the routine
  211. $where = "ROUTINE_SCHEMA " . Util::getCollateForIS() . "="
  212. . "'" . $GLOBALS['dbi']->escapeString($db) . "' "
  213. . "AND SPECIFIC_NAME='" . $GLOBALS['dbi']->escapeString($routine['name']) . "'"
  214. . "AND ROUTINE_TYPE='" . $GLOBALS['dbi']->escapeString($routine['type']) . "'";
  215. $query = "SELECT `DEFINER` FROM INFORMATION_SCHEMA.ROUTINES WHERE $where;";
  216. $routine_definer = $GLOBALS['dbi']->fetchValue($query);
  217. $curr_user = $GLOBALS['dbi']->getCurrentUser();
  218. // Since editing a procedure involved dropping and recreating, check also for
  219. // CREATE ROUTINE privilege to avoid lost procedures.
  220. if ((Util::currentUserHasPrivilege('CREATE ROUTINE', $db)
  221. && $curr_user == $routine_definer)
  222. || $GLOBALS['dbi']->isSuperuser()
  223. ) {
  224. $retval .= ' <a class="ajax edit_anchor"'
  225. . ' href="db_routines.php'
  226. . $url_query
  227. . '&amp;edit_item=1'
  228. . '&amp;item_name='
  229. . urlencode($routine['name'])
  230. . '&amp;' . $type_link
  231. . '">' . $titles['Edit'] . "</a>\n";
  232. } else {
  233. $retval .= " {$titles['NoEdit']}\n";
  234. }
  235. $retval .= " </td>\n";
  236. $retval .= " <td>\n";
  237. // There is a problem with Util::currentUserHasPrivilege():
  238. // it does not detect all kinds of privileges, for example
  239. // a direct privilege on a specific routine. So, at this point,
  240. // we show the Execute link, hoping that the user has the correct rights.
  241. // Also, information_schema might be hiding the ROUTINE_DEFINITION
  242. // but a routine with no input parameters can be nonetheless executed.
  243. // Check if the routine has any input parameters. If it does,
  244. // we will show a dialog to get values for these parameters,
  245. // otherwise we can execute it directly.
  246. $definition = $GLOBALS['dbi']->getDefinition(
  247. $db, $routine['type'], $routine['name']
  248. );
  249. if ($definition !== false) {
  250. $parser = new Parser($definition);
  251. /**
  252. * @var CreateStatement $stmt
  253. */
  254. $stmt = $parser->statements[0];
  255. $params = Routine::getParameters($stmt);
  256. if (Util::currentUserHasPrivilege('EXECUTE', $db)) {
  257. $execute_action = 'execute_routine';
  258. for ($i = 0; $i < $params['num']; $i++) {
  259. if ($routine['type'] == 'PROCEDURE'
  260. && $params['dir'][$i] == 'OUT'
  261. ) {
  262. continue;
  263. }
  264. $execute_action = 'execute_dialog';
  265. break;
  266. }
  267. $query_part = $execute_action . '=1&amp;item_name='
  268. . urlencode($routine['name']) . '&amp;' . $type_link;
  269. $retval .= ' <a class="ajax exec_anchor"'
  270. . ' href="db_routines.php'
  271. . $url_query
  272. . ($execute_action == 'execute_routine'
  273. ? '" data-post="' . $query_part
  274. : '&amp;' . $query_part)
  275. . '">' . $titles['Execute'] . "</a>\n";
  276. } else {
  277. $retval .= " {$titles['NoExecute']}\n";
  278. }
  279. }
  280. $retval .= " </td>\n";
  281. $retval .= " <td>\n";
  282. if ((Util::currentUserHasPrivilege('CREATE ROUTINE', $db)
  283. && $curr_user == $routine_definer)
  284. || $GLOBALS['dbi']->isSuperuser()
  285. ) {
  286. $retval .= ' <a class="ajax export_anchor"'
  287. . ' href="db_routines.php'
  288. . $url_query
  289. . '&amp;export_item=1'
  290. . '&amp;item_name='
  291. . urlencode($routine['name'])
  292. . '&amp;' . $type_link
  293. . '">' . $titles['Export'] . "</a>\n";
  294. } else {
  295. $retval .= " {$titles['NoExport']}\n";
  296. }
  297. $retval .= " </td>\n";
  298. $retval .= " <td>\n";
  299. $retval .= Util::linkOrButton(
  300. 'sql.php',
  301. array_merge(
  302. $url_params,
  303. ['sql_query' => $sql_drop, 'goto' => 'db_routines.php' . Url::getCommon(['db' => $db])]
  304. ),
  305. $titles['Drop'],
  306. ['class' => 'ajax drop_anchor']
  307. );
  308. $retval .= " </td>\n";
  309. $retval .= " <td>\n";
  310. $retval .= " {$routine['type']}\n";
  311. $retval .= " </td>\n";
  312. $retval .= " <td dir=\"ltr\">\n";
  313. $retval .= " "
  314. . htmlspecialchars($routine['returns']) . "\n";
  315. $retval .= " </td>\n";
  316. $retval .= " </tr>\n";
  317. return $retval;
  318. } // end self::getRoutineRow()
  319. /**
  320. * Creates the contents for a row in the list of triggers
  321. *
  322. * @param array $trigger An array of routine data
  323. * @param string $rowclass Additional class
  324. *
  325. * @return string HTML code of a cell for the list of triggers
  326. */
  327. public static function getTriggerRow(array $trigger, $rowclass = '')
  328. {
  329. global $url_query, $url_params, $db, $table, $titles;
  330. $retval = " <tr class='$rowclass'>\n";
  331. $retval .= " <td>\n";
  332. $retval .= ' <input type="checkbox"'
  333. . ' class="checkall" name="item_name[]"'
  334. . ' value="' . htmlspecialchars($trigger['name']) . '" />';
  335. $retval .= " </td>\n";
  336. $retval .= " <td>\n";
  337. $retval .= " <span class='drop_sql hide'>"
  338. . htmlspecialchars($trigger['drop']) . "</span>\n";
  339. $retval .= " <strong>\n";
  340. $retval .= " " . htmlspecialchars($trigger['name']) . "\n";
  341. $retval .= " </strong>\n";
  342. $retval .= " </td>\n";
  343. if (empty($table)) {
  344. $retval .= " <td>\n";
  345. $retval .= "<a href='db_triggers.php"
  346. . Url::getCommon(array_merge($url_params, ['table' => $trigger['table']])) . "'>"
  347. . htmlspecialchars($trigger['table']) . "</a>";
  348. $retval .= " </td>\n";
  349. }
  350. $retval .= " <td>\n";
  351. if (Util::currentUserHasPrivilege('TRIGGER', $db, $table)) {
  352. $retval .= ' <a class="ajax edit_anchor"'
  353. . ' href="db_triggers.php'
  354. . $url_query
  355. . '&amp;edit_item=1'
  356. . '&amp;item_name='
  357. . urlencode($trigger['name'])
  358. . '">' . $titles['Edit'] . "</a>\n";
  359. } else {
  360. $retval .= " {$titles['NoEdit']}\n";
  361. }
  362. $retval .= " </td>\n";
  363. $retval .= " <td>\n";
  364. $retval .= ' <a class="ajax export_anchor"'
  365. . ' href="db_triggers.php'
  366. . $url_query
  367. . '&amp;export_item=1'
  368. . '&amp;item_name='
  369. . urlencode($trigger['name'])
  370. . '">' . $titles['Export'] . "</a>\n";
  371. $retval .= " </td>\n";
  372. $retval .= " <td>\n";
  373. if (Util::currentUserHasPrivilege('TRIGGER', $db)) {
  374. $retval .= Util::linkOrButton(
  375. 'sql.php',
  376. array_merge(
  377. $url_params,
  378. ['sql_query' => $trigger['drop'], 'goto' => 'db_triggers.php' . Url::getCommon(['db' => $db])]
  379. ),
  380. $titles['Drop'],
  381. ['class' => 'ajax drop_anchor']
  382. );
  383. } else {
  384. $retval .= " {$titles['NoDrop']}\n";
  385. }
  386. $retval .= " </td>\n";
  387. $retval .= " <td>\n";
  388. $retval .= " {$trigger['action_timing']}\n";
  389. $retval .= " </td>\n";
  390. $retval .= " <td>\n";
  391. $retval .= " {$trigger['event_manipulation']}\n";
  392. $retval .= " </td>\n";
  393. $retval .= " </tr>\n";
  394. return $retval;
  395. } // end self::getTriggerRow()
  396. /**
  397. * Creates the contents for a row in the list of events
  398. *
  399. * @param array $event An array of routine data
  400. * @param string $rowclass Additional class
  401. *
  402. * @return string HTML code of a cell for the list of events
  403. */
  404. public static function getEventRow(array $event, $rowclass = '')
  405. {
  406. global $url_query, $url_params, $db, $titles;
  407. $sql_drop = sprintf(
  408. 'DROP EVENT IF EXISTS %s',
  409. Util::backquote($event['name'])
  410. );
  411. $retval = " <tr class='$rowclass'>\n";
  412. $retval .= " <td>\n";
  413. $retval .= ' <input type="checkbox"'
  414. . ' class="checkall" name="item_name[]"'
  415. . ' value="' . htmlspecialchars($event['name']) . '" />';
  416. $retval .= " </td>\n";
  417. $retval .= " <td>\n";
  418. $retval .= " <span class='drop_sql hide'>"
  419. . htmlspecialchars($sql_drop) . "</span>\n";
  420. $retval .= " <strong>\n";
  421. $retval .= " "
  422. . htmlspecialchars($event['name']) . "\n";
  423. $retval .= " </strong>\n";
  424. $retval .= " </td>\n";
  425. $retval .= " <td>\n";
  426. $retval .= " {$event['status']}\n";
  427. $retval .= " </td>\n";
  428. $retval .= " <td>\n";
  429. if (Util::currentUserHasPrivilege('EVENT', $db)) {
  430. $retval .= ' <a class="ajax edit_anchor"'
  431. . ' href="db_events.php'
  432. . $url_query
  433. . '&amp;edit_item=1'
  434. . '&amp;item_name='
  435. . urlencode($event['name'])
  436. . '">' . $titles['Edit'] . "</a>\n";
  437. } else {
  438. $retval .= " {$titles['NoEdit']}\n";
  439. }
  440. $retval .= " </td>\n";
  441. $retval .= " <td>\n";
  442. $retval .= ' <a class="ajax export_anchor"'
  443. . ' href="db_events.php'
  444. . $url_query
  445. . '&amp;export_item=1'
  446. . '&amp;item_name='
  447. . urlencode($event['name'])
  448. . '">' . $titles['Export'] . "</a>\n";
  449. $retval .= " </td>\n";
  450. $retval .= " <td>\n";
  451. if (Util::currentUserHasPrivilege('EVENT', $db)) {
  452. $retval .= Util::linkOrButton(
  453. 'sql.php',
  454. array_merge(
  455. $url_params,
  456. ['sql_query' => $sql_drop, 'goto' => 'db_events.php' . Url::getCommon(['db' => $db])]
  457. ),
  458. $titles['Drop'],
  459. ['class' => 'ajax drop_anchor']
  460. );
  461. } else {
  462. $retval .= " {$titles['NoDrop']}\n";
  463. }
  464. $retval .= " </td>\n";
  465. $retval .= " <td>\n";
  466. $retval .= " {$event['type']}\n";
  467. $retval .= " </td>\n";
  468. $retval .= " </tr>\n";
  469. return $retval;
  470. } // end self::getEventRow()
  471. }