Quick fixes for XML export
[phpmyadmin.git] / libraries / List_Database.class.php
blob759ced40922622b4ec3d9ebc069eab4c83721c8d
1 <?php
2 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 /**
4 * holds the PMA_List_Database class
6 * @package phpMyAdmin
7 */
9 /**
10 * the list base class
12 require_once './libraries/List.class.php';
14 /**
15 * handles database lists
17 * <code>
18 * $PMA_List_Database = new PMA_List_Database($userlink, $controllink);
19 * </code>
21 * @todo this object should be attached to the PMA_Server object
22 * @todo ? make use of INFORMATION_SCHEMA
23 * @todo ? support --skip-showdatabases and user has only global rights
24 * @access public
25 * @since phpMyAdmin 2.9.10
26 * @package phpMyAdmin
28 /*public*/ class PMA_List_Database extends PMA_List
30 /**
31 * @var mixed database link resource|object to be used
33 protected $_db_link = null;
35 /**
36 * @var mixed user database link resource|object
38 protected $_db_link_user = null;
40 /**
41 * @var mixed controluser database link resource|object
43 protected $_db_link_control = null;
45 /**
46 * @var boolean whether SHOW DATABASES is disabled or not
47 * @access protected
49 protected $_show_databases_disabled = false;
51 /**
52 * @var string command to retrieve databases from server
54 protected $_command = null;
56 /**
57 * Constructor
59 * @param mixed $db_link_user user database link resource|object
60 * @param mixed $db_link_control control database link resource|object
62 public function __construct($db_link_user = null, $db_link_control = null)
64 $this->_db_link = $db_link_user;
65 $this->_db_link_user = $db_link_user;
66 $this->_db_link_control = $db_link_control;
68 parent::__construct();
69 $this->build();
72 /**
73 * checks if the configuration wants to hide some databases
75 * @todo temporaly use this docblock to test how to doc $GLOBALS
77 protected function _checkHideDatabase()
79 if (empty($GLOBALS['cfg']['Server']['hide_db'])) {
80 return;
83 foreach ($this->getArrayCopy() as $key => $db) {
84 if (preg_match('/' . $GLOBALS['cfg']['Server']['hide_db'] . '/', $db)) {
85 $this->offsetUnset($key);
90 /**
91 * retrieves database list from server
93 * @todo we could also search mysql tables if all fail?
94 * @param string $like_db_name usally a db_name containing wildcards
96 protected function _retrieve($like_db_name = null)
98 if ($this->_show_databases_disabled) {
99 return array();
102 if (null !== $like_db_name) {
103 $command = "SHOW DATABASES LIKE '" . $like_db_name . "'";
104 } elseif (null === $this->_command) {
105 $command = str_replace('#user#', $GLOBALS['cfg']['Server']['user'],
106 $GLOBALS['cfg']['Server']['ShowDatabasesCommand']);
107 $this->_command = $command;
108 } else {
109 $command = $this->_command;
112 $database_list = PMA_DBI_fetch_result($command, null, null, $this->_db_link);
113 PMA_DBI_getError();
115 if ($GLOBALS['errno'] !== 0) {
116 // failed to get database list, try the control user
117 // (hopefully there is one and he has SHOW DATABASES right)
118 $this->_db_link = $this->_db_link_control;
119 $database_list = PMA_DBI_fetch_result($command, null, null, $this->_db_link);
121 PMA_DBI_getError();
123 if ($GLOBALS['errno'] !== 0) {
124 // failed! we will display a warning that phpMyAdmin could not safely
125 // retrieve database list, the admin has to setup a control user or
126 // allow SHOW DATABASES
127 $GLOBALS['error_showdatabases'] = true;
128 $this->_show_databases_disabled = true;
132 if ($GLOBALS['cfg']['NaturalOrder']) {
133 natsort($database_list);
134 } else {
135 // need to sort anyway, otherwise information_schema
136 // goes at the top
137 sort($database_list);
140 return $database_list;
144 * builds up the list
147 public function build()
149 if (! $this->_checkOnlyDatabase()) {
150 $items = $this->_retrieve();
151 $this->exchangeArray($items);
154 $this->_checkHideDatabase();
158 * checks the only_db configuration
160 * @return boolean false if there is no only_db, otherwise true
162 protected function _checkOnlyDatabase()
164 if (is_string($GLOBALS['cfg']['Server']['only_db'])
165 && strlen($GLOBALS['cfg']['Server']['only_db'])) {
166 $GLOBALS['cfg']['Server']['only_db'] = array(
167 $GLOBALS['cfg']['Server']['only_db']
171 if (! is_array($GLOBALS['cfg']['Server']['only_db'])) {
172 return false;
175 $items = array();
177 foreach ($GLOBALS['cfg']['Server']['only_db'] as $each_only_db) {
178 if ($each_only_db === '*' && ! $this->_show_databases_disabled) {
179 // append all not already listed dbs to the list
180 $items = array_merge($items,
181 array_diff($this->_retrieve(), $items));
182 // there can only be one '*', and this can only be last
183 break;
186 // check if the db name contains wildcard,
187 // thus containing not escaped _ or %
188 if (! preg_match('/(^|[^\\\\])(_|%)/', $each_only_db)) {
189 // ... not contains wildcard
190 $items[] = PMA_unescape_mysql_wildcards($each_only_db);
191 continue;
194 if (! $this->_show_databases_disabled) {
195 $items = array_merge($items, $this->_retrieve($each_only_db));
196 continue;
199 // @todo induce error, about not using wildcards with SHOW DATABASE disabled?
202 $this->exchangeArray($items);
204 return true;
208 * returns default item
210 * @return string default item
212 public function getDefault()
214 if (strlen($GLOBALS['db'])) {
215 return $GLOBALS['db'];
218 return $this->getEmpty();
222 * returns array with dbs grouped with extended infos
224 * @param integer $offset
225 * @param integer $count
226 * @return array db list
228 public function getGroupedDetails($offset, $count)
230 $dbgroups = array();
231 $parts = array();
233 if ($GLOBALS['cfg']['ShowTooltip']
234 && $GLOBALS['cfgRelation']['commwork']) {
235 $db_tooltips = PMA_getDbComments();
238 if (!$GLOBALS['cfg']['LeftFrameDBTree']) {
239 $separators = array();
240 } elseif (is_array($GLOBALS['cfg']['LeftFrameDBSeparator'])) {
241 $separators = $GLOBALS['cfg']['LeftFrameDBSeparator'];
242 } elseif (!empty($GLOBALS['cfg']['LeftFrameDBSeparator'])) {
243 $separators = array($GLOBALS['cfg']['LeftFrameDBSeparator']);
244 } else {
245 $separators = array();
248 foreach ($this->getLimitedItems($offset, $count) as $key => $db) {
249 // Get comments from PMA comments table
250 $db_tooltip = '';
252 if (isset($db_tooltips[$db])) {
253 $db_tooltip = $db_tooltips[$db];
256 $pos = false;
258 foreach($separators as $separator) {
259 // use strpos instead of strrpos; it seems more common to
260 // have the db name, the separator, then the rest which
261 // might contain a separator
262 // like dbname_the_rest
263 $pos = strpos($db, $separator, 1);
265 if ($pos !== false) {
266 break;
270 if ($pos !== false) {
271 $group = substr($db, 0, $pos);
272 $disp_name_cut = substr($db, $pos);
273 } else {
274 $group = $db;
275 $disp_name_cut = $db;
278 $disp_name = $db;
279 if ($db_tooltip && $GLOBALS['cfg']['ShowTooltipAliasDB']) {
280 $disp_name = $db_tooltip;
281 $disp_name_cut = $db_tooltip;
282 $db_tooltip = $db;
285 $dbgroups[$group][$db] = array(
286 'name' => $db,
287 'disp_name_cut' => $disp_name_cut,
288 'disp_name' => $disp_name,
289 'comment' => $db_tooltip,
292 if ($GLOBALS['cfg']['Server']['CountTables']) {
293 $dbgroups[$group][$db]['num_tables'] = PMA_getTableCount($db);
295 } // end foreach ($GLOBALS['PMA_List_Database']->items as $db)
296 return $dbgroups;
300 * returns a part of the items
302 * @param integer $offset
303 * @param integer $count
304 * @return array some items
306 public function getLimitedItems($offset, $count)
308 return array_slice($this->getArrayCopy(), $offset, $count);
312 * returns html code for list with dbs
314 * @return string html code list
316 public function getHtmlListGrouped($selected = '', $offset, $count)
318 if (true === $selected) {
319 $selected = $this->getDefault();
322 $return = '<ul id="databaseList" xml:lang="en" dir="ltr">' . "\n";
323 foreach ($this->getGroupedDetails($offset, $count) as $group => $dbs) {
324 if (count($dbs) > 1) {
325 $return .= '<li class="group"><span>' . htmlspecialchars($group) . '</span><ul>' . "\n";
326 // whether display db_name cut by the group part
327 $cut = true;
328 } else {
329 // .. or full
330 $cut = false;
332 foreach ($dbs as $db) {
333 $return .= '<li';
334 if ($db['name'] == $selected) {
335 $return .= ' class="selected"';
337 $return .= '><a';
338 if (! empty($db['comment'])) {
339 $return .= ' title="' . htmlspecialchars($db['comment']) . '"';
341 $return .= ' href="index.php?' . PMA_generate_common_url($db['name'])
342 . '" target="_parent">';
343 if ($cut) {
344 $return .= htmlspecialchars($db['disp_name_cut']);
345 } else {
346 $return .= htmlspecialchars($db['disp_name']);
349 if (! empty($db['num_tables'])) {
350 $return .= ' (' . $db['num_tables'] . ')';
352 $return .= '</a></li>' . "\n";
354 if (count($dbs) > 1) {
355 $return .= '</ul></li>' . "\n";
358 $return .= '</ul>';
360 return $return;
364 * returns html code for select form element with dbs
366 * @todo IE can not handle different text directions in select boxes so,
367 * as mostly names will be in english, we set the whole selectbox to LTR
368 * and EN
370 * @return string html code select
372 public function getHtmlSelectGrouped($selected = '', $offset, $count)
374 if (true === $selected) {
375 $selected = $this->getDefault();
378 $return = '<select name="db" id="lightm_db" xml:lang="en" dir="ltr"'
379 . ' onchange="if (this.value != \'\') window.parent.openDb(this.value);">' . "\n"
380 . '<option value="" dir="' . $GLOBALS['text_dir'] . '">'
381 . '(' . __('Databases') . ') ...</option>' . "\n";
382 foreach ($this->getGroupedDetails($offset, $count) as $group => $dbs) {
383 if (count($dbs) > 1) {
384 $return .= '<optgroup label="' . htmlspecialchars($group)
385 . '">' . "\n";
386 // whether display db_name cuted by the group part
387 $cut = true;
388 } else {
389 // .. or full
390 $cut = false;
392 foreach ($dbs as $db) {
393 $return .= '<option value="' . htmlspecialchars($db['name']) . '"'
394 .' title="' . htmlspecialchars($db['comment']) . '"';
395 if ($db['name'] == $selected || (PMA_DRIZZLE && strtolower($db['name']) == strtolower($selected))) {
396 $return .= ' selected="selected"';
398 $return .= '>' . htmlspecialchars($cut ? $db['disp_name_cut'] : $db['disp_name']);
399 if (! empty($db['num_tables'])) {
400 $return .= ' (' . $db['num_tables'] . ')';
402 $return .= '</option>' . "\n";
404 if (count($dbs) > 1) {
405 $return .= '</optgroup>' . "\n";
408 $return .= '</select>';
410 return $return;
414 * this is just a backup, if all is fine this can be deleted later
416 * @deprecated
418 protected function _checkAgainstPrivTables()
420 // 1. get allowed dbs from the "mysql.db" table
421 // User can be blank (anonymous user)
422 $local_query = "
423 SELECT DISTINCT `Db` FROM `mysql`.`db`
424 WHERE `Select_priv` = 'Y'
425 AND `User`
426 IN ('" . PMA_sqlAddSlashes($GLOBALS['cfg']['Server']['user']) . "', '')";
427 $tmp_mydbs = PMA_DBI_fetch_result($local_query, null, null,
428 $GLOBALS['controllink']);
429 if ($tmp_mydbs) {
430 // Will use as associative array of the following 2 code
431 // lines:
432 // the 1st is the only line intact from before
433 // correction,
434 // the 2nd replaces $dblist[] = $row['Db'];
436 // Code following those 2 lines in correction continues
437 // populating $dblist[], as previous code did. But it is
438 // now populated with actual database names instead of
439 // with regular expressions.
440 $tmp_alldbs = PMA_DBI_query('SHOW DATABASES;', $GLOBALS['controllink']);
441 // all databases cases - part 2
442 if (isset($tmp_mydbs['%'])) {
443 while ($tmp_row = PMA_DBI_fetch_row($tmp_alldbs)) {
444 $dblist[] = $tmp_row[0];
445 } // end while
446 } else {
447 while ($tmp_row = PMA_DBI_fetch_row($tmp_alldbs)) {
448 $tmp_db = $tmp_row[0];
449 if (isset($tmp_mydbs[$tmp_db]) && $tmp_mydbs[$tmp_db] == 1) {
450 $dblist[] = $tmp_db;
451 $tmp_mydbs[$tmp_db] = 0;
452 } elseif (! isset($dblist[$tmp_db])) {
453 foreach ($tmp_mydbs as $tmp_matchpattern => $tmp_value) {
454 // fixed bad regexp
455 // TODO: db names may contain characters
456 // that are regexp instructions
457 $re = '(^|(\\\\\\\\)+|[^\])';
458 $tmp_regex = preg_replace('/' . addcslashes($re,'/') . '%/', '\\1.*', preg_replace('/' . addcslashes($re,'/') . '_/', '\\1.{1}', $tmp_matchpattern));
459 // Fixed db name matching
460 // 2000-08-28 -- Benjamin Gandon
461 if (preg_match('/^' . addcslashes($tmp_regex,'/') . '$/', $tmp_db)) {
462 $dblist[] = $tmp_db;
463 break;
465 } // end while
466 } // end if ... elseif ...
467 } // end while
468 } // end else
469 PMA_DBI_free_result($tmp_alldbs);
470 unset($tmp_mydbs);
471 } // end if
473 // 2. get allowed dbs from the "mysql.tables_priv" table
474 $local_query = 'SELECT DISTINCT Db FROM mysql.tables_priv WHERE Table_priv LIKE \'%Select%\' AND User = \'' . PMA_sqlAddSlashes($GLOBALS['cfg']['Server']['user']) . '\'';
475 $rs = PMA_DBI_try_query($local_query, $GLOBALS['controllink']);
476 if ($rs && @PMA_DBI_num_rows($rs)) {
477 while ($row = PMA_DBI_fetch_assoc($rs)) {
478 if (!in_array($row['Db'], $dblist)) {
479 $dblist[] = $row['Db'];
481 } // end while
482 PMA_DBI_free_result($rs);
483 } // end if