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