1<?php 2/* vim: set expandtab sw=4 ts=4 sts=4: */ 3 4/** 5 * Holds the PhpMyAdmin\Controllers\Server\ServerDatabasesController 6 * 7 * @package PhpMyAdmin\Controllers 8 */ 9 10namespace PhpMyAdmin\Controllers\Server; 11 12use PhpMyAdmin\Controllers\Controller; 13use PhpMyAdmin\Charsets; 14use PhpMyAdmin\DatabaseInterface; 15use PhpMyAdmin\Message; 16use PhpMyAdmin\Response; 17use PhpMyAdmin\Server\Common; 18use PhpMyAdmin\Template; 19use PhpMyAdmin\Url; 20use PhpMyAdmin\Util; 21 22/** 23 * Handles viewing and creating and deleting databases 24 * 25 * @package PhpMyAdmin\Controllers 26 */ 27class ServerDatabasesController extends Controller 28{ 29 /** 30 * @var array array of database details 31 */ 32 private $_databases; 33 /** 34 * @var int number of databases 35 */ 36 private $_database_count; 37 /** 38 * @var string sort by column 39 */ 40 private $_sort_by; 41 /** 42 * @var string sort order of databases 43 */ 44 private $_sort_order; 45 /** 46 * @var boolean whether to show database statistics 47 */ 48 private $_dbstats; 49 /** 50 * @var int position in list navigation 51 */ 52 private $_pos; 53 54 /** 55 * Index action 56 * 57 * @return void 58 */ 59 public function indexAction() 60 { 61 include_once 'libraries/check_user_privileges.inc.php'; 62 63 $response = Response::getInstance(); 64 65 if (isset($_POST['drop_selected_dbs']) 66 && $response->isAjax() 67 && ($GLOBALS['dbi']->isSuperuser() || $GLOBALS['cfg']['AllowUserDropDatabase']) 68 ) { 69 $this->dropDatabasesAction(); 70 return; 71 } 72 73 include_once 'libraries/replication.inc.php'; 74 75 if (isset($_POST['new_db']) 76 && $response->isAjax() 77 ) { 78 $this->createDatabaseAction(); 79 return; 80 } 81 82 include_once 'libraries/server_common.inc.php'; 83 84 $header = $this->response->getHeader(); 85 $scripts = $header->getScripts(); 86 $scripts->addFile('server_databases.js'); 87 88 $this->_setSortDetails(); 89 $this->_dbstats = ! empty($_POST['dbstats']); 90 $this->_pos = empty($_REQUEST['pos']) ? 0 : (int) $_REQUEST['pos']; 91 92 /** 93 * Gets the databases list 94 */ 95 if ($GLOBALS['server'] > 0) { 96 $this->_databases = $this->dbi->getDatabasesFull( 97 null, $this->_dbstats, DatabaseInterface::CONNECT_USER, $this->_sort_by, 98 $this->_sort_order, $this->_pos, true 99 ); 100 $this->_database_count = count($GLOBALS['dblist']->databases); 101 } else { 102 $this->_database_count = 0; 103 } 104 105 if ($this->_database_count > 0 && ! empty($this->_databases)) { 106 $databases = $this->_getHtmlForDatabases($replication_types); 107 } 108 109 $this->response->addHTML(Template::get('server/databases/index')->render([ 110 'show_create_db' => $GLOBALS['cfg']['ShowCreateDb'], 111 'is_create_db_priv' => $GLOBALS['is_create_db_priv'], 112 'dbstats' => $this->_dbstats, 113 'db_to_create' => $GLOBALS['db_to_create'], 114 'server_collation' => $GLOBALS['dbi']->getServerCollation(), 115 'databases' => isset($databases) ? $databases : null, 116 'dbi' => $GLOBALS['dbi'], 117 'disable_is' => $GLOBALS['cfg']['Server']['DisableIS'], 118 ])); 119 } 120 121 /** 122 * Handles creating a new database 123 * 124 * @return void 125 */ 126 public function createDatabaseAction() 127 { 128 // lower_case_table_names=1 `DB` becomes `db` 129 if ($GLOBALS['dbi']->getLowerCaseNames() === '1') { 130 $_POST['new_db'] = mb_strtolower( 131 $_POST['new_db'] 132 ); 133 } 134 /** 135 * Builds and executes the db creation sql query 136 */ 137 $sql_query = 'CREATE DATABASE ' . Util::backquote($_POST['new_db']); 138 if (! empty($_POST['db_collation'])) { 139 list($db_charset) = explode('_', $_POST['db_collation']); 140 $charsets = Charsets::getMySQLCharsets( 141 $GLOBALS['dbi'], 142 $GLOBALS['cfg']['Server']['DisableIS'] 143 ); 144 $collations = Charsets::getMySQLCollations( 145 $GLOBALS['dbi'], 146 $GLOBALS['cfg']['Server']['DisableIS'] 147 ); 148 if (in_array($db_charset, $charsets) 149 && in_array($_POST['db_collation'], $collations[$db_charset]) 150 ) { 151 $sql_query .= ' DEFAULT' 152 . Util::getCharsetQueryPart($_POST['db_collation']); 153 } 154 } 155 $sql_query .= ';'; 156 157 $result = $GLOBALS['dbi']->tryQuery($sql_query); 158 159 if (! $result) { 160 // avoid displaying the not-created db name in header or navi panel 161 $GLOBALS['db'] = ''; 162 163 $message = Message::rawError($GLOBALS['dbi']->getError()); 164 $this->response->setRequestStatus(false); 165 $this->response->addJSON('message', $message); 166 } else { 167 $GLOBALS['db'] = $_POST['new_db']; 168 169 $message = Message::success(__('Database %1$s has been created.')); 170 $message->addParam($_POST['new_db']); 171 $this->response->addJSON('message', $message); 172 $this->response->addJSON( 173 'sql_query', Util::getMessage(null, $sql_query, 'success') 174 ); 175 176 $this->response->addJSON( 177 'url_query', 178 Util::getScriptNameForOption( 179 $GLOBALS['cfg']['DefaultTabDatabase'], 'database' 180 ) 181 . Url::getCommon(array('db' => $_POST['new_db'])) 182 ); 183 } 184 } 185 186 /** 187 * Handles dropping multiple databases 188 * 189 * @return void 190 */ 191 public function dropDatabasesAction() 192 { 193 if (! isset($_POST['selected_dbs'])) { 194 $message = Message::error(__('No databases selected.')); 195 } else { 196 $action = 'server_databases.php'; 197 $err_url = $action . Url::getCommon(); 198 199 $GLOBALS['submit_mult'] = 'drop_db'; 200 $GLOBALS['mult_btn'] = __('Yes'); 201 202 include 'libraries/mult_submits.inc.php'; 203 204 if (empty($message)) { // no error message 205 $number_of_databases = count($selected); 206 $message = Message::success( 207 _ngettext( 208 '%1$d database has been dropped successfully.', 209 '%1$d databases have been dropped successfully.', 210 $number_of_databases 211 ) 212 ); 213 $message->addParam($number_of_databases); 214 } 215 } 216 217 if ($message instanceof Message) { 218 $this->response->setRequestStatus($message->isSuccess()); 219 $this->response->addJSON('message', $message); 220 } 221 } 222 223 /** 224 * Extracts parameters $sort_order and $sort_by 225 * 226 * @return void 227 */ 228 private function _setSortDetails() 229 { 230 if (empty($_REQUEST['sort_by'])) { 231 $this->_sort_by = 'SCHEMA_NAME'; 232 } else { 233 $sort_by_whitelist = array( 234 'SCHEMA_NAME', 235 'DEFAULT_COLLATION_NAME', 236 'SCHEMA_TABLES', 237 'SCHEMA_TABLE_ROWS', 238 'SCHEMA_DATA_LENGTH', 239 'SCHEMA_INDEX_LENGTH', 240 'SCHEMA_LENGTH', 241 'SCHEMA_DATA_FREE' 242 ); 243 if (in_array($_REQUEST['sort_by'], $sort_by_whitelist)) { 244 $this->_sort_by = $_REQUEST['sort_by']; 245 } else { 246 $this->_sort_by = 'SCHEMA_NAME'; 247 } 248 } 249 250 if (isset($_REQUEST['sort_order']) 251 && mb_strtolower($_REQUEST['sort_order']) == 'desc' 252 ) { 253 $this->_sort_order = 'desc'; 254 } else { 255 $this->_sort_order = 'asc'; 256 } 257 } 258 259 /** 260 * Returns the html for Database List 261 * 262 * @param array $replication_types replication types 263 * 264 * @return string 265 */ 266 private function _getHtmlForDatabases(array $replication_types) 267 { 268 $first_database = reset($this->_databases); 269 // table col order 270 $column_order = $this->_getColumnOrder(); 271 272 // calculate aggregate stats to display in footer 273 foreach ($this->_databases as $current) { 274 foreach ($column_order as $stat_name => $stat) { 275 if (array_key_exists($stat_name, $current) 276 && is_numeric($stat['footer']) 277 ) { 278 $column_order[$stat_name]['footer'] += $current[$stat_name]; 279 } 280 } 281 } 282 283 $_url_params = array( 284 'pos' => $this->_pos, 285 'dbstats' => $this->_dbstats, 286 'sort_by' => $this->_sort_by, 287 'sort_order' => $this->_sort_order, 288 ); 289 290 $html = Template::get('server/databases/databases_header')->render([ 291 'database_count' => $this->_database_count, 292 'pos' => $this->_pos, 293 'url_params' => $_url_params, 294 'max_db_list' => $GLOBALS['cfg']['MaxDbList'], 295 'sort_by' => $this->_sort_by, 296 'sort_order' => $this->_sort_order, 297 'column_order' => $column_order, 298 'first_database' => $first_database, 299 'master_replication' => $GLOBALS['replication_info']['master']['status'], 300 'slave_replication' => $GLOBALS['replication_info']['slave']['status'], 301 'is_superuser' => $GLOBALS['dbi']->isSuperuser(), 302 'allow_user_drop_database' => $GLOBALS['cfg']['AllowUserDropDatabase'], 303 ]); 304 305 $html .= $this->_getHtmlForTableBody($column_order, $replication_types); 306 307 $html .= Template::get('server/databases/databases_footer')->render([ 308 'column_order' => $column_order, 309 'first_database' => $first_database, 310 'master_replication' => $GLOBALS['replication_info']['master']['status'], 311 'slave_replication' => $GLOBALS['replication_info']['slave']['status'], 312 'database_count' => $this->_database_count, 313 'is_superuser' => $GLOBALS['dbi']->isSuperuser(), 314 'allow_user_drop_database' => $GLOBALS['cfg']['AllowUserDropDatabase'], 315 'pma_theme_image' => $GLOBALS['pmaThemeImage'], 316 'text_dir' => $GLOBALS['text_dir'], 317 'dbstats' => $this->_dbstats, 318 ]); 319 320 return $html; 321 } 322 323 /** 324 * Prepares the $column_order array 325 * 326 * @return array 327 */ 328 private function _getColumnOrder() 329 { 330 $column_order = array(); 331 $column_order['DEFAULT_COLLATION_NAME'] = array( 332 'disp_name' => __('Collation'), 333 'description_function' => array(Charsets::class, 'getCollationDescr'), 334 'format' => 'string', 335 'footer' => $this->dbi->getServerCollation(), 336 ); 337 $column_order['SCHEMA_TABLES'] = array( 338 'disp_name' => __('Tables'), 339 'format' => 'number', 340 'footer' => 0, 341 ); 342 $column_order['SCHEMA_TABLE_ROWS'] = array( 343 'disp_name' => __('Rows'), 344 'format' => 'number', 345 'footer' => 0, 346 ); 347 $column_order['SCHEMA_DATA_LENGTH'] = array( 348 'disp_name' => __('Data'), 349 'format' => 'byte', 350 'footer' => 0, 351 ); 352 $column_order['SCHEMA_INDEX_LENGTH'] = array( 353 'disp_name' => __('Indexes'), 354 'format' => 'byte', 355 'footer' => 0, 356 ); 357 $column_order['SCHEMA_LENGTH'] = array( 358 'disp_name' => __('Total'), 359 'format' => 'byte', 360 'footer' => 0, 361 ); 362 $column_order['SCHEMA_DATA_FREE'] = array( 363 'disp_name' => __('Overhead'), 364 'format' => 'byte', 365 'footer' => 0, 366 ); 367 368 return $column_order; 369 } 370 371 /** 372 * Returns the html for Database List 373 * 374 * @param array $column_order column order 375 * @param array $replication_types replication types 376 * 377 * @return string 378 */ 379 private function _getHtmlForTableBody(array $column_order, array $replication_types) 380 { 381 $html = '<tbody>' . "\n"; 382 383 foreach ($this->_databases as $current) { 384 $tr_class = ' db-row'; 385 if ($this->dbi->isSystemSchema($current['SCHEMA_NAME'], true)) { 386 $tr_class .= ' noclick'; 387 } 388 389 $generated_html = $this->_buildHtmlForDb( 390 $current, 391 $column_order, 392 $replication_types, 393 $GLOBALS['replication_info'], 394 $tr_class 395 ); 396 $html .= $generated_html; 397 } // end foreach ($this->_databases as $key => $current) 398 $html .= '</tbody>'; 399 400 return $html; 401 } 402 403 /** 404 * Builds the HTML for one database to display in the list 405 * of databases from server_databases.php 406 * 407 * @param array $current current database 408 * @param array $column_order column order 409 * @param array $replication_types replication types 410 * @param array $replication_info replication info 411 * @param string $tr_class HTMl class for the row 412 * 413 * @return array $column_order, $out 414 */ 415 function _buildHtmlForDb( 416 array $current, array $column_order, 417 array $replication_types, array $replication_info, $tr_class = '' 418 ) { 419 $master_replication = $slave_replication = ''; 420 foreach ($replication_types as $type) { 421 if ($replication_info[$type]['status']) { 422 $out = ''; 423 $key = array_search( 424 $current["SCHEMA_NAME"], 425 $replication_info[$type]['Ignore_DB'] 426 ); 427 if (strlen($key) > 0) { 428 $out = Util::getIcon( 429 's_cancel', 430 __('Not replicated') 431 ); 432 } else { 433 $key = array_search( 434 $current["SCHEMA_NAME"], $replication_info[$type]['Do_DB'] 435 ); 436 437 if (strlen($key) > 0 438 || count($replication_info[$type]['Do_DB']) == 0 439 ) { 440 // if ($key != null) did not work for index "0" 441 $out = Util::getIcon( 442 's_success', 443 __('Replicated') 444 ); 445 } 446 } 447 448 if ($type == 'master') { 449 $master_replication = $out; 450 } elseif ($type == 'slave') { 451 $slave_replication = $out; 452 } 453 } 454 } 455 456 return Template::get('server/databases/table_row')->render([ 457 'current' => $current, 458 'tr_class' => $tr_class, 459 'column_order' => $column_order, 460 'master_replication_status' => $GLOBALS['replication_info']['master']['status'], 461 'master_replication' => $master_replication, 462 'slave_replication_status' => $GLOBALS['replication_info']['slave']['status'], 463 'slave_replication' => $slave_replication, 464 'is_superuser' => $GLOBALS['dbi']->isSuperuser(), 465 'allow_user_drop_database' => $GLOBALS['cfg']['AllowUserDropDatabase'], 466 'is_system_schema' => $GLOBALS['dbi']->isSystemSchema($current['SCHEMA_NAME'], true), 467 'default_tab_database' => $GLOBALS['cfg']['DefaultTabDatabase'], 468 ]); 469 } 470} 471