Designer.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. <?php
  2. declare(strict_types=1);
  3. namespace PhpMyAdmin\Database;
  4. use PhpMyAdmin\Database\Designer\DesignerTable;
  5. use PhpMyAdmin\DatabaseInterface;
  6. use PhpMyAdmin\Message;
  7. use PhpMyAdmin\Plugins;
  8. use PhpMyAdmin\Relation;
  9. use PhpMyAdmin\Template;
  10. use PhpMyAdmin\Util;
  11. use stdClass;
  12. use function count;
  13. use function intval;
  14. use function is_array;
  15. use function json_decode;
  16. use function json_encode;
  17. use function strpos;
  18. /**
  19. * Set of functions related to database designer
  20. */
  21. class Designer
  22. {
  23. /** @var DatabaseInterface */
  24. private $dbi;
  25. /** @var Relation */
  26. private $relation;
  27. /** @var Template */
  28. public $template;
  29. /**
  30. * @param DatabaseInterface $dbi DatabaseInterface object
  31. * @param Relation $relation Relation instance
  32. * @param Template $template Template instance
  33. */
  34. public function __construct(DatabaseInterface $dbi, Relation $relation, Template $template)
  35. {
  36. $this->dbi = $dbi;
  37. $this->relation = $relation;
  38. $this->template = $template;
  39. }
  40. /**
  41. * Function to get html for displaying the page edit/delete form
  42. *
  43. * @param string $db database name
  44. * @param string $operation 'edit' or 'delete' depending on the operation
  45. *
  46. * @return string html content
  47. */
  48. public function getHtmlForEditOrDeletePages($db, $operation)
  49. {
  50. $cfgRelation = $this->relation->getRelationsParam();
  51. return $this->template->render('database/designer/edit_delete_pages', [
  52. 'db' => $db,
  53. 'operation' => $operation,
  54. 'pdfwork' => $cfgRelation['pdfwork'],
  55. 'pages' => $this->getPageIdsAndNames($db),
  56. ]);
  57. }
  58. /**
  59. * Function to get html for displaying the page save as form
  60. *
  61. * @param string $db database name
  62. *
  63. * @return string html content
  64. */
  65. public function getHtmlForPageSaveAs($db)
  66. {
  67. $cfgRelation = $this->relation->getRelationsParam();
  68. return $this->template->render('database/designer/page_save_as', [
  69. 'db' => $db,
  70. 'pdfwork' => $cfgRelation['pdfwork'],
  71. 'pages' => $this->getPageIdsAndNames($db),
  72. ]);
  73. }
  74. /**
  75. * Retrieve IDs and names of schema pages
  76. *
  77. * @param string $db database name
  78. *
  79. * @return array array of schema page id and names
  80. */
  81. private function getPageIdsAndNames($db)
  82. {
  83. $result = [];
  84. $cfgRelation = $this->relation->getRelationsParam();
  85. if (! $cfgRelation['pdfwork']) {
  86. return $result;
  87. }
  88. $page_query = 'SELECT `page_nr`, `page_descr` FROM '
  89. . Util::backquote($cfgRelation['db']) . '.'
  90. . Util::backquote($cfgRelation['pdf_pages'])
  91. . " WHERE db_name = '" . $this->dbi->escapeString($db) . "'"
  92. . ' ORDER BY `page_descr`';
  93. $page_rs = $this->relation->queryAsControlUser(
  94. $page_query,
  95. false,
  96. DatabaseInterface::QUERY_STORE
  97. );
  98. while ($curr_page = $this->dbi->fetchAssoc($page_rs)) {
  99. $result[intval($curr_page['page_nr'])] = $curr_page['page_descr'];
  100. }
  101. return $result;
  102. }
  103. /**
  104. * Function to get html for displaying the schema export
  105. *
  106. * @param string $db database name
  107. * @param int $page the page to be exported
  108. *
  109. * @return string
  110. */
  111. public function getHtmlForSchemaExport($db, $page)
  112. {
  113. $export_list = Plugins::getSchema();
  114. /* Fail if we didn't find any schema plugin */
  115. if (empty($export_list)) {
  116. return Message::error(
  117. __('Could not load schema plugins, please check your installation!')
  118. )->getDisplay();
  119. }
  120. return $this->template->render('database/designer/schema_export', [
  121. 'db' => $db,
  122. 'page' => $page,
  123. 'export_list' => $export_list,
  124. ]);
  125. }
  126. /**
  127. * Returns array of stored values of Designer Settings
  128. *
  129. * @return array stored values
  130. */
  131. private function getSideMenuParamsArray()
  132. {
  133. /** @var DatabaseInterface $dbi */
  134. global $dbi;
  135. $params = [];
  136. $cfgRelation = $this->relation->getRelationsParam();
  137. if ($cfgRelation['designersettingswork']) {
  138. $query = 'SELECT `settings_data` FROM '
  139. . Util::backquote($cfgRelation['db']) . '.'
  140. . Util::backquote($cfgRelation['designer_settings'])
  141. . ' WHERE ' . Util::backquote('username') . ' = "'
  142. . $dbi->escapeString($GLOBALS['cfg']['Server']['user'])
  143. . '";';
  144. $result = $this->dbi->fetchSingleRow($query);
  145. if (is_array($result)) {
  146. $params = json_decode((string) $result['settings_data'], true);
  147. }
  148. }
  149. return $params;
  150. }
  151. /**
  152. * Returns class names for various buttons on Designer Side Menu
  153. *
  154. * @return array class names of various buttons
  155. */
  156. public function returnClassNamesFromMenuButtons()
  157. {
  158. $classes_array = [];
  159. $params_array = $this->getSideMenuParamsArray();
  160. if (isset($params_array['angular_direct'])
  161. && $params_array['angular_direct'] === 'angular'
  162. ) {
  163. $classes_array['angular_direct'] = 'M_butt_Selected_down';
  164. } else {
  165. $classes_array['angular_direct'] = 'M_butt';
  166. }
  167. if (isset($params_array['snap_to_grid'])
  168. && $params_array['snap_to_grid'] === 'on'
  169. ) {
  170. $classes_array['snap_to_grid'] = 'M_butt_Selected_down';
  171. } else {
  172. $classes_array['snap_to_grid'] = 'M_butt';
  173. }
  174. if (isset($params_array['pin_text'])
  175. && $params_array['pin_text'] === 'true'
  176. ) {
  177. $classes_array['pin_text'] = 'M_butt_Selected_down';
  178. } else {
  179. $classes_array['pin_text'] = 'M_butt';
  180. }
  181. if (isset($params_array['relation_lines'])
  182. && $params_array['relation_lines'] === 'false'
  183. ) {
  184. $classes_array['relation_lines'] = 'M_butt_Selected_down';
  185. } else {
  186. $classes_array['relation_lines'] = 'M_butt';
  187. }
  188. if (isset($params_array['small_big_all'])
  189. && $params_array['small_big_all'] === 'v'
  190. ) {
  191. $classes_array['small_big_all'] = 'M_butt_Selected_down';
  192. } else {
  193. $classes_array['small_big_all'] = 'M_butt';
  194. }
  195. if (isset($params_array['side_menu'])
  196. && $params_array['side_menu'] === 'true'
  197. ) {
  198. $classes_array['side_menu'] = 'M_butt_Selected_down';
  199. } else {
  200. $classes_array['side_menu'] = 'M_butt';
  201. }
  202. return $classes_array;
  203. }
  204. /**
  205. * Get HTML to display tables on designer page
  206. *
  207. * @param string $db The database name from the request
  208. * @param DesignerTable[] $designerTables The designer tables
  209. * @param array $tab_pos tables positions
  210. * @param int $display_page page number of the selected page
  211. * @param array $tab_column table column info
  212. * @param array $tables_all_keys all indices
  213. * @param array $tables_pk_or_unique_keys unique or primary indices
  214. *
  215. * @return string html
  216. */
  217. public function getDatabaseTables(
  218. string $db,
  219. array $designerTables,
  220. array $tab_pos,
  221. $display_page,
  222. array $tab_column,
  223. array $tables_all_keys,
  224. array $tables_pk_or_unique_keys
  225. ) {
  226. global $text_dir;
  227. $columns_type = [];
  228. foreach ($designerTables as $designerTable) {
  229. $table_name = $designerTable->getDbTableString();
  230. $limit = count($tab_column[$table_name]['COLUMN_ID']);
  231. for ($j = 0; $j < $limit; $j++) {
  232. $table_column_name = $table_name . '.' . $tab_column[$table_name]['COLUMN_NAME'][$j];
  233. if (isset($tables_pk_or_unique_keys[$table_column_name])) {
  234. $columns_type[$table_column_name] = 'designer/FieldKey_small';
  235. } else {
  236. $columns_type[$table_column_name] = 'designer/Field_small';
  237. if (strpos($tab_column[$table_name]['TYPE'][$j], 'char') !== false
  238. || strpos($tab_column[$table_name]['TYPE'][$j], 'text') !== false
  239. ) {
  240. $columns_type[$table_column_name] .= '_char';
  241. } elseif (strpos($tab_column[$table_name]['TYPE'][$j], 'int') !== false
  242. || strpos($tab_column[$table_name]['TYPE'][$j], 'float') !== false
  243. || strpos($tab_column[$table_name]['TYPE'][$j], 'double') !== false
  244. || strpos($tab_column[$table_name]['TYPE'][$j], 'decimal') !== false
  245. ) {
  246. $columns_type[$table_column_name] .= '_int';
  247. } elseif (strpos($tab_column[$table_name]['TYPE'][$j], 'date') !== false
  248. || strpos($tab_column[$table_name]['TYPE'][$j], 'time') !== false
  249. || strpos($tab_column[$table_name]['TYPE'][$j], 'year') !== false
  250. ) {
  251. $columns_type[$table_column_name] .= '_date';
  252. }
  253. }
  254. }
  255. }
  256. return $this->template->render('database/designer/database_tables', [
  257. 'db' => $GLOBALS['db'],
  258. 'text_dir' => $text_dir,
  259. 'get_db' => $db,
  260. 'has_query' => isset($_REQUEST['query']),
  261. 'tab_pos' => $tab_pos,
  262. 'display_page' => $display_page,
  263. 'tab_column' => $tab_column,
  264. 'tables_all_keys' => $tables_all_keys,
  265. 'tables_pk_or_unique_keys' => $tables_pk_or_unique_keys,
  266. 'tables' => $designerTables,
  267. 'columns_type' => $columns_type,
  268. 'theme' => $GLOBALS['PMA_Theme'],
  269. ]);
  270. }
  271. /**
  272. * Returns HTML for Designer page
  273. *
  274. * @param string $db database in use
  275. * @param string $getDb database in url
  276. * @param DesignerTable[] $designerTables The designer tables
  277. * @param array $scriptTables array on foreign key support for each table
  278. * @param array $scriptContr initialization data array
  279. * @param DesignerTable[] $scriptDisplayField displayed tables in designer with their display fields
  280. * @param int $displayPage page number of the selected page
  281. * @param bool $visualBuilderMode whether this is visual query builder
  282. * @param string $selectedPage name of the selected page
  283. * @param array $paramsArray array with class name for various buttons on side menu
  284. * @param array|null $tabPos table positions
  285. * @param array $tabColumn table column info
  286. * @param array $tablesAllKeys all indices
  287. * @param array $tablesPkOrUniqueKeys unique or primary indices
  288. *
  289. * @return string html
  290. */
  291. public function getHtmlForMain(
  292. string $db,
  293. string $getDb,
  294. array $designerTables,
  295. array $scriptTables,
  296. array $scriptContr,
  297. array $scriptDisplayField,
  298. $displayPage,
  299. bool $visualBuilderMode,
  300. $selectedPage,
  301. array $paramsArray,
  302. ?array $tabPos,
  303. array $tabColumn,
  304. array $tablesAllKeys,
  305. array $tablesPkOrUniqueKeys
  306. ): string {
  307. global $text_dir;
  308. $cfgRelation = $this->relation->getRelationsParam();
  309. $columnsType = [];
  310. foreach ($designerTables as $designerTable) {
  311. $tableName = $designerTable->getDbTableString();
  312. $limit = count($tabColumn[$tableName]['COLUMN_ID']);
  313. for ($j = 0; $j < $limit; $j++) {
  314. $tableColumnName = $tableName . '.' . $tabColumn[$tableName]['COLUMN_NAME'][$j];
  315. if (isset($tablesPkOrUniqueKeys[$tableColumnName])) {
  316. $columnsType[$tableColumnName] = 'designer/FieldKey_small';
  317. } else {
  318. $columnsType[$tableColumnName] = 'designer/Field_small';
  319. if (strpos($tabColumn[$tableName]['TYPE'][$j], 'char') !== false
  320. || strpos($tabColumn[$tableName]['TYPE'][$j], 'text') !== false
  321. ) {
  322. $columnsType[$tableColumnName] .= '_char';
  323. } elseif (strpos($tabColumn[$tableName]['TYPE'][$j], 'int') !== false
  324. || strpos($tabColumn[$tableName]['TYPE'][$j], 'float') !== false
  325. || strpos($tabColumn[$tableName]['TYPE'][$j], 'double') !== false
  326. || strpos($tabColumn[$tableName]['TYPE'][$j], 'decimal') !== false
  327. ) {
  328. $columnsType[$tableColumnName] .= '_int';
  329. } elseif (strpos($tabColumn[$tableName]['TYPE'][$j], 'date') !== false
  330. || strpos($tabColumn[$tableName]['TYPE'][$j], 'time') !== false
  331. || strpos($tabColumn[$tableName]['TYPE'][$j], 'year') !== false
  332. ) {
  333. $columnsType[$tableColumnName] .= '_date';
  334. }
  335. }
  336. }
  337. }
  338. $displayedFields = [];
  339. foreach ($scriptDisplayField as $designerTable) {
  340. if ($designerTable->getDisplayField() === null) {
  341. continue;
  342. }
  343. $displayedFields[$designerTable->getTableName()] = $designerTable->getDisplayField();
  344. }
  345. $designerConfig = new stdClass();
  346. $designerConfig->db = $db;
  347. $designerConfig->scriptTables = $scriptTables;
  348. $designerConfig->scriptContr = $scriptContr;
  349. $designerConfig->server = $GLOBALS['server'];
  350. $designerConfig->scriptDisplayField = $displayedFields;
  351. $designerConfig->displayPage = (int) $displayPage;
  352. $designerConfig->tablesEnabled = $cfgRelation['pdfwork'];
  353. return $this->template->render('database/designer/main', [
  354. 'db' => $db,
  355. 'text_dir' => $text_dir,
  356. 'get_db' => $getDb,
  357. 'designer_config' => json_encode($designerConfig),
  358. 'display_page' => (int) $displayPage,
  359. 'has_query' => $visualBuilderMode,
  360. 'visual_builder' => $visualBuilderMode,
  361. 'selected_page' => $selectedPage,
  362. 'params_array' => $paramsArray,
  363. 'theme' => $GLOBALS['PMA_Theme'],
  364. 'tab_pos' => $tabPos,
  365. 'tab_column' => $tabColumn,
  366. 'tables_all_keys' => $tablesAllKeys,
  367. 'tables_pk_or_unique_keys' => $tablesPkOrUniqueKeys,
  368. 'designerTables' => $designerTables,
  369. 'columns_type' => $columnsType,
  370. ]);
  371. }
  372. }