ChangeController.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. <?php
  2. declare(strict_types=1);
  3. namespace PhpMyAdmin\Controllers\Table;
  4. use PhpMyAdmin\Config\PageSettings;
  5. use PhpMyAdmin\DbTableExists;
  6. use PhpMyAdmin\Html\Generator;
  7. use PhpMyAdmin\InsertEdit;
  8. use PhpMyAdmin\Relation;
  9. use PhpMyAdmin\Response;
  10. use PhpMyAdmin\Template;
  11. use PhpMyAdmin\Url;
  12. use function array_fill;
  13. use function count;
  14. use function is_array;
  15. use function mb_strpos;
  16. use function strlen;
  17. /**
  18. * Displays form for editing and inserting new table rows.
  19. */
  20. class ChangeController extends AbstractController
  21. {
  22. /** @var InsertEdit */
  23. private $insertEdit;
  24. /** @var Relation */
  25. private $relation;
  26. /**
  27. * @param Response $response
  28. * @param string $db Database name.
  29. * @param string $table Table name.
  30. */
  31. public function __construct(
  32. $response,
  33. Template $template,
  34. $db,
  35. $table,
  36. InsertEdit $insertEdit,
  37. Relation $relation
  38. ) {
  39. parent::__construct($response, $template, $db, $table);
  40. $this->insertEdit = $insertEdit;
  41. $this->relation = $relation;
  42. }
  43. public function index(): void
  44. {
  45. global $cfg, $is_upload, $db, $table, $text_dir, $disp_message, $url_params;
  46. global $err_url, $where_clause, $unsaved_values, $insert_mode, $where_clause_array, $where_clauses;
  47. global $result, $rows, $found_unique_key, $after_insert, $comments_map, $table_columns;
  48. global $chg_evt_handler, $timestamp_seen, $columns_cnt, $tabindex, $tabindex_for_function;
  49. global $tabindex_for_null, $tabindex_for_value, $o_rows, $biggest_max_file_size, $has_blob_field;
  50. global $jsvkey, $vkey, $current_result, $repopulate, $checked;
  51. $pageSettings = new PageSettings('Edit');
  52. $this->response->addHTML($pageSettings->getErrorHTML());
  53. $this->response->addHTML($pageSettings->getHTML());
  54. DbTableExists::check();
  55. /**
  56. * Determine whether Insert or Edit and set global variables
  57. */
  58. [
  59. $insert_mode,
  60. $where_clause,
  61. $where_clause_array,
  62. $where_clauses,
  63. $result,
  64. $rows,
  65. $found_unique_key,
  66. $after_insert,
  67. ] = $this->insertEdit->determineInsertOrEdit(
  68. $where_clause ?? null,
  69. $db,
  70. $table
  71. );
  72. // Increase number of rows if unsaved rows are more
  73. if (! empty($unsaved_values) && count($rows) < count($unsaved_values)) {
  74. $rows = array_fill(0, count($unsaved_values), false);
  75. }
  76. /**
  77. * Defines the url to return to in case of error in a sql statement
  78. * (at this point, $GLOBALS['goto'] will be set but could be empty)
  79. */
  80. if (empty($GLOBALS['goto'])) {
  81. if (strlen($table) > 0) {
  82. // avoid a problem (see bug #2202709)
  83. $GLOBALS['goto'] = Url::getFromRoute('/table/sql');
  84. } else {
  85. $GLOBALS['goto'] = Url::getFromRoute('/database/sql');
  86. }
  87. }
  88. $_url_params = $this->insertEdit->getUrlParameters($db, $table);
  89. $err_url = $GLOBALS['goto'] . Url::getCommon(
  90. $_url_params,
  91. mb_strpos($GLOBALS['goto'], '?') === false ? '?' : '&'
  92. );
  93. unset($_url_params);
  94. $comments_map = $this->insertEdit->getCommentsMap($db, $table);
  95. /**
  96. * START REGULAR OUTPUT
  97. */
  98. $this->addScriptFiles([
  99. 'makegrid.js',
  100. 'vendor/stickyfill.min.js',
  101. 'sql.js',
  102. 'table/change.js',
  103. 'vendor/jquery/additional-methods.js',
  104. 'gis_data_editor.js',
  105. ]);
  106. /**
  107. * Displays the query submitted and its result
  108. *
  109. * $disp_message come from /table/replace
  110. */
  111. if (! empty($disp_message)) {
  112. $this->response->addHTML(Generator::getMessage($disp_message, null));
  113. }
  114. $table_columns = $this->insertEdit->getTableColumns($db, $table);
  115. // retrieve keys into foreign fields, if any
  116. $foreigners = $this->relation->getForeigners($db, $table);
  117. // Retrieve form parameters for insert/edit form
  118. $_form_params = $this->insertEdit->getFormParametersForInsertForm(
  119. $db,
  120. $table,
  121. $where_clauses,
  122. $where_clause_array,
  123. $err_url
  124. );
  125. /**
  126. * Displays the form
  127. */
  128. // autocomplete feature of IE kills the "onchange" event handler and it
  129. // must be replaced by the "onpropertychange" one in this case
  130. $chg_evt_handler = 'onchange';
  131. // Had to put the URI because when hosted on an https server,
  132. // some browsers send wrongly this form to the http server.
  133. $html_output = '';
  134. // Set if we passed the first timestamp field
  135. $timestamp_seen = false;
  136. $columns_cnt = count($table_columns);
  137. $tabindex = 0;
  138. $tabindex_for_function = +3000;
  139. $tabindex_for_null = +6000;
  140. $tabindex_for_value = 0;
  141. $o_rows = 0;
  142. $biggest_max_file_size = 0;
  143. $url_params['db'] = $db;
  144. $url_params['table'] = $table;
  145. $url_params = $this->insertEdit->urlParamsInEditMode(
  146. $url_params,
  147. $where_clause_array
  148. );
  149. $has_blob_field = false;
  150. foreach ($table_columns as $column) {
  151. if ($this->insertEdit->isColumn(
  152. $column,
  153. [
  154. 'blob',
  155. 'tinyblob',
  156. 'mediumblob',
  157. 'longblob',
  158. ]
  159. )) {
  160. $has_blob_field = true;
  161. break;
  162. }
  163. }
  164. //Insert/Edit form
  165. //If table has blob fields we have to disable ajax.
  166. $html_output .= $this->insertEdit->getHtmlForInsertEditFormHeader($has_blob_field, $is_upload);
  167. $html_output .= Url::getHiddenInputs($_form_params);
  168. // user can toggle the display of Function column and column types
  169. // (currently does not work for multi-edits)
  170. if (! $cfg['ShowFunctionFields'] || ! $cfg['ShowFieldTypesInDataEditView']) {
  171. $html_output .= __('Show');
  172. }
  173. if (! $cfg['ShowFunctionFields']) {
  174. $html_output .= $this->insertEdit->showTypeOrFunction('function', $url_params, false);
  175. }
  176. if (! $cfg['ShowFieldTypesInDataEditView']) {
  177. $html_output .= $this->insertEdit->showTypeOrFunction('type', $url_params, false);
  178. }
  179. $GLOBALS['plugin_scripts'] = [];
  180. foreach ($rows as $row_id => $current_row) {
  181. if (empty($current_row)) {
  182. $current_row = [];
  183. }
  184. $jsvkey = $row_id;
  185. $vkey = '[multi_edit][' . $jsvkey . ']';
  186. $current_result = (isset($result) && is_array($result) && isset($result[$row_id])
  187. ? $result[$row_id]
  188. : $result);
  189. $repopulate = [];
  190. $checked = true;
  191. if (isset($unsaved_values[$row_id])) {
  192. $repopulate = $unsaved_values[$row_id];
  193. $checked = false;
  194. }
  195. if ($insert_mode && $row_id > 0) {
  196. $html_output .= $this->insertEdit->getHtmlForIgnoreOption($row_id, $checked);
  197. }
  198. $html_output .= $this->insertEdit->getHtmlForInsertEditRow(
  199. $url_params,
  200. $table_columns,
  201. $comments_map,
  202. $timestamp_seen,
  203. $current_result,
  204. $chg_evt_handler,
  205. $jsvkey,
  206. $vkey,
  207. $insert_mode,
  208. $current_row,
  209. $o_rows,
  210. $tabindex,
  211. $columns_cnt,
  212. $is_upload,
  213. $tabindex_for_function,
  214. $foreigners,
  215. $tabindex_for_null,
  216. $tabindex_for_value,
  217. $table,
  218. $db,
  219. $row_id,
  220. $biggest_max_file_size,
  221. $text_dir,
  222. $repopulate,
  223. $where_clause_array
  224. );
  225. }
  226. $this->addScriptFiles($GLOBALS['plugin_scripts']);
  227. unset($unsaved_values, $checked, $repopulate, $GLOBALS['plugin_scripts']);
  228. if (! isset($after_insert)) {
  229. $after_insert = 'back';
  230. }
  231. //action panel
  232. $html_output .= $this->insertEdit->getActionsPanel(
  233. $where_clause,
  234. $after_insert,
  235. $tabindex,
  236. $tabindex_for_value,
  237. $found_unique_key
  238. );
  239. if ($biggest_max_file_size > 0) {
  240. $html_output .= '<input type="hidden" name="MAX_FILE_SIZE" value="' . $biggest_max_file_size . '">' . "\n";
  241. }
  242. $html_output .= '</form>';
  243. $html_output .= $this->insertEdit->getHtmlForGisEditor();
  244. // end Insert/Edit form
  245. if ($insert_mode) {
  246. //Continue insertion form
  247. $html_output .= $this->insertEdit->getContinueInsertionForm(
  248. $table,
  249. $db,
  250. $where_clause_array,
  251. $err_url
  252. );
  253. }
  254. $this->response->addHTML($html_output);
  255. }
  256. public function rows(): void
  257. {
  258. global $active_page, $where_clause;
  259. if (isset($_POST['goto']) && (! isset($_POST['rows_to_delete']) || ! is_array($_POST['rows_to_delete']))) {
  260. $this->response->setRequestStatus(false);
  261. $this->response->addJSON('message', __('No row selected.'));
  262. return;
  263. }
  264. // As we got the rows to be edited from the
  265. // 'rows_to_delete' checkbox, we use the index of it as the
  266. // indicating WHERE clause. Then we build the array which is used
  267. // for the /table/change script.
  268. $where_clause = [];
  269. if (isset($_POST['rows_to_delete']) && is_array($_POST['rows_to_delete'])) {
  270. foreach ($_POST['rows_to_delete'] as $i => $i_where_clause) {
  271. $where_clause[] = $i_where_clause;
  272. }
  273. }
  274. $active_page = Url::getFromRoute('/table/change');
  275. $this->index();
  276. }
  277. }