RteList.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  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, $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' . $url_query . '&amp;sql_query=' . urlencode($sql_drop) . '&amp;goto=db_routines.php' . urlencode("?db={$db}"),
  301. $titles['Drop'],
  302. ['class' => 'ajax drop_anchor']
  303. );
  304. $retval .= " </td>\n";
  305. $retval .= " <td>\n";
  306. $retval .= " {$routine['type']}\n";
  307. $retval .= " </td>\n";
  308. $retval .= " <td dir=\"ltr\">\n";
  309. $retval .= " "
  310. . htmlspecialchars($routine['returns']) . "\n";
  311. $retval .= " </td>\n";
  312. $retval .= " </tr>\n";
  313. return $retval;
  314. } // end self::getRoutineRow()
  315. /**
  316. * Creates the contents for a row in the list of triggers
  317. *
  318. * @param array $trigger An array of routine data
  319. * @param string $rowclass Additional class
  320. *
  321. * @return string HTML code of a cell for the list of triggers
  322. */
  323. public static function getTriggerRow(array $trigger, $rowclass = '')
  324. {
  325. global $url_query, $db, $table, $titles;
  326. $retval = " <tr class='$rowclass'>\n";
  327. $retval .= " <td>\n";
  328. $retval .= ' <input type="checkbox"'
  329. . ' class="checkall" name="item_name[]"'
  330. . ' value="' . htmlspecialchars($trigger['name']) . '" />';
  331. $retval .= " </td>\n";
  332. $retval .= " <td>\n";
  333. $retval .= " <span class='drop_sql hide'>"
  334. . htmlspecialchars($trigger['drop']) . "</span>\n";
  335. $retval .= " <strong>\n";
  336. $retval .= " " . htmlspecialchars($trigger['name']) . "\n";
  337. $retval .= " </strong>\n";
  338. $retval .= " </td>\n";
  339. if (empty($table)) {
  340. $retval .= " <td>\n";
  341. $retval .= "<a href='db_triggers.php{$url_query}"
  342. . "&amp;table=" . urlencode($trigger['table']) . "'>"
  343. . htmlspecialchars($trigger['table']) . "</a>";
  344. $retval .= " </td>\n";
  345. }
  346. $retval .= " <td>\n";
  347. if (Util::currentUserHasPrivilege('TRIGGER', $db, $table)) {
  348. $retval .= ' <a class="ajax edit_anchor"'
  349. . ' href="db_triggers.php'
  350. . $url_query
  351. . '&amp;edit_item=1'
  352. . '&amp;item_name='
  353. . urlencode($trigger['name'])
  354. . '">' . $titles['Edit'] . "</a>\n";
  355. } else {
  356. $retval .= " {$titles['NoEdit']}\n";
  357. }
  358. $retval .= " </td>\n";
  359. $retval .= " <td>\n";
  360. $retval .= ' <a class="ajax export_anchor"'
  361. . ' href="db_triggers.php'
  362. . $url_query
  363. . '&amp;export_item=1'
  364. . '&amp;item_name='
  365. . urlencode($trigger['name'])
  366. . '">' . $titles['Export'] . "</a>\n";
  367. $retval .= " </td>\n";
  368. $retval .= " <td>\n";
  369. if (Util::currentUserHasPrivilege('TRIGGER', $db)) {
  370. $retval .= Util::linkOrButton(
  371. 'sql.php' . $url_query . '&amp;sql_query=' . urlencode($trigger['drop']) . '&amp;goto=db_triggers.php' . urlencode("?db={$db}"),
  372. $titles['Drop'],
  373. ['class' => 'ajax drop_anchor']
  374. );
  375. } else {
  376. $retval .= " {$titles['NoDrop']}\n";
  377. }
  378. $retval .= " </td>\n";
  379. $retval .= " <td>\n";
  380. $retval .= " {$trigger['action_timing']}\n";
  381. $retval .= " </td>\n";
  382. $retval .= " <td>\n";
  383. $retval .= " {$trigger['event_manipulation']}\n";
  384. $retval .= " </td>\n";
  385. $retval .= " </tr>\n";
  386. return $retval;
  387. } // end self::getTriggerRow()
  388. /**
  389. * Creates the contents for a row in the list of events
  390. *
  391. * @param array $event An array of routine data
  392. * @param string $rowclass Additional class
  393. *
  394. * @return string HTML code of a cell for the list of events
  395. */
  396. public static function getEventRow(array $event, $rowclass = '')
  397. {
  398. global $url_query, $db, $titles;
  399. $sql_drop = sprintf(
  400. 'DROP EVENT IF EXISTS %s',
  401. Util::backquote($event['name'])
  402. );
  403. $retval = " <tr class='$rowclass'>\n";
  404. $retval .= " <td>\n";
  405. $retval .= ' <input type="checkbox"'
  406. . ' class="checkall" name="item_name[]"'
  407. . ' value="' . htmlspecialchars($event['name']) . '" />';
  408. $retval .= " </td>\n";
  409. $retval .= " <td>\n";
  410. $retval .= " <span class='drop_sql hide'>"
  411. . htmlspecialchars($sql_drop) . "</span>\n";
  412. $retval .= " <strong>\n";
  413. $retval .= " "
  414. . htmlspecialchars($event['name']) . "\n";
  415. $retval .= " </strong>\n";
  416. $retval .= " </td>\n";
  417. $retval .= " <td>\n";
  418. $retval .= " {$event['status']}\n";
  419. $retval .= " </td>\n";
  420. $retval .= " <td>\n";
  421. if (Util::currentUserHasPrivilege('EVENT', $db)) {
  422. $retval .= ' <a class="ajax edit_anchor"'
  423. . ' href="db_events.php'
  424. . $url_query
  425. . '&amp;edit_item=1'
  426. . '&amp;item_name='
  427. . urlencode($event['name'])
  428. . '">' . $titles['Edit'] . "</a>\n";
  429. } else {
  430. $retval .= " {$titles['NoEdit']}\n";
  431. }
  432. $retval .= " </td>\n";
  433. $retval .= " <td>\n";
  434. $retval .= ' <a class="ajax export_anchor"'
  435. . ' href="db_events.php'
  436. . $url_query
  437. . '&amp;export_item=1'
  438. . '&amp;item_name='
  439. . urlencode($event['name'])
  440. . '">' . $titles['Export'] . "</a>\n";
  441. $retval .= " </td>\n";
  442. $retval .= " <td>\n";
  443. if (Util::currentUserHasPrivilege('EVENT', $db)) {
  444. $retval .= Util::linkOrButton(
  445. 'sql.php' . $url_query . '&amp;sql_query=' . urlencode($sql_drop) . '&amp;goto=db_events.php' . urlencode("?db={$db}"),
  446. $titles['Drop'],
  447. ['class' => 'ajax drop_anchor']
  448. );
  449. } else {
  450. $retval .= " {$titles['NoDrop']}\n";
  451. }
  452. $retval .= " </td>\n";
  453. $retval .= " <td>\n";
  454. $retval .= " {$event['type']}\n";
  455. $retval .= " </td>\n";
  456. $retval .= " </tr>\n";
  457. return $retval;
  458. } // end self::getEventRow()
  459. }