Fix for the Open in New Window in Patient/Client->Patients search gui, take 2.
[openemr.git] / phpmyadmin / libraries / List_Database.class.php
blob52c9c837798c03dc3e20f7c0b1d89e610259db1e
1 <?php
2 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 /**
4 * holds the PMA_List_Database class
6 * @version $Id$
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
27 /*public*/ class PMA_List_Database extends PMA_List
29 /**
30 * @var mixed database link resource|object to be used
31 * @access protected
33 var $_db_link = null;
35 /**
36 * @var mixed user database link resource|object
37 * @access protected
39 var $_db_link_user = null;
41 /**
42 * @var mixed controluser database link resource|object
43 * @access protected
45 var $_db_link_control = null;
47 /**
48 * @var boolean whether SHOW DATABASES is disabled or not
49 * @access protected
51 var $_show_databases_disabled = false;
53 /**
54 * Constructor
56 * @uses PMA_List_Database::$_db_link
57 * @uses PMA_List_Database::$_db_link_user
58 * @uses PMA_List_Database::$_db_link_control
59 * @uses PMA_List_Database::build()
60 * @param mixed $db_link_user user database link resource|object
61 * @param mixed $db_link_control control database link resource|object
63 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 $this->build();
71 /**
72 * old PHP 4 style constructor
74 * @see PMA_List_Database::__construct()
76 function PMA_List_Database($db_link_user = null, $db_link_control = null) {
77 $this->__construct($db_link_user, $db_link_control);
80 /**
81 * removes all databases not accessible by current user from list
83 * @access protected
84 * @uses PMA_List_Database::$items
85 * @uses PMA_List_Database::$_db_link_user
86 * @uses PMA_List_Database::$_need_to_reindex to set it if reuqired
87 * @uses PMA_DBI_select_db()
89 function _checkAccess()
91 foreach ($this->items as $key => $db) {
92 if (! @PMA_DBI_select_db($db, $this->_db_link_user)) {
93 unset($this->items[$key]);
97 // re-index values
98 $this->_need_to_reindex = true;
102 * checks if the configuration wants to hide some databases
104 * @todo temporaly use this docblock to test how to doc $GLOBALS
105 * @access protected
106 * @uses PMA_List_Database::$items
107 * @uses PMA_List_Database::$_need_to_reindex to set it if reuqired
108 * @uses preg_match()
109 * @uses $cfg['Server']['hide_db']
111 function _checkHideDatabase()
113 if (empty($GLOBALS['cfg']['Server']['hide_db'])) {
114 return;
117 foreach ($this->items as $key => $db) {
118 if (preg_match('/' . $GLOBALS['cfg']['Server']['hide_db'] . '/', $db)) {
119 unset($this->items[$key]);
122 // re-index values
123 $this->_need_to_reindex = true;
127 * retrieves database list from server
129 * @todo we could also search mysql tables if all fail?
130 * @access protected
131 * @uses PMA_List_Database::$_show_databases_disabled for not retrying if SHOW DATABASES is disabled
132 * @uses PMA_List_Database::$_db_link
133 * @uses PMA_List_Database::$_db_link_control in case of SHOW DATABASES is disabled for userlink
134 * @uses PMA_DBI_fetch_result()
135 * @uses PMA_DBI_getError()
136 * @uses $GLOBALS['error_showdatabases']
137 * @uses $GLOBALS['errno']
138 * @param string $like_db_name usally a db_name containing wildcards
140 function _retrieve($like_db_name = '')
142 if ($this->_show_databases_disabled) {
143 return array();
146 if (! empty($like_db_name)) {
147 $like = " LIKE '" . $like_db_name . "';";
148 } else {
149 $like = ";";
152 $database_list = PMA_DBI_fetch_result('SHOW DATABASES' . $like, null, null, $this->_db_link);
153 PMA_DBI_getError();
155 if ($GLOBALS['errno'] !== 0) {
156 // failed to get database list, try the control user
157 // (hopefully there is one and he has SHOW DATABASES right)
158 $this->_db_link = $this->_db_link_control;
159 $database_list = PMA_DBI_fetch_result('SHOW DATABASES' . $like, null, null, $this->_db_link);
161 PMA_DBI_getError();
163 if ($GLOBALS['errno'] !== 0) {
164 // failed! we will display a warning that phpMyAdmin could not safely
165 // retrieve database list, the admin has to setup a control user or
166 // allow SHOW DATABASES
167 $GLOBALS['error_showdatabases'] = true;
168 $this->_show_databases_disabled = true;
172 return $database_list;
176 * builds up the list
178 * @uses PMA_List_Database::$items to initialize it
179 * @uses PMA_List_Database::$_need_to_reindex
180 * @uses PMA_List_Database::_checkOnlyDatabase()
181 * @uses PMA_List_Database::_retrieve()
182 * @uses PMA_List_Database::_checkHideDatabase()
183 * @uses PMA_List_Database::_checkAccess()
184 * @uses PMA_MYSQL_INT_VERSION
185 * @uses array_values()
186 * @uses natsort()
187 * @uses $cfg['NaturalOrder']
189 function build()
191 $this->items = array();
193 if (! $this->_checkOnlyDatabase()) {
194 $this->items = $this->_retrieve();
195 if ($GLOBALS['cfg']['NaturalOrder']) {
196 natsort($this->items);
197 $this->_need_to_reindex = true;
201 $this->_checkHideDatabase();
203 // Before MySQL 4.0.2, SHOW DATABASES could send the
204 // whole list, so check if we really have access:
205 if (PMA_MYSQL_INT_VERSION < 40002) {
206 $this->_checkAccess();
209 if ($this->_need_to_reindex) {
210 $this->items = array_values($this->items);
215 * checks the only_db configuration
217 * @uses PMA_List_Database::$_show_databases_disabled
218 * @uses PMA_List_Database::$items
219 * @uses PMA_List_Database::_retrieve()
220 * @uses PMA_unescape_mysql_wildcards()
221 * @uses preg_match()
222 * @uses array_diff()
223 * @uses array_merge()
224 * @uses is_array()
225 * @uses strlen()
226 * @uses is_string()
227 * @uses $cfg['Server']['only_db']
228 * @return boolean false if there is no only_db, otherwise true
230 function _checkOnlyDatabase()
232 if (is_string($GLOBALS['cfg']['Server']['only_db'])
233 && strlen($GLOBALS['cfg']['Server']['only_db'])) {
234 $GLOBALS['cfg']['Server']['only_db'] = array(
235 $GLOBALS['cfg']['Server']['only_db']
239 if (! is_array($GLOBALS['cfg']['Server']['only_db'])) {
240 return false;
243 foreach ($GLOBALS['cfg']['Server']['only_db'] as $each_only_db) {
244 if ($each_only_db === '*' && ! $this->_show_databases_disabled) {
245 // append all not already listed dbs to the list
246 $this->items = array_merge($this->items,
247 array_diff($this->_retrieve(), $this->items));
248 // there can only be one '*', and this can only be last
249 break;
252 // check if the db name contains wildcard,
253 // thus containing not escaped _ or %
254 if (! preg_match('/(^|[^\\\\])(_|%)/', $each_only_db)) {
255 // ... not contains wildcard
256 $this->items[] = PMA_unescape_mysql_wildcards($each_only_db);
257 continue;
260 if (! $this->_show_databases_disabled) {
261 $this->items = array_merge($this->items, $this->_retrieve($each_only_db));
262 continue;
265 // @todo induce error, about not using wildcards with SHOW DATABASE disabled?
268 return true;
272 * returns default item
274 * @uses PMA_List::getEmpty()
275 * @uses $GLOBALS['db']
276 * @uses strlen()
277 * @return string default item
279 function getDefault()
281 if (strlen($GLOBALS['db'])) {
282 return $GLOBALS['db'];
285 return $this->getEmpty();
289 * returns array with dbs grouped with extended infos
291 * @uses $GLOBALS['PMA_List_Database']
292 * @uses $GLOBALS['cfgRelation']['commwork']
293 * @uses $cfg['ShowTooltip']
294 * @uses $cfg['LeftFrameDBTree']
295 * @uses $cfg['LeftFrameDBSeparator']
296 * @uses $cfg['ShowTooltipAliasDB']
297 * @uses PMA_getTableCount()
298 * @uses PMA_getComments()
299 * @uses is_array()
300 * @uses implode()
301 * @uses strstr()
302 * @uses explode()
303 * @param integer $offset
304 * @param integer $count
305 * @return array db list
307 function getGroupedDetails($offset, $count)
309 $dbgroups = array();
310 $parts = array();
311 foreach ($this->getLimitedItems($offset, $count) as $key => $db) {
312 // garvin: Get comments from PMA comments table
313 $db_tooltip = '';
314 if ($GLOBALS['cfg']['ShowTooltip']
315 && $GLOBALS['cfgRelation']['commwork']) {
316 $_db_tooltip = PMA_getComments($db);
317 if (is_array($_db_tooltip)) {
318 $db_tooltip = implode(' ', $_db_tooltip);
322 if ($GLOBALS['cfg']['LeftFrameDBTree']
323 && $GLOBALS['cfg']['LeftFrameDBSeparator']
324 && strstr($db, $GLOBALS['cfg']['LeftFrameDBSeparator']))
326 // use strpos instead of strrpos; it seems more common to
327 // have the db name, the separator, then the rest which
328 // might contain a separator
329 // like dbname_the_rest
330 $pos = strpos($db, $GLOBALS['cfg']['LeftFrameDBSeparator']);
331 $group = substr($db, 0, $pos);
332 $disp_name_cut = substr($db, $pos);
333 } else {
334 $group = $db;
335 $disp_name_cut = $db;
338 $disp_name = $db;
339 if ($db_tooltip && $GLOBALS['cfg']['ShowTooltipAliasDB']) {
340 $disp_name = $db_tooltip;
341 $disp_name_cut = $db_tooltip;
342 $db_tooltip = $db;
345 $dbgroups[$group][$db] = array(
346 'name' => $db,
347 'disp_name_cut' => $disp_name_cut,
348 'disp_name' => $disp_name,
349 'comment' => $db_tooltip,
350 'num_tables' => PMA_getTableCount($db),
352 } // end foreach ($GLOBALS['PMA_List_Database']->items as $db)
353 return $dbgroups;
357 * returns a part of the items
359 * @uses PMA_List_Database::$items
360 * @uses array_slice()
361 * @param integer $offset
362 * @param integer $count
363 * @return array some items
365 function getLimitedItems($offset, $count)
367 return(array_slice($this->items, $offset, $count));
371 * returns html code for list with dbs
373 * @return string html code list
375 function getHtmlListGrouped($selected = '', $offset, $count)
377 if (true === $selected) {
378 $selected = $this->getDefault();
381 $return = '<ul id="databaseList" xml:lang="en" dir="ltr">' . "\n";
382 foreach ($this->getGroupedDetails($offset, $count) as $group => $dbs) {
383 if (count($dbs) > 1) {
384 $return .= '<li>' . htmlspecialchars($group) . '<ul>' . "\n";
385 // wether display db_name cuted by the group part
386 $cut = true;
387 } else {
388 // .. or full
389 $cut = false;
391 foreach ($dbs as $db) {
392 $return .= '<li';
393 if ($db['name'] == $selected) {
394 $return .= ' class="selected"';
396 $return .= '><a';
397 if (! empty($db['comment'])) {
398 $return .= ' title="' . htmlspecialchars($db['comment']) . '"';
400 $return .= ' href="index.php?' . PMA_generate_common_url($db['name'])
401 . '" target="_parent">';
402 if ($cut) {
403 $return .= htmlspecialchars($db['disp_name_cut']);
404 } else {
405 $return .= htmlspecialchars($db['disp_name']);
407 $return .= ' (' . $db['num_tables'] . ')';
408 $return .= '</a></li>' . "\n";
410 if (count($dbs) > 1) {
411 $return .= '</ul></li>' . "\n";
414 $return .= '</ul>';
416 return $return;
420 * returns html code for select form element with dbs
422 * @todo IE can not handle different text directions in select boxes so,
423 * as mostly names will be in english, we set the whole selectbox to LTR
424 * and EN
426 * @return string html code select
428 function getHtmlSelectGrouped($selected = '', $offset, $count)
430 if (true === $selected) {
431 $selected = $this->getDefault();
434 $return = '<select name="db" id="lightm_db" xml:lang="en" dir="ltr"'
435 . ' onchange="if (this.value != \'\') window.parent.openDb(this.value);">' . "\n"
436 . '<option value="" dir="' . $GLOBALS['text_dir'] . '">'
437 . '(' . $GLOBALS['strDatabases'] . ') ...</option>' . "\n";
438 foreach ($this->getGroupedDetails($offset, $count) as $group => $dbs) {
439 if (count($dbs) > 1) {
440 $return .= '<optgroup label="' . htmlspecialchars($group)
441 . '">' . "\n";
442 // wether display db_name cuted by the group part
443 $cut = true;
444 } else {
445 // .. or full
446 $cut = false;
448 foreach ($dbs as $db) {
449 $return .= '<option value="' . htmlspecialchars($db['name']) . '"'
450 .' title="' . htmlspecialchars($db['comment']) . '"';
451 if ($db['name'] == $selected) {
452 $return .= ' selected="selected"';
454 $return .= '>' . htmlspecialchars($cut ? $db['disp_name_cut'] : $db['disp_name'])
455 .' (' . $db['num_tables'] . ')</option>' . "\n";
457 if (count($dbs) > 1) {
458 $return .= '</optgroup>' . "\n";
461 $return .= '</select>';
463 return $return;
467 * this is just a backup, if all is fine this can be deleted later
469 * @deprecated
470 * @access protected
472 function _checkAgainstPrivTables()
474 // 1. get allowed dbs from the "mysql.db" table
475 // lem9: User can be blank (anonymous user)
476 $local_query = "
477 SELECT DISTINCT `Db` FROM `mysql`.`db`
478 WHERE `Select_priv` = 'Y'
479 AND `User`
480 IN ('" . PMA_sqlAddslashes($GLOBALS['cfg']['Server']['user']) . "', '')";
481 $tmp_mydbs = PMA_DBI_fetch_result($local_query, null, null,
482 $GLOBALS['controllink']);
483 if ($tmp_mydbs) {
484 // Will use as associative array of the following 2 code
485 // lines:
486 // the 1st is the only line intact from before
487 // correction,
488 // the 2nd replaces $dblist[] = $row['Db'];
490 // Code following those 2 lines in correction continues
491 // populating $dblist[], as previous code did. But it is
492 // now populated with actual database names instead of
493 // with regular expressions.
494 var_dump($tmp_mydbs);
495 $tmp_alldbs = PMA_DBI_query('SHOW DATABASES;', $GLOBALS['controllink']);
496 // loic1: all databases cases - part 2
497 if (isset($tmp_mydbs['%'])) {
498 while ($tmp_row = PMA_DBI_fetch_row($tmp_alldbs)) {
499 $dblist[] = $tmp_row[0];
500 } // end while
501 } else {
502 while ($tmp_row = PMA_DBI_fetch_row($tmp_alldbs)) {
503 $tmp_db = $tmp_row[0];
504 if (isset($tmp_mydbs[$tmp_db]) && $tmp_mydbs[$tmp_db] == 1) {
505 $dblist[] = $tmp_db;
506 $tmp_mydbs[$tmp_db] = 0;
507 } elseif (!isset($dblist[$tmp_db])) {
508 foreach ($tmp_mydbs as $tmp_matchpattern => $tmp_value) {
509 // loic1: fixed bad regexp
510 // TODO: db names may contain characters
511 // that are regexp instructions
512 $re = '(^|(\\\\\\\\)+|[^\])';
513 $tmp_regex = ereg_replace($re . '%', '\\1.*', ereg_replace($re . '_', '\\1.{1}', $tmp_matchpattern));
514 // Fixed db name matching
515 // 2000-08-28 -- Benjamin Gandon
516 if (ereg('^' . $tmp_regex . '$', $tmp_db)) {
517 $dblist[] = $tmp_db;
518 break;
520 } // end while
521 } // end if ... elseif ...
522 } // end while
523 } // end else
524 PMA_DBI_free_result($tmp_alldbs);
525 unset($tmp_mydbs);
526 } // end if
528 // 2. get allowed dbs from the "mysql.tables_priv" table
529 $local_query = 'SELECT DISTINCT Db FROM mysql.tables_priv WHERE Table_priv LIKE \'%Select%\' AND User = \'' . PMA_sqlAddslashes($GLOBALS['cfg']['Server']['user']) . '\'';
530 $rs = PMA_DBI_try_query($local_query, $GLOBALS['controllink']);
531 if ($rs && @PMA_DBI_num_rows($rs)) {
532 while ($row = PMA_DBI_fetch_assoc($rs)) {
533 if (!in_array($row['Db'], $dblist)) {
534 $dblist[] = $row['Db'];
536 } // end while
537 PMA_DBI_free_result($rs);
538 } // end if