Relation.php 76 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Set of functions used with the relation and PDF feature
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. namespace PhpMyAdmin;
  9. use PhpMyAdmin\Core;
  10. use PhpMyAdmin\DatabaseInterface;
  11. use PhpMyAdmin\Message;
  12. use PhpMyAdmin\RecentFavoriteTable;
  13. use PhpMyAdmin\SqlParser\Parser;
  14. use PhpMyAdmin\SqlParser\Utils\Table as TableUtils;
  15. use PhpMyAdmin\Table;
  16. use PhpMyAdmin\Template;
  17. use PhpMyAdmin\Url;
  18. use PhpMyAdmin\Util;
  19. /**
  20. * Set of functions used with the relation and PDF feature
  21. *
  22. * @package PhpMyAdmin
  23. */
  24. class Relation
  25. {
  26. /**
  27. * Executes a query as controluser if possible, otherwise as normal user
  28. *
  29. * @param string $sql the query to execute
  30. * @param boolean $show_error whether to display SQL error messages or not
  31. * @param int $options query options
  32. *
  33. * @return resource|boolean the result set, or false if no result set
  34. *
  35. * @access public
  36. *
  37. */
  38. public function queryAsControlUser($sql, $show_error = true, $options = 0)
  39. {
  40. // Avoid caching of the number of rows affected; for example, this function
  41. // is called for tracking purposes but we want to display the correct number
  42. // of rows affected by the original query, not by the query generated for
  43. // tracking.
  44. $cache_affected_rows = false;
  45. if ($show_error) {
  46. $result = $GLOBALS['dbi']->query(
  47. $sql,
  48. DatabaseInterface::CONNECT_CONTROL,
  49. $options,
  50. $cache_affected_rows
  51. );
  52. } else {
  53. $result = @$GLOBALS['dbi']->tryQuery(
  54. $sql,
  55. DatabaseInterface::CONNECT_CONTROL,
  56. $options,
  57. $cache_affected_rows
  58. );
  59. } // end if... else...
  60. if ($result) {
  61. return $result;
  62. }
  63. return false;
  64. }
  65. /**
  66. * Returns current relation parameters
  67. *
  68. * @return array $cfgRelation
  69. */
  70. public function getRelationsParam()
  71. {
  72. if (empty($_SESSION['relation'][$GLOBALS['server']])
  73. || (empty($_SESSION['relation'][$GLOBALS['server']]['PMA_VERSION']))
  74. || $_SESSION['relation'][$GLOBALS['server']]['PMA_VERSION'] != PMA_VERSION
  75. ) {
  76. $_SESSION['relation'][$GLOBALS['server']] = $this->checkRelationsParam();
  77. }
  78. // just for BC but needs to be before getRelationsParamDiagnostic()
  79. // which uses it
  80. $GLOBALS['cfgRelation'] = $_SESSION['relation'][$GLOBALS['server']];
  81. return $_SESSION['relation'][$GLOBALS['server']];
  82. }
  83. /**
  84. * prints out diagnostic info for pma relation feature
  85. *
  86. * @param array $cfgRelation Relation configuration
  87. *
  88. * @return string
  89. */
  90. public function getRelationsParamDiagnostic(array $cfgRelation)
  91. {
  92. $retval = '<br>';
  93. $messages = array();
  94. $messages['error'] = '<span style="color:red"><strong>'
  95. . __('not OK')
  96. . '</strong></span>';
  97. $messages['ok'] = '<span style="color:green"><strong>'
  98. . _pgettext('Correctly working', 'OK')
  99. . '</strong></span>';
  100. $messages['enabled'] = '<span style="color:green">' . __('Enabled') . '</span>';
  101. $messages['disabled'] = '<span style="color:red">' . __('Disabled') . '</span>';
  102. if (strlen($cfgRelation['db']) == 0) {
  103. $retval .= __('Configuration of pmadb…') . ' '
  104. . $messages['error']
  105. . Util::showDocu('setup', 'linked-tables')
  106. . '<br />' . "\n"
  107. . __('General relation features')
  108. . ' <font color="green">' . __('Disabled')
  109. . '</font>' . "\n";
  110. if ($GLOBALS['cfg']['ZeroConf']) {
  111. if (strlen($GLOBALS['db']) == 0) {
  112. $retval .= $this->getHtmlFixPmaTables(true, true);
  113. } else {
  114. $retval .= $this->getHtmlFixPmaTables(true);
  115. }
  116. }
  117. } else {
  118. $retval .= '<table>' . "\n";
  119. if (! $cfgRelation['allworks']
  120. && $GLOBALS['cfg']['ZeroConf']
  121. // Avoid showing a "Create missing tables" link if it's a
  122. // problem of missing definition
  123. && $this->arePmadbTablesDefined()
  124. ) {
  125. $retval .= $this->getHtmlFixPmaTables(false);
  126. $retval .= '<br />';
  127. }
  128. $retval .= $this->getDiagMessageForParameter(
  129. 'pmadb',
  130. $cfgRelation['db'],
  131. $messages,
  132. 'pmadb'
  133. );
  134. $retval .= $this->getDiagMessageForParameter(
  135. 'relation',
  136. isset($cfgRelation['relation']),
  137. $messages,
  138. 'relation'
  139. );
  140. $retval .= $this->getDiagMessageForFeature(
  141. __('General relation features'),
  142. 'relwork',
  143. $messages
  144. );
  145. $retval .= $this->getDiagMessageForParameter(
  146. 'table_info',
  147. isset($cfgRelation['table_info']),
  148. $messages,
  149. 'table_info'
  150. );
  151. $retval .= $this->getDiagMessageForFeature(
  152. __('Display Features'),
  153. 'displaywork',
  154. $messages
  155. );
  156. $retval .= $this->getDiagMessageForParameter(
  157. 'table_coords',
  158. isset($cfgRelation['table_coords']),
  159. $messages,
  160. 'table_coords'
  161. );
  162. $retval .= $this->getDiagMessageForParameter(
  163. 'pdf_pages',
  164. isset($cfgRelation['pdf_pages']),
  165. $messages,
  166. 'pdf_pages'
  167. );
  168. $retval .= $this->getDiagMessageForFeature(
  169. __('Designer and creation of PDFs'),
  170. 'pdfwork',
  171. $messages
  172. );
  173. $retval .= $this->getDiagMessageForParameter(
  174. 'column_info',
  175. isset($cfgRelation['column_info']),
  176. $messages,
  177. 'column_info'
  178. );
  179. $retval .= $this->getDiagMessageForFeature(
  180. __('Displaying Column Comments'),
  181. 'commwork',
  182. $messages,
  183. false
  184. );
  185. $retval .= $this->getDiagMessageForFeature(
  186. __('Browser transformation'),
  187. 'mimework',
  188. $messages
  189. );
  190. if ($cfgRelation['commwork'] && ! $cfgRelation['mimework']) {
  191. $retval .= '<tr><td colspan=2 class="left error">';
  192. $retval .= __(
  193. 'Please see the documentation on how to'
  194. . ' update your column_info table.'
  195. );
  196. $retval .= Util::showDocu(
  197. 'config',
  198. 'cfg_Servers_column_info'
  199. );
  200. $retval .= '</td></tr>';
  201. }
  202. $retval .= $this->getDiagMessageForParameter(
  203. 'bookmarktable',
  204. isset($cfgRelation['bookmark']),
  205. $messages,
  206. 'bookmark'
  207. );
  208. $retval .= $this->getDiagMessageForFeature(
  209. __('Bookmarked SQL query'),
  210. 'bookmarkwork',
  211. $messages
  212. );
  213. $retval .= $this->getDiagMessageForParameter(
  214. 'history',
  215. isset($cfgRelation['history']),
  216. $messages,
  217. 'history'
  218. );
  219. $retval .= $this->getDiagMessageForFeature(
  220. __('SQL history'),
  221. 'historywork',
  222. $messages
  223. );
  224. $retval .= $this->getDiagMessageForParameter(
  225. 'recent',
  226. isset($cfgRelation['recent']),
  227. $messages,
  228. 'recent'
  229. );
  230. $retval .= $this->getDiagMessageForFeature(
  231. __('Persistent recently used tables'),
  232. 'recentwork',
  233. $messages
  234. );
  235. $retval .= $this->getDiagMessageForParameter(
  236. 'favorite',
  237. isset($cfgRelation['favorite']),
  238. $messages,
  239. 'favorite'
  240. );
  241. $retval .= $this->getDiagMessageForFeature(
  242. __('Persistent favorite tables'),
  243. 'favoritework',
  244. $messages
  245. );
  246. $retval .= $this->getDiagMessageForParameter(
  247. 'table_uiprefs',
  248. isset($cfgRelation['table_uiprefs']),
  249. $messages,
  250. 'table_uiprefs'
  251. );
  252. $retval .= $this->getDiagMessageForFeature(
  253. __('Persistent tables\' UI preferences'),
  254. 'uiprefswork',
  255. $messages
  256. );
  257. $retval .= $this->getDiagMessageForParameter(
  258. 'tracking',
  259. isset($cfgRelation['tracking']),
  260. $messages,
  261. 'tracking'
  262. );
  263. $retval .= $this->getDiagMessageForFeature(
  264. __('Tracking'),
  265. 'trackingwork',
  266. $messages
  267. );
  268. $retval .= $this->getDiagMessageForParameter(
  269. 'userconfig',
  270. isset($cfgRelation['userconfig']),
  271. $messages,
  272. 'userconfig'
  273. );
  274. $retval .= $this->getDiagMessageForFeature(
  275. __('User preferences'),
  276. 'userconfigwork',
  277. $messages
  278. );
  279. $retval .= $this->getDiagMessageForParameter(
  280. 'users',
  281. isset($cfgRelation['users']),
  282. $messages,
  283. 'users'
  284. );
  285. $retval .= $this->getDiagMessageForParameter(
  286. 'usergroups',
  287. isset($cfgRelation['usergroups']),
  288. $messages,
  289. 'usergroups'
  290. );
  291. $retval .= $this->getDiagMessageForFeature(
  292. __('Configurable menus'),
  293. 'menuswork',
  294. $messages
  295. );
  296. $retval .= $this->getDiagMessageForParameter(
  297. 'navigationhiding',
  298. isset($cfgRelation['navigationhiding']),
  299. $messages,
  300. 'navigationhiding'
  301. );
  302. $retval .= $this->getDiagMessageForFeature(
  303. __('Hide/show navigation items'),
  304. 'navwork',
  305. $messages
  306. );
  307. $retval .= $this->getDiagMessageForParameter(
  308. 'savedsearches',
  309. isset($cfgRelation['savedsearches']),
  310. $messages,
  311. 'savedsearches'
  312. );
  313. $retval .= $this->getDiagMessageForFeature(
  314. __('Saving Query-By-Example searches'),
  315. 'savedsearcheswork',
  316. $messages
  317. );
  318. $retval .= $this->getDiagMessageForParameter(
  319. 'central_columns',
  320. isset($cfgRelation['central_columns']),
  321. $messages,
  322. 'central_columns'
  323. );
  324. $retval .= $this->getDiagMessageForFeature(
  325. __('Managing Central list of columns'),
  326. 'centralcolumnswork',
  327. $messages
  328. );
  329. $retval .= $this->getDiagMessageForParameter(
  330. 'designer_settings',
  331. isset($cfgRelation['designer_settings']),
  332. $messages,
  333. 'designer_settings'
  334. );
  335. $retval .= $this->getDiagMessageForFeature(
  336. __('Remembering Designer Settings'),
  337. 'designersettingswork',
  338. $messages
  339. );
  340. $retval .= $this->getDiagMessageForParameter(
  341. 'export_templates',
  342. isset($cfgRelation['export_templates']),
  343. $messages,
  344. 'export_templates'
  345. );
  346. $retval .= $this->getDiagMessageForFeature(
  347. __('Saving export templates'),
  348. 'exporttemplateswork',
  349. $messages
  350. );
  351. $retval .= '</table>' . "\n";
  352. if (! $cfgRelation['allworks']) {
  353. $retval .= '<p>' . __('Quick steps to set up advanced features:')
  354. . '</p>';
  355. $items = array();
  356. $items[] = sprintf(
  357. __(
  358. 'Create the needed tables with the '
  359. . '<code>%screate_tables.sql</code>.'
  360. ),
  361. htmlspecialchars(SQL_DIR)
  362. ) . ' ' . Util::showDocu('setup', 'linked-tables');
  363. $items[] = __('Create a pma user and give access to these tables.') . ' '
  364. . Util::showDocu('config', 'cfg_Servers_controluser');
  365. $items[] = __(
  366. 'Enable advanced features in configuration file '
  367. . '(<code>config.inc.php</code>), for example by '
  368. . 'starting from <code>config.sample.inc.php</code>.'
  369. ) . ' ' . Util::showDocu('setup', 'quick-install');
  370. $items[] = __(
  371. 'Re-login to phpMyAdmin to load the updated configuration file.'
  372. );
  373. $retval .= Template::get('list/unordered')->render(
  374. array('items' => $items,)
  375. );
  376. }
  377. }
  378. return $retval;
  379. }
  380. /**
  381. * prints out one diagnostic message for a feature
  382. *
  383. * @param string $feature_name feature name in a message string
  384. * @param string $relation_parameter the $GLOBALS['cfgRelation'] parameter to check
  385. * @param array $messages utility messages
  386. * @param boolean $skip_line whether to skip a line after the message
  387. *
  388. * @return string
  389. */
  390. public function getDiagMessageForFeature($feature_name,
  391. $relation_parameter, array $messages, $skip_line = true
  392. ) {
  393. $retval = ' <tr><td colspan=2 class="right">' . $feature_name . ': ';
  394. if (isset($GLOBALS['cfgRelation'][$relation_parameter])
  395. && $GLOBALS['cfgRelation'][$relation_parameter]
  396. ) {
  397. $retval .= $messages['enabled'];
  398. } else {
  399. $retval .= $messages['disabled'];
  400. }
  401. $retval .= '</td></tr>';
  402. if ($skip_line) {
  403. $retval .= '<tr><td>&nbsp;</td></tr>';
  404. }
  405. return $retval;
  406. }
  407. /**
  408. * prints out one diagnostic message for a configuration parameter
  409. *
  410. * @param string $parameter config parameter name to display
  411. * @param boolean $relationParameterSet whether this parameter is set
  412. * @param array $messages utility messages
  413. * @param string $docAnchor anchor in documentation
  414. *
  415. * @return string
  416. */
  417. public function getDiagMessageForParameter($parameter,
  418. $relationParameterSet, array $messages, $docAnchor
  419. ) {
  420. $retval = '<tr><th class="left">';
  421. $retval .= '$cfg[\'Servers\'][$i][\'' . $parameter . '\'] ... ';
  422. $retval .= '</th><td class="right">';
  423. if ($relationParameterSet) {
  424. $retval .= $messages['ok'];
  425. } else {
  426. $retval .= sprintf(
  427. $messages['error'],
  428. Util::getDocuLink('config', 'cfg_Servers_' . $docAnchor)
  429. );
  430. }
  431. $retval .= '</td></tr>' . "\n";
  432. return $retval;
  433. }
  434. /**
  435. * Defines the relation parameters for the current user
  436. * just a copy of the functions used for relations ;-)
  437. * but added some stuff to check what will work
  438. *
  439. * @access protected
  440. * @return array the relation parameters for the current user
  441. */
  442. public function checkRelationsParam()
  443. {
  444. $cfgRelation = array();
  445. $cfgRelation['PMA_VERSION'] = PMA_VERSION;
  446. $workToTable = array(
  447. 'relwork' => 'relation',
  448. 'displaywork' => array('relation', 'table_info'),
  449. 'bookmarkwork' => 'bookmarktable',
  450. 'pdfwork' => array('table_coords', 'pdf_pages'),
  451. 'commwork' => 'column_info',
  452. 'mimework' => 'column_info',
  453. 'historywork' => 'history',
  454. 'recentwork' => 'recent',
  455. 'favoritework' => 'favorite',
  456. 'uiprefswork' => 'table_uiprefs',
  457. 'trackingwork' => 'tracking',
  458. 'userconfigwork' => 'userconfig',
  459. 'menuswork' => array('users', 'usergroups'),
  460. 'navwork' => 'navigationhiding',
  461. 'savedsearcheswork' => 'savedsearches',
  462. 'centralcolumnswork' => 'central_columns',
  463. 'designersettingswork' => 'designer_settings',
  464. 'exporttemplateswork' => 'export_templates',
  465. );
  466. foreach ($workToTable as $work => $table) {
  467. $cfgRelation[$work] = false;
  468. }
  469. $cfgRelation['allworks'] = false;
  470. $cfgRelation['user'] = null;
  471. $cfgRelation['db'] = null;
  472. if ($GLOBALS['server'] == 0
  473. || empty($GLOBALS['cfg']['Server']['pmadb'])
  474. || ! $GLOBALS['dbi']->selectDb(
  475. $GLOBALS['cfg']['Server']['pmadb'], DatabaseInterface::CONNECT_CONTROL
  476. )
  477. ) {
  478. // No server selected -> no bookmark table
  479. // we return the array with the falses in it,
  480. // to avoid some 'Uninitialized string offset' errors later
  481. $GLOBALS['cfg']['Server']['pmadb'] = false;
  482. return $cfgRelation;
  483. }
  484. $cfgRelation['user'] = $GLOBALS['cfg']['Server']['user'];
  485. $cfgRelation['db'] = $GLOBALS['cfg']['Server']['pmadb'];
  486. // Now I just check if all tables that i need are present so I can for
  487. // example enable relations but not pdf...
  488. // I was thinking of checking if they have all required columns but I
  489. // fear it might be too slow
  490. $tab_query = 'SHOW TABLES FROM '
  491. . Util::backquote(
  492. $GLOBALS['cfg']['Server']['pmadb']
  493. );
  494. $tab_rs = $this->queryAsControlUser(
  495. $tab_query, false, DatabaseInterface::QUERY_STORE
  496. );
  497. if (! $tab_rs) {
  498. // query failed ... ?
  499. //$GLOBALS['cfg']['Server']['pmadb'] = false;
  500. return $cfgRelation;
  501. }
  502. while ($curr_table = @$GLOBALS['dbi']->fetchRow($tab_rs)) {
  503. if ($curr_table[0] == $GLOBALS['cfg']['Server']['bookmarktable']) {
  504. $cfgRelation['bookmark'] = $curr_table[0];
  505. } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['relation']) {
  506. $cfgRelation['relation'] = $curr_table[0];
  507. } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['table_info']) {
  508. $cfgRelation['table_info'] = $curr_table[0];
  509. } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['table_coords']) {
  510. $cfgRelation['table_coords'] = $curr_table[0];
  511. } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['column_info']) {
  512. $cfgRelation['column_info'] = $curr_table[0];
  513. } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['pdf_pages']) {
  514. $cfgRelation['pdf_pages'] = $curr_table[0];
  515. } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['history']) {
  516. $cfgRelation['history'] = $curr_table[0];
  517. } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['recent']) {
  518. $cfgRelation['recent'] = $curr_table[0];
  519. } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['favorite']) {
  520. $cfgRelation['favorite'] = $curr_table[0];
  521. } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['table_uiprefs']) {
  522. $cfgRelation['table_uiprefs'] = $curr_table[0];
  523. } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['tracking']) {
  524. $cfgRelation['tracking'] = $curr_table[0];
  525. } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['userconfig']) {
  526. $cfgRelation['userconfig'] = $curr_table[0];
  527. } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['users']) {
  528. $cfgRelation['users'] = $curr_table[0];
  529. } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['usergroups']) {
  530. $cfgRelation['usergroups'] = $curr_table[0];
  531. } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['navigationhiding']) {
  532. $cfgRelation['navigationhiding'] = $curr_table[0];
  533. } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['savedsearches']) {
  534. $cfgRelation['savedsearches'] = $curr_table[0];
  535. } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['central_columns']) {
  536. $cfgRelation['central_columns'] = $curr_table[0];
  537. } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['designer_settings']) {
  538. $cfgRelation['designer_settings'] = $curr_table[0];
  539. } elseif ($curr_table[0] == $GLOBALS['cfg']['Server']['export_templates']) {
  540. $cfgRelation['export_templates'] = $curr_table[0];
  541. }
  542. } // end while
  543. $GLOBALS['dbi']->freeResult($tab_rs);
  544. if (isset($cfgRelation['relation'])) {
  545. $cfgRelation['relwork'] = true;
  546. }
  547. if (isset($cfgRelation['relation']) && isset($cfgRelation['table_info'])) {
  548. $cfgRelation['displaywork'] = true;
  549. }
  550. if (isset($cfgRelation['table_coords']) && isset($cfgRelation['pdf_pages'])) {
  551. $cfgRelation['pdfwork'] = true;
  552. }
  553. if (isset($cfgRelation['column_info'])) {
  554. $cfgRelation['commwork'] = true;
  555. // phpMyAdmin 4.3+
  556. // Check for input transformations upgrade.
  557. $cfgRelation['mimework'] = $this->tryUpgradeTransformations();
  558. }
  559. if (isset($cfgRelation['history'])) {
  560. $cfgRelation['historywork'] = true;
  561. }
  562. if (isset($cfgRelation['recent'])) {
  563. $cfgRelation['recentwork'] = true;
  564. }
  565. if (isset($cfgRelation['favorite'])) {
  566. $cfgRelation['favoritework'] = true;
  567. }
  568. if (isset($cfgRelation['table_uiprefs'])) {
  569. $cfgRelation['uiprefswork'] = true;
  570. }
  571. if (isset($cfgRelation['tracking'])) {
  572. $cfgRelation['trackingwork'] = true;
  573. }
  574. if (isset($cfgRelation['userconfig'])) {
  575. $cfgRelation['userconfigwork'] = true;
  576. }
  577. if (isset($cfgRelation['bookmark'])) {
  578. $cfgRelation['bookmarkwork'] = true;
  579. }
  580. if (isset($cfgRelation['users']) && isset($cfgRelation['usergroups'])) {
  581. $cfgRelation['menuswork'] = true;
  582. }
  583. if (isset($cfgRelation['navigationhiding'])) {
  584. $cfgRelation['navwork'] = true;
  585. }
  586. if (isset($cfgRelation['savedsearches'])) {
  587. $cfgRelation['savedsearcheswork'] = true;
  588. }
  589. if (isset($cfgRelation['central_columns'])) {
  590. $cfgRelation['centralcolumnswork'] = true;
  591. }
  592. if (isset($cfgRelation['designer_settings'])) {
  593. $cfgRelation['designersettingswork'] = true;
  594. }
  595. if (isset($cfgRelation['export_templates'])) {
  596. $cfgRelation['exporttemplateswork'] = true;
  597. }
  598. $allWorks = true;
  599. foreach ($workToTable as $work => $table) {
  600. if (! $cfgRelation[$work]) {
  601. if (is_string($table)) {
  602. if (isset($GLOBALS['cfg']['Server'][$table])
  603. && $GLOBALS['cfg']['Server'][$table] !== false
  604. ) {
  605. $allWorks = false;
  606. break;
  607. }
  608. } elseif (is_array($table)) {
  609. $oneNull = false;
  610. foreach ($table as $t) {
  611. if (isset($GLOBALS['cfg']['Server'][$t])
  612. && $GLOBALS['cfg']['Server'][$t] === false
  613. ) {
  614. $oneNull = true;
  615. break;
  616. }
  617. }
  618. if (! $oneNull) {
  619. $allWorks = false;
  620. break;
  621. }
  622. }
  623. }
  624. }
  625. $cfgRelation['allworks'] = $allWorks;
  626. return $cfgRelation;
  627. }
  628. /**
  629. * Check whether column_info table input transformation
  630. * upgrade is required and try to upgrade silently
  631. *
  632. * @return bool false if upgrade failed
  633. *
  634. * @access public
  635. */
  636. public function tryUpgradeTransformations()
  637. {
  638. // From 4.3, new input oriented transformation feature was introduced.
  639. // Check whether column_info table has input transformation columns
  640. $new_cols = array(
  641. "input_transformation",
  642. "input_transformation_options"
  643. );
  644. $query = 'SHOW COLUMNS FROM '
  645. . Util::backquote($GLOBALS['cfg']['Server']['pmadb'])
  646. . '.' . Util::backquote(
  647. $GLOBALS['cfg']['Server']['column_info']
  648. )
  649. . ' WHERE Field IN (\'' . implode('\', \'', $new_cols) . '\')';
  650. $result = $this->queryAsControlUser(
  651. $query, false, DatabaseInterface::QUERY_STORE
  652. );
  653. if ($result) {
  654. $rows = $GLOBALS['dbi']->numRows($result);
  655. $GLOBALS['dbi']->freeResult($result);
  656. // input transformations are present
  657. // no need to upgrade
  658. if ($rows === 2) {
  659. return true;
  660. // try silent upgrade without disturbing the user
  661. }
  662. // read upgrade query file
  663. $query = @file_get_contents(SQL_DIR . 'upgrade_column_info_4_3_0+.sql');
  664. // replace database name from query to with set in config.inc.php
  665. $query = str_replace(
  666. '`phpmyadmin`',
  667. Util::backquote($GLOBALS['cfg']['Server']['pmadb']),
  668. $query
  669. );
  670. // replace pma__column_info table name from query
  671. // to with set in config.inc.php
  672. $query = str_replace(
  673. '`pma__column_info`',
  674. Util::backquote(
  675. $GLOBALS['cfg']['Server']['column_info']
  676. ),
  677. $query
  678. );
  679. $GLOBALS['dbi']->tryMultiQuery($query, DatabaseInterface::CONNECT_CONTROL);
  680. // skips result sets of query as we are not interested in it
  681. while ($GLOBALS['dbi']->moreResults(DatabaseInterface::CONNECT_CONTROL)
  682. && $GLOBALS['dbi']->nextResult(DatabaseInterface::CONNECT_CONTROL)
  683. ) {
  684. }
  685. $error = $GLOBALS['dbi']->getError(DatabaseInterface::CONNECT_CONTROL);
  686. // return true if no error exists otherwise false
  687. return empty($error);
  688. }
  689. // some failure, either in upgrading or something else
  690. // make some noise, time to wake up user.
  691. return false;
  692. }
  693. /**
  694. * Gets all Relations to foreign tables for a given table or
  695. * optionally a given column in a table
  696. *
  697. * @param string $db the name of the db to check for
  698. * @param string $table the name of the table to check for
  699. * @param string $column the name of the column to check for
  700. * @param string $source the source for foreign key information
  701. *
  702. * @return array db,table,column
  703. *
  704. * @access public
  705. */
  706. public function getForeigners($db, $table, $column = '', $source = 'both')
  707. {
  708. $cfgRelation = $this->getRelationsParam();
  709. $foreign = array();
  710. if ($cfgRelation['relwork'] && ($source == 'both' || $source == 'internal')) {
  711. $rel_query = '
  712. SELECT `master_field`,
  713. `foreign_db`,
  714. `foreign_table`,
  715. `foreign_field`
  716. FROM ' . Util::backquote($cfgRelation['db'])
  717. . '.' . Util::backquote($cfgRelation['relation']) . '
  718. WHERE `master_db` = \'' . $GLOBALS['dbi']->escapeString($db) . '\'
  719. AND `master_table` = \'' . $GLOBALS['dbi']->escapeString($table)
  720. . '\' ';
  721. if (strlen($column) > 0) {
  722. $rel_query .= ' AND `master_field` = '
  723. . '\'' . $GLOBALS['dbi']->escapeString($column) . '\'';
  724. }
  725. $foreign = $GLOBALS['dbi']->fetchResult(
  726. $rel_query, 'master_field', null, DatabaseInterface::CONNECT_CONTROL
  727. );
  728. }
  729. if (($source == 'both' || $source == 'foreign') && strlen($table) > 0) {
  730. $tableObj = new Table($table, $db);
  731. $show_create_table = $tableObj->showCreate();
  732. if ($show_create_table) {
  733. $parser = new Parser($show_create_table);
  734. /**
  735. * @var \PhpMyAdmin\SqlParser\Statements\CreateStatement $stmt
  736. */
  737. $stmt = $parser->statements[0];
  738. $foreign['foreign_keys_data'] = TableUtils::getForeignKeys(
  739. $stmt
  740. );
  741. }
  742. }
  743. /**
  744. * Emulating relations for some information_schema tables
  745. */
  746. $isInformationSchema = mb_strtolower($db) == 'information_schema';
  747. $isMysql = mb_strtolower($db) == 'mysql';
  748. if (($isInformationSchema || $isMysql)
  749. && ($source == 'internal' || $source == 'both')
  750. ) {
  751. if ($isInformationSchema) {
  752. $relations_key = 'information_schema_relations';
  753. include_once './libraries/information_schema_relations.inc.php';
  754. } else {
  755. $relations_key = 'mysql_relations';
  756. include_once './libraries/mysql_relations.inc.php';
  757. }
  758. if (isset($GLOBALS[$relations_key][$table])) {
  759. foreach ($GLOBALS[$relations_key][$table] as $field => $relations) {
  760. if ((strlen($column) === 0 || $column == $field)
  761. && (! isset($foreign[$field])
  762. || strlen($foreign[$field]) === 0)
  763. ) {
  764. $foreign[$field] = $relations;
  765. }
  766. }
  767. }
  768. }
  769. return $foreign;
  770. }
  771. /**
  772. * Gets the display field of a table
  773. *
  774. * @param string $db the name of the db to check for
  775. * @param string $table the name of the table to check for
  776. *
  777. * @return string field name
  778. *
  779. * @access public
  780. */
  781. public function getDisplayField($db, $table)
  782. {
  783. $cfgRelation = $this->getRelationsParam();
  784. /**
  785. * Try to fetch the display field from DB.
  786. */
  787. if ($cfgRelation['displaywork']) {
  788. $disp_query = '
  789. SELECT `display_field`
  790. FROM ' . Util::backquote($cfgRelation['db'])
  791. . '.' . Util::backquote($cfgRelation['table_info']) . '
  792. WHERE `db_name` = \'' . $GLOBALS['dbi']->escapeString($db) . '\'
  793. AND `table_name` = \'' . $GLOBALS['dbi']->escapeString($table)
  794. . '\'';
  795. $row = $GLOBALS['dbi']->fetchSingleRow(
  796. $disp_query, 'ASSOC', DatabaseInterface::CONNECT_CONTROL
  797. );
  798. if (isset($row['display_field'])) {
  799. return $row['display_field'];
  800. }
  801. }
  802. /**
  803. * Emulating the display field for some information_schema tables.
  804. */
  805. if ($db == 'information_schema') {
  806. switch ($table) {
  807. case 'CHARACTER_SETS':
  808. return 'DESCRIPTION';
  809. case 'TABLES':
  810. return 'TABLE_COMMENT';
  811. }
  812. }
  813. /**
  814. * Pick first char field
  815. */
  816. $columns = $GLOBALS['dbi']->getColumnsFull($db, $table);
  817. if ($columns) {
  818. foreach ($columns as $column) {
  819. if ($GLOBALS['dbi']->types->getTypeClass($column['DATA_TYPE']) == 'CHAR') {
  820. return $column['COLUMN_NAME'];
  821. }
  822. }
  823. }
  824. return false;
  825. }
  826. /**
  827. * Gets the comments for all columns of a table or the db itself
  828. *
  829. * @param string $db the name of the db to check for
  830. * @param string $table the name of the table to check for
  831. *
  832. * @return array [column_name] = comment
  833. *
  834. * @access public
  835. */
  836. public function getComments($db, $table = '')
  837. {
  838. $comments = array();
  839. if ($table != '') {
  840. // MySQL native column comments
  841. $columns = $GLOBALS['dbi']->getColumns($db, $table, null, true);
  842. if ($columns) {
  843. foreach ($columns as $column) {
  844. if (! empty($column['Comment'])) {
  845. $comments[$column['Field']] = $column['Comment'];
  846. }
  847. }
  848. }
  849. } else {
  850. $comments[] = $this->getDbComment($db);
  851. }
  852. return $comments;
  853. }
  854. /**
  855. * Gets the comment for a db
  856. *
  857. * @param string $db the name of the db to check for
  858. *
  859. * @return string comment
  860. *
  861. * @access public
  862. */
  863. public function getDbComment($db)
  864. {
  865. $cfgRelation = $this->getRelationsParam();
  866. $comment = '';
  867. if ($cfgRelation['commwork']) {
  868. // pmadb internal db comment
  869. $com_qry = "
  870. SELECT `comment`
  871. FROM " . Util::backquote($cfgRelation['db'])
  872. . "." . Util::backquote($cfgRelation['column_info'])
  873. . "
  874. WHERE db_name = '" . $GLOBALS['dbi']->escapeString($db) . "'
  875. AND table_name = ''
  876. AND column_name = '(db_comment)'";
  877. $com_rs = $this->queryAsControlUser(
  878. $com_qry, true, DatabaseInterface::QUERY_STORE
  879. );
  880. if ($com_rs && $GLOBALS['dbi']->numRows($com_rs) > 0) {
  881. $row = $GLOBALS['dbi']->fetchAssoc($com_rs);
  882. $comment = $row['comment'];
  883. }
  884. $GLOBALS['dbi']->freeResult($com_rs);
  885. }
  886. return $comment;
  887. }
  888. /**
  889. * Gets the comment for a db
  890. *
  891. * @access public
  892. *
  893. * @return string comment
  894. */
  895. public function getDbComments()
  896. {
  897. $cfgRelation = $this->getRelationsParam();
  898. $comments = array();
  899. if ($cfgRelation['commwork']) {
  900. // pmadb internal db comment
  901. $com_qry = "
  902. SELECT `db_name`, `comment`
  903. FROM " . Util::backquote($cfgRelation['db'])
  904. . "." . Util::backquote($cfgRelation['column_info'])
  905. . "
  906. WHERE `column_name` = '(db_comment)'";
  907. $com_rs = $this->queryAsControlUser(
  908. $com_qry, true, DatabaseInterface::QUERY_STORE
  909. );
  910. if ($com_rs && $GLOBALS['dbi']->numRows($com_rs) > 0) {
  911. while ($row = $GLOBALS['dbi']->fetchAssoc($com_rs)) {
  912. $comments[$row['db_name']] = $row['comment'];
  913. }
  914. }
  915. $GLOBALS['dbi']->freeResult($com_rs);
  916. }
  917. return $comments;
  918. }
  919. /**
  920. * Set a database comment to a certain value.
  921. *
  922. * @param string $db the name of the db
  923. * @param string $comment the value of the column
  924. *
  925. * @return boolean true, if comment-query was made.
  926. *
  927. * @access public
  928. */
  929. public function setDbComment($db, $comment = '')
  930. {
  931. $cfgRelation = $this->getRelationsParam();
  932. if (! $cfgRelation['commwork']) {
  933. return false;
  934. }
  935. if (strlen($comment) > 0) {
  936. $upd_query = 'INSERT INTO '
  937. . Util::backquote($cfgRelation['db']) . '.'
  938. . Util::backquote($cfgRelation['column_info'])
  939. . ' (`db_name`, `table_name`, `column_name`, `comment`)'
  940. . ' VALUES (\''
  941. . $GLOBALS['dbi']->escapeString($db)
  942. . "', '', '(db_comment)', '"
  943. . $GLOBALS['dbi']->escapeString($comment)
  944. . "') "
  945. . ' ON DUPLICATE KEY UPDATE '
  946. . "`comment` = '" . $GLOBALS['dbi']->escapeString($comment) . "'";
  947. } else {
  948. $upd_query = 'DELETE FROM '
  949. . Util::backquote($cfgRelation['db']) . '.'
  950. . Util::backquote($cfgRelation['column_info'])
  951. . ' WHERE `db_name` = \'' . $GLOBALS['dbi']->escapeString($db)
  952. . '\'
  953. AND `table_name` = \'\'
  954. AND `column_name` = \'(db_comment)\'';
  955. }
  956. if (isset($upd_query)) {
  957. return $this->queryAsControlUser($upd_query);
  958. }
  959. return false;
  960. }
  961. /**
  962. * Set a SQL history entry
  963. *
  964. * @param string $db the name of the db
  965. * @param string $table the name of the table
  966. * @param string $username the username
  967. * @param string $sqlquery the sql query
  968. *
  969. * @return void
  970. *
  971. * @access public
  972. */
  973. public function setHistory($db, $table, $username, $sqlquery)
  974. {
  975. $maxCharactersInDisplayedSQL = $GLOBALS['cfg']['MaxCharactersInDisplayedSQL'];
  976. // Prevent to run this automatically on Footer class destroying in testsuite
  977. if (defined('TESTSUITE')
  978. || mb_strlen($sqlquery) > $maxCharactersInDisplayedSQL
  979. ) {
  980. return;
  981. }
  982. $cfgRelation = $this->getRelationsParam();
  983. if (! isset($_SESSION['sql_history'])) {
  984. $_SESSION['sql_history'] = array();
  985. }
  986. $_SESSION['sql_history'][] = array(
  987. 'db' => $db,
  988. 'table' => $table,
  989. 'sqlquery' => $sqlquery,
  990. );
  991. if (count($_SESSION['sql_history']) > $GLOBALS['cfg']['QueryHistoryMax']) {
  992. // history should not exceed a maximum count
  993. array_shift($_SESSION['sql_history']);
  994. }
  995. if (! $cfgRelation['historywork'] || ! $GLOBALS['cfg']['QueryHistoryDB']) {
  996. return;
  997. }
  998. $this->queryAsControlUser(
  999. 'INSERT INTO '
  1000. . Util::backquote($cfgRelation['db']) . '.'
  1001. . Util::backquote($cfgRelation['history']) . '
  1002. (`username`,
  1003. `db`,
  1004. `table`,
  1005. `timevalue`,
  1006. `sqlquery`)
  1007. VALUES
  1008. (\'' . $GLOBALS['dbi']->escapeString($username) . '\',
  1009. \'' . $GLOBALS['dbi']->escapeString($db) . '\',
  1010. \'' . $GLOBALS['dbi']->escapeString($table) . '\',
  1011. NOW(),
  1012. \'' . $GLOBALS['dbi']->escapeString($sqlquery) . '\')'
  1013. );
  1014. $this->purgeHistory($username);
  1015. }
  1016. /**
  1017. * Gets a SQL history entry
  1018. *
  1019. * @param string $username the username
  1020. *
  1021. * @return array list of history items
  1022. *
  1023. * @access public
  1024. */
  1025. public function getHistory($username)
  1026. {
  1027. $cfgRelation = $this->getRelationsParam();
  1028. if (! $cfgRelation['historywork']) {
  1029. return false;
  1030. }
  1031. /**
  1032. * if db-based history is disabled but there exists a session-based
  1033. * history, use it
  1034. */
  1035. if (! $GLOBALS['cfg']['QueryHistoryDB']) {
  1036. if (isset($_SESSION['sql_history'])) {
  1037. return array_reverse($_SESSION['sql_history']);
  1038. }
  1039. return false;
  1040. }
  1041. $hist_query = '
  1042. SELECT `db`,
  1043. `table`,
  1044. `sqlquery`,
  1045. `timevalue`
  1046. FROM ' . Util::backquote($cfgRelation['db'])
  1047. . '.' . Util::backquote($cfgRelation['history']) . '
  1048. WHERE `username` = \'' . $GLOBALS['dbi']->escapeString($username) . '\'
  1049. ORDER BY `id` DESC';
  1050. return $GLOBALS['dbi']->fetchResult(
  1051. $hist_query, null, null, DatabaseInterface::CONNECT_CONTROL
  1052. );
  1053. }
  1054. /**
  1055. * purges SQL history
  1056. *
  1057. * deletes entries that exceeds $cfg['QueryHistoryMax'], oldest first, for the
  1058. * given user
  1059. *
  1060. * @param string $username the username
  1061. *
  1062. * @return void
  1063. *
  1064. * @access public
  1065. */
  1066. public function purgeHistory($username)
  1067. {
  1068. $cfgRelation = $this->getRelationsParam();
  1069. if (! $GLOBALS['cfg']['QueryHistoryDB'] || ! $cfgRelation['historywork']) {
  1070. return;
  1071. }
  1072. if (! $cfgRelation['historywork']) {
  1073. return;
  1074. }
  1075. $search_query = '
  1076. SELECT `timevalue`
  1077. FROM ' . Util::backquote($cfgRelation['db'])
  1078. . '.' . Util::backquote($cfgRelation['history']) . '
  1079. WHERE `username` = \'' . $GLOBALS['dbi']->escapeString($username) . '\'
  1080. ORDER BY `timevalue` DESC
  1081. LIMIT ' . $GLOBALS['cfg']['QueryHistoryMax'] . ', 1';
  1082. if ($max_time = $GLOBALS['dbi']->fetchValue(
  1083. $search_query, 0, 0, DatabaseInterface::CONNECT_CONTROL
  1084. )) {
  1085. $this->queryAsControlUser(
  1086. 'DELETE FROM '
  1087. . Util::backquote($cfgRelation['db']) . '.'
  1088. . Util::backquote($cfgRelation['history']) . '
  1089. WHERE `username` = \'' . $GLOBALS['dbi']->escapeString($username)
  1090. . '\'
  1091. AND `timevalue` <= \'' . $max_time . '\''
  1092. );
  1093. }
  1094. }
  1095. /**
  1096. * Prepares the dropdown for one mode
  1097. *
  1098. * @param array $foreign the keys and values for foreigns
  1099. * @param string $data the current data of the dropdown
  1100. * @param string $mode the needed mode
  1101. *
  1102. * @return array the <option value=""><option>s
  1103. *
  1104. * @access protected
  1105. */
  1106. public function buildForeignDropdown(array $foreign, $data, $mode)
  1107. {
  1108. $reloptions = array();
  1109. // id-only is a special mode used when no foreign display column
  1110. // is available
  1111. if ($mode == 'id-content' || $mode == 'id-only') {
  1112. // sort for id-content
  1113. if ($GLOBALS['cfg']['NaturalOrder']) {
  1114. uksort($foreign, 'strnatcasecmp');
  1115. } else {
  1116. ksort($foreign);
  1117. }
  1118. } elseif ($mode == 'content-id') {
  1119. // sort for content-id
  1120. if ($GLOBALS['cfg']['NaturalOrder']) {
  1121. natcasesort($foreign);
  1122. } else {
  1123. asort($foreign);
  1124. }
  1125. }
  1126. foreach ($foreign as $key => $value) {
  1127. if (mb_strlen($value) <= $GLOBALS['cfg']['LimitChars']
  1128. ) {
  1129. $vtitle = '';
  1130. $value = htmlspecialchars($value);
  1131. } else {
  1132. $vtitle = htmlspecialchars($value);
  1133. $value = htmlspecialchars(
  1134. mb_substr(
  1135. $value, 0, $GLOBALS['cfg']['LimitChars']
  1136. ) . '...'
  1137. );
  1138. }
  1139. $reloption = '<option value="' . htmlspecialchars($key) . '"';
  1140. if ($vtitle != '') {
  1141. $reloption .= ' title="' . $vtitle . '"';
  1142. }
  1143. if ((string) $key == (string) $data) {
  1144. $reloption .= ' selected="selected"';
  1145. }
  1146. if ($mode == 'content-id') {
  1147. $reloptions[] = $reloption . '>'
  1148. . $value . '&nbsp;-&nbsp;' . htmlspecialchars($key) . '</option>';
  1149. } elseif ($mode == 'id-content') {
  1150. $reloptions[] = $reloption . '>'
  1151. . htmlspecialchars($key) . '&nbsp;-&nbsp;' . $value . '</option>';
  1152. } elseif ($mode == 'id-only') {
  1153. $reloptions[] = $reloption . '>'
  1154. . htmlspecialchars($key) . '</option>';
  1155. }
  1156. } // end foreach
  1157. return $reloptions;
  1158. }
  1159. /**
  1160. * Outputs dropdown with values of foreign fields
  1161. *
  1162. * @param array $disp_row array of the displayed row
  1163. * @param string $foreign_field the foreign field
  1164. * @param string $foreign_display the foreign field to display
  1165. * @param string $data the current data of the dropdown (field in row)
  1166. * @param int $max maximum number of items in the dropdown
  1167. *
  1168. * @return string the <option value=""><option>s
  1169. *
  1170. * @access public
  1171. */
  1172. public function foreignDropdown(array $disp_row, $foreign_field, $foreign_display, $data,
  1173. $max = null
  1174. ) {
  1175. if (null === $max) {
  1176. $max = $GLOBALS['cfg']['ForeignKeyMaxLimit'];
  1177. }
  1178. $foreign = array();
  1179. // collect the data
  1180. foreach ($disp_row as $relrow) {
  1181. $key = $relrow[$foreign_field];
  1182. // if the display field has been defined for this foreign table
  1183. if ($foreign_display) {
  1184. $value = $relrow[$foreign_display];
  1185. } else {
  1186. $value = '';
  1187. } // end if ($foreign_display)
  1188. $foreign[$key] = $value;
  1189. } // end foreach
  1190. // put the dropdown sections in correct order
  1191. $top = array();
  1192. $bottom = array();
  1193. if ($foreign_display) {
  1194. if (Core::isValid($GLOBALS['cfg']['ForeignKeyDropdownOrder'], 'array')) {
  1195. if (Core::isValid($GLOBALS['cfg']['ForeignKeyDropdownOrder'][0])) {
  1196. $top = $this->buildForeignDropdown(
  1197. $foreign,
  1198. $data,
  1199. $GLOBALS['cfg']['ForeignKeyDropdownOrder'][0]
  1200. );
  1201. }
  1202. if (Core::isValid($GLOBALS['cfg']['ForeignKeyDropdownOrder'][1])) {
  1203. $bottom = $this->buildForeignDropdown(
  1204. $foreign,
  1205. $data,
  1206. $GLOBALS['cfg']['ForeignKeyDropdownOrder'][1]
  1207. );
  1208. }
  1209. } else {
  1210. $top = $this->buildForeignDropdown($foreign, $data, 'id-content');
  1211. $bottom = $this->buildForeignDropdown($foreign, $data, 'content-id');
  1212. }
  1213. } else {
  1214. $top = $this->buildForeignDropdown($foreign, $data, 'id-only');
  1215. }
  1216. // beginning of dropdown
  1217. $ret = '<option value="">&nbsp;</option>';
  1218. $top_count = count($top);
  1219. if ($max == -1 || $top_count < $max) {
  1220. $ret .= implode('', $top);
  1221. if ($foreign_display && $top_count > 0) {
  1222. // this empty option is to visually mark the beginning of the
  1223. // second series of values (bottom)
  1224. $ret .= '<option value="">&nbsp;</option>';
  1225. }
  1226. }
  1227. if ($foreign_display) {
  1228. $ret .= implode('', $bottom);
  1229. }
  1230. return $ret;
  1231. }
  1232. /**
  1233. * Gets foreign keys in preparation for a drop-down selector
  1234. *
  1235. * @param array|boolean $foreigners array of the foreign keys
  1236. * @param string $field the foreign field name
  1237. * @param bool $override_total whether to override the total
  1238. * @param string $foreign_filter a possible filter
  1239. * @param string $foreign_limit a possible LIMIT clause
  1240. * @param bool $get_total optional, whether to get total num of rows
  1241. * in $foreignData['the_total;]
  1242. * (has an effect of performance)
  1243. *
  1244. * @return array data about the foreign keys
  1245. *
  1246. * @access public
  1247. */
  1248. public function getForeignData(
  1249. $foreigners, $field, $override_total,
  1250. $foreign_filter, $foreign_limit, $get_total=false
  1251. ) {
  1252. // we always show the foreign field in the drop-down; if a display
  1253. // field is defined, we show it besides the foreign field
  1254. $foreign_link = false;
  1255. do {
  1256. if (! $foreigners) {
  1257. break;
  1258. }
  1259. $foreigner = $this->searchColumnInForeigners($foreigners, $field);
  1260. if ($foreigner != false) {
  1261. $foreign_db = $foreigner['foreign_db'];
  1262. $foreign_table = $foreigner['foreign_table'];
  1263. $foreign_field = $foreigner['foreign_field'];
  1264. } else {
  1265. break;
  1266. }
  1267. // Count number of rows in the foreign table. Currently we do
  1268. // not use a drop-down if more than ForeignKeyMaxLimit rows in the
  1269. // foreign table,
  1270. // for speed reasons and because we need a better interface for this.
  1271. //
  1272. // We could also do the SELECT anyway, with a LIMIT, and ensure that
  1273. // the current value of the field is one of the choices.
  1274. // Check if table has more rows than specified by
  1275. // $GLOBALS['cfg']['ForeignKeyMaxLimit']
  1276. $moreThanLimit = $GLOBALS['dbi']->getTable($foreign_db, $foreign_table)
  1277. ->checkIfMinRecordsExist($GLOBALS['cfg']['ForeignKeyMaxLimit']);
  1278. if ($override_total == true
  1279. || !$moreThanLimit
  1280. ) {
  1281. // foreign_display can be false if no display field defined:
  1282. $foreign_display = $this->getDisplayField($foreign_db, $foreign_table);
  1283. $f_query_main = 'SELECT ' . Util::backquote($foreign_field)
  1284. . (
  1285. ($foreign_display == false)
  1286. ? ''
  1287. : ', ' . Util::backquote($foreign_display)
  1288. );
  1289. $f_query_from = ' FROM ' . Util::backquote($foreign_db)
  1290. . '.' . Util::backquote($foreign_table);
  1291. $f_query_filter = empty($foreign_filter) ? '' : ' WHERE '
  1292. . Util::backquote($foreign_field)
  1293. . ' LIKE "%' . $GLOBALS['dbi']->escapeString($foreign_filter) . '%"'
  1294. . (
  1295. ($foreign_display == false)
  1296. ? ''
  1297. : ' OR ' . Util::backquote($foreign_display)
  1298. . ' LIKE "%' . $GLOBALS['dbi']->escapeString($foreign_filter)
  1299. . '%"'
  1300. );
  1301. $f_query_order = ($foreign_display == false) ? '' :' ORDER BY '
  1302. . Util::backquote($foreign_table) . '.'
  1303. . Util::backquote($foreign_display);
  1304. $f_query_limit = ! empty($foreign_limit) ? ($foreign_limit) : '';
  1305. if (!empty($foreign_filter)) {
  1306. $the_total = $GLOBALS['dbi']->fetchValue(
  1307. 'SELECT COUNT(*)' . $f_query_from . $f_query_filter
  1308. );
  1309. if ($the_total === false) {
  1310. $the_total = 0;
  1311. }
  1312. }
  1313. $disp = $GLOBALS['dbi']->tryQuery(
  1314. $f_query_main . $f_query_from . $f_query_filter
  1315. . $f_query_order . $f_query_limit
  1316. );
  1317. if ($disp && $GLOBALS['dbi']->numRows($disp) > 0) {
  1318. // If a resultset has been created, pre-cache it in the $disp_row
  1319. // array. This helps us from not needing to use mysql_data_seek by
  1320. // accessing a pre-cached PHP array. Usually those resultsets are
  1321. // not that big, so a performance hit should not be expected.
  1322. $disp_row = array();
  1323. while ($single_disp_row = @$GLOBALS['dbi']->fetchAssoc($disp)) {
  1324. $disp_row[] = $single_disp_row;
  1325. }
  1326. @$GLOBALS['dbi']->freeResult($disp);
  1327. } else {
  1328. // Either no data in the foreign table or
  1329. // user does not have select permission to foreign table/field
  1330. // Show an input field with a 'Browse foreign values' link
  1331. $disp_row = null;
  1332. $foreign_link = true;
  1333. }
  1334. } else {
  1335. $disp_row = null;
  1336. $foreign_link = true;
  1337. }
  1338. } while (false);
  1339. if ($get_total) {
  1340. $the_total = $GLOBALS['dbi']->getTable($foreign_db, $foreign_table)
  1341. ->countRecords(true);
  1342. }
  1343. $foreignData = array();
  1344. $foreignData['foreign_link'] = $foreign_link;
  1345. $foreignData['the_total'] = isset($the_total) ? $the_total : null;
  1346. $foreignData['foreign_display'] = (
  1347. isset($foreign_display) ? $foreign_display : null
  1348. );
  1349. $foreignData['disp_row'] = isset($disp_row) ? $disp_row : null;
  1350. $foreignData['foreign_field'] = isset($foreign_field) ? $foreign_field : null;
  1351. return $foreignData;
  1352. }
  1353. /**
  1354. * Rename a field in relation tables
  1355. *
  1356. * usually called after a column in a table was renamed
  1357. *
  1358. * @param string $db database name
  1359. * @param string $table table name
  1360. * @param string $field old field name
  1361. * @param string $new_name new field name
  1362. *
  1363. * @return void
  1364. */
  1365. public function renameField($db, $table, $field, $new_name)
  1366. {
  1367. $cfgRelation = $this->getRelationsParam();
  1368. if ($cfgRelation['displaywork']) {
  1369. $table_query = 'UPDATE '
  1370. . Util::backquote($cfgRelation['db']) . '.'
  1371. . Util::backquote($cfgRelation['table_info'])
  1372. . ' SET display_field = \'' . $GLOBALS['dbi']->escapeString(
  1373. $new_name
  1374. ) . '\''
  1375. . ' WHERE db_name = \'' . $GLOBALS['dbi']->escapeString($db)
  1376. . '\''
  1377. . ' AND table_name = \'' . $GLOBALS['dbi']->escapeString($table)
  1378. . '\''
  1379. . ' AND display_field = \'' . $GLOBALS['dbi']->escapeString($field)
  1380. . '\'';
  1381. $this->queryAsControlUser($table_query);
  1382. }
  1383. if ($cfgRelation['relwork']) {
  1384. $table_query = 'UPDATE '
  1385. . Util::backquote($cfgRelation['db']) . '.'
  1386. . Util::backquote($cfgRelation['relation'])
  1387. . ' SET master_field = \'' . $GLOBALS['dbi']->escapeString(
  1388. $new_name
  1389. ) . '\''
  1390. . ' WHERE master_db = \'' . $GLOBALS['dbi']->escapeString($db)
  1391. . '\''
  1392. . ' AND master_table = \'' . $GLOBALS['dbi']->escapeString($table)
  1393. . '\''
  1394. . ' AND master_field = \'' . $GLOBALS['dbi']->escapeString($field)
  1395. . '\'';
  1396. $this->queryAsControlUser($table_query);
  1397. $table_query = 'UPDATE '
  1398. . Util::backquote($cfgRelation['db']) . '.'
  1399. . Util::backquote($cfgRelation['relation'])
  1400. . ' SET foreign_field = \'' . $GLOBALS['dbi']->escapeString(
  1401. $new_name
  1402. ) . '\''
  1403. . ' WHERE foreign_db = \'' . $GLOBALS['dbi']->escapeString($db)
  1404. . '\''
  1405. . ' AND foreign_table = \'' . $GLOBALS['dbi']->escapeString($table)
  1406. . '\''
  1407. . ' AND foreign_field = \'' . $GLOBALS['dbi']->escapeString($field)
  1408. . '\'';
  1409. $this->queryAsControlUser($table_query);
  1410. }
  1411. }
  1412. /**
  1413. * Performs SQL query used for renaming table.
  1414. *
  1415. * @param string $table Relation table to use
  1416. * @param string $source_db Source database name
  1417. * @param string $target_db Target database name
  1418. * @param string $source_table Source table name
  1419. * @param string $target_table Target table name
  1420. * @param string $db_field Name of database field
  1421. * @param string $table_field Name of table field
  1422. *
  1423. * @return void
  1424. */
  1425. public function renameSingleTable($table,
  1426. $source_db, $target_db,
  1427. $source_table, $target_table,
  1428. $db_field, $table_field
  1429. ) {
  1430. $query = 'UPDATE '
  1431. . Util::backquote($GLOBALS['cfgRelation']['db']) . '.'
  1432. . Util::backquote($GLOBALS['cfgRelation'][$table])
  1433. . ' SET '
  1434. . $db_field . ' = \'' . $GLOBALS['dbi']->escapeString($target_db)
  1435. . '\', '
  1436. . $table_field . ' = \'' . $GLOBALS['dbi']->escapeString($target_table)
  1437. . '\''
  1438. . ' WHERE '
  1439. . $db_field . ' = \'' . $GLOBALS['dbi']->escapeString($source_db) . '\''
  1440. . ' AND '
  1441. . $table_field . ' = \'' . $GLOBALS['dbi']->escapeString($source_table)
  1442. . '\'';
  1443. $this->queryAsControlUser($query);
  1444. }
  1445. /**
  1446. * Rename a table in relation tables
  1447. *
  1448. * usually called after table has been moved
  1449. *
  1450. * @param string $source_db Source database name
  1451. * @param string $target_db Target database name
  1452. * @param string $source_table Source table name
  1453. * @param string $target_table Target table name
  1454. *
  1455. * @return void
  1456. */
  1457. public function renameTable($source_db, $target_db, $source_table, $target_table)
  1458. {
  1459. // Move old entries from PMA-DBs to new table
  1460. if ($GLOBALS['cfgRelation']['commwork']) {
  1461. $this->renameSingleTable(
  1462. 'column_info',
  1463. $source_db, $target_db,
  1464. $source_table, $target_table,
  1465. 'db_name', 'table_name'
  1466. );
  1467. }
  1468. // updating bookmarks is not possible since only a single table is
  1469. // moved, and not the whole DB.
  1470. if ($GLOBALS['cfgRelation']['displaywork']) {
  1471. $this->renameSingleTable(
  1472. 'table_info',
  1473. $source_db, $target_db,
  1474. $source_table, $target_table,
  1475. 'db_name', 'table_name'
  1476. );
  1477. }
  1478. if ($GLOBALS['cfgRelation']['relwork']) {
  1479. $this->renameSingleTable(
  1480. 'relation',
  1481. $source_db, $target_db,
  1482. $source_table, $target_table,
  1483. 'foreign_db', 'foreign_table'
  1484. );
  1485. $this->renameSingleTable(
  1486. 'relation',
  1487. $source_db, $target_db,
  1488. $source_table, $target_table,
  1489. 'master_db', 'master_table'
  1490. );
  1491. }
  1492. if ($GLOBALS['cfgRelation']['pdfwork']) {
  1493. if ($source_db == $target_db) {
  1494. // rename within the database can be handled
  1495. $this->renameSingleTable(
  1496. 'table_coords',
  1497. $source_db, $target_db,
  1498. $source_table, $target_table,
  1499. 'db_name', 'table_name'
  1500. );
  1501. } else {
  1502. // if the table is moved out of the database we can no loger keep the
  1503. // record for table coordinate
  1504. $remove_query = "DELETE FROM "
  1505. . Util::backquote($GLOBALS['cfgRelation']['db']) . "."
  1506. . Util::backquote($GLOBALS['cfgRelation']['table_coords'])
  1507. . " WHERE db_name = '" . $GLOBALS['dbi']->escapeString($source_db) . "'"
  1508. . " AND table_name = '" . $GLOBALS['dbi']->escapeString($source_table)
  1509. . "'";
  1510. $this->queryAsControlUser($remove_query);
  1511. }
  1512. }
  1513. if ($GLOBALS['cfgRelation']['uiprefswork']) {
  1514. $this->renameSingleTable(
  1515. 'table_uiprefs',
  1516. $source_db, $target_db,
  1517. $source_table, $target_table,
  1518. 'db_name', 'table_name'
  1519. );
  1520. }
  1521. if ($GLOBALS['cfgRelation']['navwork']) {
  1522. // update hidden items inside table
  1523. $this->renameSingleTable(
  1524. 'navigationhiding',
  1525. $source_db, $target_db,
  1526. $source_table, $target_table,
  1527. 'db_name', 'table_name'
  1528. );
  1529. // update data for hidden table
  1530. $query = "UPDATE "
  1531. . Util::backquote($GLOBALS['cfgRelation']['db']) . "."
  1532. . Util::backquote(
  1533. $GLOBALS['cfgRelation']['navigationhiding']
  1534. )
  1535. . " SET db_name = '" . $GLOBALS['dbi']->escapeString($target_db)
  1536. . "',"
  1537. . " item_name = '" . $GLOBALS['dbi']->escapeString($target_table)
  1538. . "'"
  1539. . " WHERE db_name = '" . $GLOBALS['dbi']->escapeString($source_db)
  1540. . "'"
  1541. . " AND item_name = '" . $GLOBALS['dbi']->escapeString($source_table)
  1542. . "'"
  1543. . " AND item_type = 'table'";
  1544. $this->queryAsControlUser($query);
  1545. }
  1546. }
  1547. /**
  1548. * Create a PDF page
  1549. *
  1550. * @param string $newpage name of the new PDF page
  1551. * @param array $cfgRelation Relation configuration
  1552. * @param string $db database name
  1553. *
  1554. * @return int $pdf_page_number
  1555. */
  1556. public function createPage($newpage, array $cfgRelation, $db)
  1557. {
  1558. if (! isset($newpage) || $newpage == '') {
  1559. $newpage = __('no description');
  1560. }
  1561. $ins_query = 'INSERT INTO '
  1562. . Util::backquote($GLOBALS['cfgRelation']['db']) . '.'
  1563. . Util::backquote($cfgRelation['pdf_pages'])
  1564. . ' (db_name, page_descr)'
  1565. . ' VALUES (\''
  1566. . $GLOBALS['dbi']->escapeString($db) . '\', \''
  1567. . $GLOBALS['dbi']->escapeString($newpage) . '\')';
  1568. $this->queryAsControlUser($ins_query, false);
  1569. return $GLOBALS['dbi']->insertId(DatabaseInterface::CONNECT_CONTROL);
  1570. }
  1571. /**
  1572. * Get child table references for a table column.
  1573. * This works only if 'DisableIS' is false. An empty array is returned otherwise.
  1574. *
  1575. * @param string $db name of master table db.
  1576. * @param string $table name of master table.
  1577. * @param string $column name of master table column.
  1578. *
  1579. * @return array $child_references
  1580. */
  1581. public function getChildReferences($db, $table, $column = '')
  1582. {
  1583. $child_references = array();
  1584. if (! $GLOBALS['cfg']['Server']['DisableIS']) {
  1585. $rel_query = "SELECT `column_name`, `table_name`,"
  1586. . " `table_schema`, `referenced_column_name`"
  1587. . " FROM `information_schema`.`key_column_usage`"
  1588. . " WHERE `referenced_table_name` = '"
  1589. . $GLOBALS['dbi']->escapeString($table) . "'"
  1590. . " AND `referenced_table_schema` = '"
  1591. . $GLOBALS['dbi']->escapeString($db) . "'";
  1592. if ($column) {
  1593. $rel_query .= " AND `referenced_column_name` = '"
  1594. . $GLOBALS['dbi']->escapeString($column) . "'";
  1595. }
  1596. $child_references = $GLOBALS['dbi']->fetchResult(
  1597. $rel_query, array('referenced_column_name', null)
  1598. );
  1599. }
  1600. return $child_references;
  1601. }
  1602. /**
  1603. * Check child table references and foreign key for a table column.
  1604. *
  1605. * @param string $db name of master table db.
  1606. * @param string $table name of master table.
  1607. * @param string $column name of master table column.
  1608. * @param array|null $foreigners_full foreiners array for the whole table.
  1609. * @param array|null $child_references_full child references for the whole table.
  1610. *
  1611. * @return array $column_status telling about references if foreign key.
  1612. */
  1613. public function checkChildForeignReferences(
  1614. $db, $table, $column, $foreigners_full = null, $child_references_full = null
  1615. ) {
  1616. $column_status = array();
  1617. $column_status['isEditable'] = false;
  1618. $column_status['isReferenced'] = false;
  1619. $column_status['isForeignKey'] = false;
  1620. $column_status['references'] = array();
  1621. $foreigners = array();
  1622. if ($foreigners_full !== null) {
  1623. if (isset($foreigners_full[$column])) {
  1624. $foreigners[$column] = $foreigners_full[$column];
  1625. }
  1626. if (isset($foreigners_full['foreign_keys_data'])) {
  1627. $foreigners['foreign_keys_data'] = $foreigners_full['foreign_keys_data'];
  1628. }
  1629. } else {
  1630. $foreigners = $this->getForeigners($db, $table, $column, 'foreign');
  1631. }
  1632. $foreigner = $this->searchColumnInForeigners($foreigners, $column);
  1633. $child_references = array();
  1634. if ($child_references_full !== null) {
  1635. if (isset($child_references_full[$column])) {
  1636. $child_references = $child_references_full[$column];
  1637. }
  1638. } else {
  1639. $child_references = $this->getChildReferences($db, $table, $column);
  1640. }
  1641. if (sizeof($child_references, 0) > 0
  1642. || $foreigner
  1643. ) {
  1644. if (sizeof($child_references, 0) > 0) {
  1645. $column_status['isReferenced'] = true;
  1646. foreach ($child_references as $columns) {
  1647. array_push(
  1648. $column_status['references'],
  1649. Util::backquote($columns['table_schema'])
  1650. . '.' . Util::backquote($columns['table_name'])
  1651. );
  1652. }
  1653. }
  1654. if ($foreigner) {
  1655. $column_status['isForeignKey'] = true;
  1656. }
  1657. } else {
  1658. $column_status['isEditable'] = true;
  1659. }
  1660. return $column_status;
  1661. }
  1662. /**
  1663. * Search a table column in foreign data.
  1664. *
  1665. * @param array $foreigners Table Foreign data
  1666. * @param string $column Column name
  1667. *
  1668. * @return bool|array
  1669. */
  1670. public function searchColumnInForeigners(array $foreigners, $column)
  1671. {
  1672. if (isset($foreigners[$column])) {
  1673. return $foreigners[$column];
  1674. }
  1675. $foreigner = array();
  1676. foreach ($foreigners['foreign_keys_data'] as $one_key) {
  1677. $column_index = array_search($column, $one_key['index_list']);
  1678. if ($column_index !== false) {
  1679. $foreigner['foreign_field']
  1680. = $one_key['ref_index_list'][$column_index];
  1681. $foreigner['foreign_db'] = isset($one_key['ref_db_name'])
  1682. ? $one_key['ref_db_name']
  1683. : $GLOBALS['db'];
  1684. $foreigner['foreign_table'] = $one_key['ref_table_name'];
  1685. $foreigner['constraint'] = $one_key['constraint'];
  1686. $foreigner['on_update'] = isset($one_key['on_update'])
  1687. ? $one_key['on_update']
  1688. : 'RESTRICT';
  1689. $foreigner['on_delete'] = isset($one_key['on_delete'])
  1690. ? $one_key['on_delete']
  1691. : 'RESTRICT';
  1692. return $foreigner;
  1693. }
  1694. }
  1695. return false;
  1696. }
  1697. /**
  1698. * Returns default PMA table names and their create queries.
  1699. *
  1700. * @return array table name, create query
  1701. */
  1702. public function getDefaultPmaTableNames()
  1703. {
  1704. $pma_tables = array();
  1705. $create_tables_file = file_get_contents(
  1706. SQL_DIR . 'create_tables.sql'
  1707. );
  1708. $queries = explode(';', $create_tables_file);
  1709. foreach ($queries as $query) {
  1710. if (preg_match(
  1711. '/CREATE TABLE IF NOT EXISTS `(.*)` \(/',
  1712. $query,
  1713. $table
  1714. )
  1715. ) {
  1716. $pma_tables[$table[1]] = $query . ';';
  1717. }
  1718. }
  1719. return $pma_tables;
  1720. }
  1721. /**
  1722. * Create a table named phpmyadmin to be used as configuration storage
  1723. *
  1724. * @return bool
  1725. */
  1726. public function createPmaDatabase()
  1727. {
  1728. $GLOBALS['dbi']->tryQuery("CREATE DATABASE IF NOT EXISTS `phpmyadmin`");
  1729. if ($error = $GLOBALS['dbi']->getError()) {
  1730. if ($GLOBALS['errno'] == 1044) {
  1731. $GLOBALS['message'] = __(
  1732. 'You do not have necessary privileges to create a database named'
  1733. . ' \'phpmyadmin\'. You may go to \'Operations\' tab of any'
  1734. . ' database to set up the phpMyAdmin configuration storage there.'
  1735. );
  1736. } else {
  1737. $GLOBALS['message'] = $error;
  1738. }
  1739. return false;
  1740. }
  1741. return true;
  1742. }
  1743. /**
  1744. * Creates PMA tables in the given db, updates if already exists.
  1745. *
  1746. * @param string $db database
  1747. * @param boolean $create whether to create tables if they don't exist.
  1748. *
  1749. * @return void
  1750. */
  1751. public function fixPmaTables($db, $create = true)
  1752. {
  1753. $tablesToFeatures = array(
  1754. 'pma__bookmark' => 'bookmarktable',
  1755. 'pma__relation' => 'relation',
  1756. 'pma__table_info' => 'table_info',
  1757. 'pma__table_coords' => 'table_coords',
  1758. 'pma__pdf_pages' => 'pdf_pages',
  1759. 'pma__column_info' => 'column_info',
  1760. 'pma__history' => 'history',
  1761. 'pma__recent' => 'recent',
  1762. 'pma__favorite' => 'favorite',
  1763. 'pma__table_uiprefs' => 'table_uiprefs',
  1764. 'pma__tracking' => 'tracking',
  1765. 'pma__userconfig' => 'userconfig',
  1766. 'pma__users' => 'users',
  1767. 'pma__usergroups' => 'usergroups',
  1768. 'pma__navigationhiding' => 'navigationhiding',
  1769. 'pma__savedsearches' => 'savedsearches',
  1770. 'pma__central_columns' => 'central_columns',
  1771. 'pma__designer_settings' => 'designer_settings',
  1772. 'pma__export_templates' => 'export_templates',
  1773. );
  1774. $existingTables = $GLOBALS['dbi']->getTables($db, DatabaseInterface::CONNECT_CONTROL);
  1775. $createQueries = null;
  1776. $foundOne = false;
  1777. foreach ($tablesToFeatures as $table => $feature) {
  1778. if (! in_array($table, $existingTables)) {
  1779. if ($create) {
  1780. if ($createQueries == null) { // first create
  1781. $createQueries = $this->getDefaultPmaTableNames();
  1782. $GLOBALS['dbi']->selectDb($db);
  1783. }
  1784. $GLOBALS['dbi']->tryQuery($createQueries[$table]);
  1785. if ($error = $GLOBALS['dbi']->getError()) {
  1786. $GLOBALS['message'] = $error;
  1787. return;
  1788. }
  1789. $foundOne = true;
  1790. $GLOBALS['cfg']['Server'][$feature] = $table;
  1791. }
  1792. } else {
  1793. $foundOne = true;
  1794. $GLOBALS['cfg']['Server'][$feature] = $table;
  1795. }
  1796. }
  1797. if (! $foundOne) {
  1798. return;
  1799. }
  1800. $GLOBALS['cfg']['Server']['pmadb'] = $db;
  1801. $_SESSION['relation'][$GLOBALS['server']] = $this->checkRelationsParam();
  1802. $cfgRelation = $this->getRelationsParam();
  1803. if ($cfgRelation['recentwork'] || $cfgRelation['favoritework']) {
  1804. // Since configuration storage is updated, we need to
  1805. // re-initialize the favorite and recent tables stored in the
  1806. // session from the current configuration storage.
  1807. if ($cfgRelation['favoritework']) {
  1808. $fav_tables = RecentFavoriteTable::getInstance('favorite');
  1809. $_SESSION['tmpval']['favorite_tables'][$GLOBALS['server']]
  1810. = $fav_tables->getFromDb();
  1811. }
  1812. if ($cfgRelation['recentwork']) {
  1813. $recent_tables = RecentFavoriteTable::getInstance('recent');
  1814. $_SESSION['tmpval']['recent_tables'][$GLOBALS['server']]
  1815. = $recent_tables->getFromDb();
  1816. }
  1817. // Reload navi panel to update the recent/favorite lists.
  1818. $GLOBALS['reload'] = true;
  1819. }
  1820. }
  1821. /**
  1822. * Get Html for PMA tables fixing anchor.
  1823. *
  1824. * @param boolean $allTables whether to create all tables
  1825. * @param boolean $createDb whether to create the pmadb also
  1826. *
  1827. * @return string Html
  1828. */
  1829. public function getHtmlFixPmaTables($allTables, $createDb = false)
  1830. {
  1831. $retval = '';
  1832. $url_query = Url::getCommon(array('db' => $GLOBALS['db']), '');
  1833. if ($allTables) {
  1834. if ($createDb) {
  1835. $url_query .= '&amp;goto=db_operations.php&amp;create_pmadb=1';
  1836. $message = Message::notice(
  1837. __(
  1838. '%sCreate%s a database named \'phpmyadmin\' and setup '
  1839. . 'the phpMyAdmin configuration storage there.'
  1840. )
  1841. );
  1842. } else {
  1843. $url_query .= '&amp;goto=db_operations.php&amp;fixall_pmadb=1';
  1844. $message = Message::notice(
  1845. __(
  1846. '%sCreate%s the phpMyAdmin configuration storage in the '
  1847. . 'current database.'
  1848. )
  1849. );
  1850. }
  1851. } else {
  1852. $url_query .= '&amp;goto=db_operations.php&amp;fix_pmadb=1';
  1853. $message = Message::notice(
  1854. __('%sCreate%s missing phpMyAdmin configuration storage tables.')
  1855. );
  1856. }
  1857. $message->addParamHtml('<a href="./chk_rel.php" data-post="' . $url_query . '">');
  1858. $message->addParamHtml('</a>');
  1859. $retval .= $message->getDisplay();
  1860. return $retval;
  1861. }
  1862. /**
  1863. * Gets the relations info and status, depending on the condition
  1864. *
  1865. * @param boolean $condition whether to look for foreigners or not
  1866. * @param string $db database name
  1867. * @param string $table table name
  1868. *
  1869. * @return array ($res_rel, $have_rel)
  1870. */
  1871. public function getRelationsAndStatus($condition, $db, $table)
  1872. {
  1873. if ($condition) {
  1874. // Find which tables are related with the current one and write it in
  1875. // an array
  1876. $res_rel = $this->getForeigners($db, $table);
  1877. if (count($res_rel) > 0) {
  1878. $have_rel = true;
  1879. } else {
  1880. $have_rel = false;
  1881. }
  1882. } else {
  1883. $have_rel = false;
  1884. $res_rel = array();
  1885. } // end if
  1886. return(array($res_rel, $have_rel));
  1887. }
  1888. /**
  1889. * Verifies if all the pmadb tables are defined
  1890. *
  1891. * @return boolean
  1892. */
  1893. public function arePmadbTablesDefined()
  1894. {
  1895. if (empty($GLOBALS['cfg']['Server']['bookmarktable'])
  1896. || empty($GLOBALS['cfg']['Server']['relation'])
  1897. || empty($GLOBALS['cfg']['Server']['table_info'])
  1898. || empty($GLOBALS['cfg']['Server']['table_coords'])
  1899. || empty($GLOBALS['cfg']['Server']['column_info'])
  1900. || empty($GLOBALS['cfg']['Server']['pdf_pages'])
  1901. || empty($GLOBALS['cfg']['Server']['history'])
  1902. || empty($GLOBALS['cfg']['Server']['recent'])
  1903. || empty($GLOBALS['cfg']['Server']['favorite'])
  1904. || empty($GLOBALS['cfg']['Server']['table_uiprefs'])
  1905. || empty($GLOBALS['cfg']['Server']['tracking'])
  1906. || empty($GLOBALS['cfg']['Server']['userconfig'])
  1907. || empty($GLOBALS['cfg']['Server']['users'])
  1908. || empty($GLOBALS['cfg']['Server']['usergroups'])
  1909. || empty($GLOBALS['cfg']['Server']['navigationhiding'])
  1910. || empty($GLOBALS['cfg']['Server']['savedsearches'])
  1911. || empty($GLOBALS['cfg']['Server']['central_columns'])
  1912. || empty($GLOBALS['cfg']['Server']['designer_settings'])
  1913. || empty($GLOBALS['cfg']['Server']['export_templates'])
  1914. ) {
  1915. return false;
  1916. }
  1917. return true;
  1918. }
  1919. /**
  1920. * Get tables for foreign key constraint
  1921. *
  1922. * @param string $foreignDb Database name
  1923. * @param string $tblStorageEngine Table storage engine
  1924. *
  1925. * @return array Table names
  1926. */
  1927. public function getTables($foreignDb, $tblStorageEngine)
  1928. {
  1929. $tables = array();
  1930. $tablesRows = $GLOBALS['dbi']->query(
  1931. 'SHOW TABLE STATUS FROM ' . Util::backquote($foreignDb),
  1932. DatabaseInterface::CONNECT_USER,
  1933. DatabaseInterface::QUERY_STORE
  1934. );
  1935. while ($row = $GLOBALS['dbi']->fetchRow($tablesRows)) {
  1936. if (isset($row[1]) && mb_strtoupper($row[1]) == $tblStorageEngine) {
  1937. $tables[] = $row[0];
  1938. }
  1939. }
  1940. if ($GLOBALS['cfg']['NaturalOrder']) {
  1941. usort($tables, 'strnatcasecmp');
  1942. }
  1943. return $tables;
  1944. }
  1945. }