ChangeController.php 9.2 KB

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