PluginProbe
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards / 5.5.84
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards v5.5.84
5.5.84 5.5.83 5.5.82 5.5.81 5.5.80 5.5.79 5.5.77 5.5.76 5.5.75 5.5.73 5.5.72 5.5.22 5.5.23 5.5.29 5.5.3 5.5.31 5.5.32 5.5.34 5.5.35 5.5.36 5.5.37 5.5.4 5.5.40 5.5.41 5.5.42 All 160 releases
wp-data-access / WPDataAccess / Design_Table / WPDA_Design_Table_Form.php

WPDA_Design_Table_Form.php in WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards 5.5.84, at WPDataAccess/Design_Table/WPDA_Design_Table_Form.php

2,778 lines 107.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Suppress "error - 0 - No summary was found for this file" on phpdoc generation
5 *
6 * @package WPDataAccess\Design_Table
7 */
8
9 namespace WPDataAccess\Design_Table {
10
11 use WPDataAccess\Connection\WPDADB;
12 use WPDataAccess\Data_Dictionary\WPDA_Dictionary_Exist;
13 use WPDataAccess\Data_Dictionary\WPDA_Dictionary_Lists;
14 use WPDataAccess\Data_Dictionary\WPDA_List_Columns_Cache;
15 use WPDataAccess\Utilities\WPDA_Message_Box;
16 use WPDataAccess\Utilities\WPDA_Reverse_Engineering;
17 use WPDataAccess\Plugin_Table_Models\WPDA_Design_Table_Model;
18 use WPDataAccess\WPDA;
19 use WPDataAccess\Dashboard\WPDA_Dashboard;
20
21 /**
22 * Class WPDA_Design_Table_Form
23 *
24 * This class provides the user interface of the Data Designer tool. The form can be used to create or alter table
25 * and index designs. Tables and indexes can be created from their design by using the appropriate buttons.
26 *
27 * Tables and their indexes can be reverse engineered from the database. This process can be started from scratch
28 * or applied to an existing table design. For the last situation users can use the reconcile button. This will
29 * bring the table and index design in the same state as their physical database counterpart.
30 *
31 * @author Peter Schulz
32 * @since 1.1.0
33 */
34 class WPDA_Design_Table_Form {
35
36 const NEW_LINE = '<br/>';
37
38 /**
39 * Page name (wpda_designer)
40 *
41 * @var string|null
42 */
43 protected $page = null;
44
45 /**
46 * Action argument
47 *
48 * @var string|null
49 */
50 protected $action = null;
51
52 /**
53 * Saved value of action2 argument used for later equations
54 *
55 * @var string|null
56 */
57 protected $action2 = null;
58
59 /**
60 * Action2 argument
61 *
62 * @var string|null
63 */
64 protected $action2_posted = null;
65
66 /**
67 * Object of type WPDA_Design_Table_Model used for data manipulation
68 *
69 * @var object|null
70 */
71 protected $model = null;
72
73 /**
74 * Table name
75 *
76 * @var string|null
77 */
78 protected $wpda_table_name = null;
79
80 /**
81 * Schema name
82 *
83 * @var string|null
84 */
85 protected $wpda_schema_name = null;
86
87 /**
88 * Table design (taken from WPDA_Design_Table_Model)
89 *
90 * @var array|null
91 */
92 protected $wpda_table_design = null;
93
94 /**
95 * Indicates whether the table was found in the database
96 *
97 * @var boolean|null
98 */
99 protected $table_exists = null;
100
101 /**
102 * Indicates whether the structure of the database table equals the table design
103 *
104 * @var boolean|null
105 */
106 protected $table_altered = false;
107
108 /**
109 * Indicates whether the table is a WordPress table
110 *
111 * @var boolean|null
112 */
113 protected $is_wp_table = false;
114
115 /**
116 * Create table statement for the designed table at startup. Updates in the user interface are not immediately
117 * reflected. User needs to reload/save the page.
118 *
119 * @var string
120 */
121 protected $create_table_statement = '';
122
123 /**
124 * Alter table statement for the designed table at startup. Updates in the user interface are not immediately
125 * reflected. User needs to reload/save the page.
126 *
127 * @var array
128 */
129 protected $alter_table_statement = array();
130
131 /**
132 * Create index statement(s) for the designed index(es) at startup. Updates in the user interface are not
133 * immediately reflected. User needs to reload/save the page.
134 *
135 * @var string
136 */
137 protected $create_index_statement = '';
138
139 /**
140 * Indicates where a create table statement succeeded
141 *
142 * @var boolean|null
143 */
144 protected $create_table_succeeded = null;
145
146 /**
147 * Indicates where a alter table statement succeeded
148 *
149 * @var boolean|null
150 */
151 protected $alter_table_succeeded = null;
152
153 /**
154 * Indicates where a create index statement failed
155 *
156 * @var boolean|null
157 */
158 protected $create_index_failed = null;
159
160 /**
161 * Named array holding table columns
162 *
163 * @see WPDA_List_Columns_Cache::get_list_columns()
164 *
165 * @var array|null
166 */
167 protected $table_columns = null;
168
169 /**
170 * Allowed values are: Basic or Advanced. Can be supplied as an argument. Taken from WPDA class f no argument
171 * is provided.
172 *
173 * @see WPDA::OPTION_BE_DESIGN_MODE
174 *
175 * @var string|null
176 */
177 protected $design_mode = null;
178
179 /**
180 * Last error from wpdb
181 *
182 * @var string|null
183 */
184 protected $wpdb_error = null;
185
186 /**
187 * Indicates whether columns or indexes were deleted in the actual design. If true checkbox "Show deleted
188 * columns and indexes" will be accessible.
189 *
190 * @var bool
191 */
192 protected $deleted_columns_and_indexes = false;
193
194 /**
195 * Indicates whether indexes were updated in the actual design.
196 *
197 * @var bool
198 */
199 protected $updated_indexes = false;
200
201 /**
202 * Holds the structure of the real physical database table
203 *
204 * @var array|null
205 */
206 protected $real_table = null;
207
208 /**
209 * Holds the structure of the real physical database indexes
210 *
211 * @var array|null
212 */
213 protected $real_indexes = null;
214
215 /**
216 * Indicates whether the design has indexes.
217 *
218 * @var bool
219 */
220 protected $indexes_found = false;
221
222 /**
223 * Argument which can be used to jump back to another page than the default table list for table designs.
224 *
225 * @var string
226 */
227 protected $caller = '';
228
229 /**
230 * Holds all available databases
231 *
232 * @var array
233 */
234 protected $database = array();
235
236 protected $databases;
237
238 protected $fulltext_support = false;
239
240 /**
241 * WPDA_Design_Table_Form constructor.
242 *
243 * @since 1.1.0
244 */
245 public function __construct() {
246 if ( isset( $_REQUEST['page'] ) ) { // phpcs:ignore
247 $this->page = sanitize_text_field( wp_unslash( $_REQUEST['page'] ) ); // phpcs:ignore
248 } else {
249 wp_die( esc_attr__( 'ERROR: Wrong arguments [page not found]', 'wp-data-access' ) );
250 }
251
252 if ( isset( $_REQUEST['action'] ) ) { // phpcs:ignore
253 $this->action = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); // phpcs:ignore
254 }
255
256 if ( isset( $_REQUEST['action2'] ) ) { // phpcs:ignore
257 $this->action2 = sanitize_text_field( wp_unslash( $_REQUEST['action2'] ) ); // phpcs:ignore
258 $this->action2_posted = $this->action2;
259 }
260
261 if ( isset( $_REQUEST['design_mode'] ) ) { // phpcs:ignore
262 $this->design_mode = sanitize_text_field( wp_unslash( $_REQUEST['design_mode'] ) ); // phpcs:ignore
263 } else {
264 $this->design_mode = WPDA::get_option( WPDA::OPTION_BE_DESIGN_MODE ); // Default design mode.
265 }
266
267 if ( isset( $_REQUEST['caller'] ) ) { // phpcs:ignore
268 $this->caller = sanitize_text_field( wp_unslash( $_REQUEST['caller'] ) ); // phpcs:ignore
269 }
270
271 $this->fulltext_support = get_option( 'wpda_fulltext_support' );
272
273 global $wpdb;
274
275 if ( 'init' === $this->action2 ) {
276 if ( isset( $_REQUEST['wpda_table_name'] ) && isset( $_REQUEST['wpda_schema_name'] ) ) { // phpcs:ignore
277 // Check if table is already in repository.
278 $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
279 $wpdb->prepare(
280 'select * from `%1s` where wpda_schema_name = %s and wpda_table_name = %s ', // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders
281 array(
282 WPDA::remove_backticks( WPDA_Design_Table_Model::get_base_table_name() ),
283 sanitize_text_field( wp_unslash( $_REQUEST['wpda_schema_name'] ) ), // phpcs:ignore
284 sanitize_text_field( wp_unslash( $_REQUEST['wpda_table_name'] ) ), // phpcs:ignore
285 )
286 )
287 );
288 if ( 1 === $wpdb->num_rows ) {
289 // Table already in repository.
290 $this->action2 = 'edit';
291 } else {
292 // Table not in repository.
293 $this->action2 = 'wpda_reverse_engineering';
294 }
295 } else {
296 wp_die( esc_attr__( 'ERROR: Wrong arguments [table not found]', 'wp-data-access' ) );
297 }
298 }
299
300 if ( 'wpda_reverse_engineering' === $this->action2 || 'wpda_reconcile' === $this->action2 ) {
301 if ( isset( $_REQUEST['wpda_table_name_re'] ) && isset( $_REQUEST['wpda_schema_name_re'] ) ) { // phpcs:ignore
302 $wpda_table_name_re = sanitize_text_field( wp_unslash( $_REQUEST['wpda_table_name_re'] ) ); // phpcs:ignore
303 $wpda_schema_name_re = sanitize_text_field( wp_unslash( $_REQUEST['wpda_schema_name_re'] ) ); // phpcs:ignore
304 if ( 'wpda_reconcile' === $this->action2 ) {
305 // Before table can be reconciled old table structure must be deleted.
306 $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
307 $wpdb->prepare(
308 'delete from `%1s` where wpda_table_name = %s and wpda_schema_name = %s ', // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders
309 array(
310 WPDA::remove_backticks( WPDA_Design_Table_Model::get_base_table_name() ),
311 $wpda_table_name_re,
312 $wpda_schema_name_re,
313 )
314 )
315 );
316 }
317 // Start reverse engineering table.
318 $wpda_reverse_engineering = new WPDA_Reverse_Engineering( $wpda_table_name_re, $wpda_schema_name_re );
319 $this->design_mode = isset( $_REQUEST['design_mode_re'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['design_mode_re'] ) ) : $this->design_mode; // phpcs:ignore
320 $table_structure = $wpda_reverse_engineering->get_designer_format( $this->design_mode );
321 if ( count( $table_structure ) > 0 ) { // phpcs:ignore -- 8.1 proof
322 if ( isset( $_REQUEST['wpda_table_name'] ) && '' !== trim( $_REQUEST['wpda_table_name'] ) ) { // phpcs:ignore
323 $this->wpda_table_name = sanitize_text_field( wp_unslash( $_REQUEST['wpda_table_name'] ) ); // phpcs:ignore
324 } else {
325 $this->wpda_table_name = $wpda_table_name_re;
326 }
327 if ( isset( $_REQUEST['wpda_schema_name'] ) && '' !== trim( $_REQUEST['wpda_schema_name'] ) ) { // phpcs:ignore
328 $this->wpda_schema_name = sanitize_text_field( wp_unslash( $_REQUEST['wpda_schema_name'] ) ); // phpcs:ignore
329 } else {
330 $this->wpda_schema_name = $wpda_schema_name_re;
331 }
332 $this->wpda_table_design = $table_structure;
333 } else {
334 wp_die( esc_attr__( 'ERROR: Reverse engineering table failed [invalid structure]', 'wp-data-access' ) );
335 }
336 if ( ! WPDA_Design_Table_Model::insert_reverse_engineered( $this->wpda_table_name, $this->wpda_schema_name, $this->wpda_table_design ) ) {
337 wp_die( esc_attr__( 'ERROR: Reverse engineering table failed [insert failed]', 'wp-data-access' ) );
338 } else {
339 // Convert named array to object (needed to display structure).
340 $this->wpda_table_design = json_decode( json_encode( $table_structure ) );
341 }
342 $this->action2 = 'edit';
343 } else {
344 wp_die( esc_attr__( 'ERROR: Wrong arguments [table not found]', 'wp-data-access' ) );
345 }
346 } elseif ( isset( $_REQUEST['wpda_table_name'] ) && isset( $_REQUEST['wpda_schema_name'] ) ) { // phpcs:ignore
347 $this->wpda_table_name = sanitize_text_field( wp_unslash( $_REQUEST['wpda_table_name'] ) ); // phpcs:ignore
348 $this->wpda_schema_name = sanitize_text_field( wp_unslash( $_REQUEST['wpda_schema_name'] ) ); // phpcs:ignore
349 $this->model = new WPDA_Design_Table_Model();
350
351 if ( 'new' === $this->action2 ) {
352 $insert_result = $this->model->insert();
353 if ( false === $insert_result || $insert_result < 1 ) {
354 wp_die( esc_attr__( 'ERROR: Insert failed', 'wp-data-access' ) );
355 }
356 $this->action2 = 'edit'; // Show saved records and allow editing.
357 } elseif ( 'edit' === $this->action2 && 'init' !== $this->action2_posted ) {
358 $result_update = $this->model->update();
359 if ( false === $result_update ) {
360 $msg = new WPDA_Message_Box(
361 array(
362 'message_text' => __( 'Update failed', 'wp-data-access' ),
363 'message_type' => 'error',
364 'message_is_dismissible' => false,
365 )
366 );
367 $msg->box();
368 }
369 if ( 0 === $result_update ) {
370 $msg = new WPDA_Message_Box(
371 array(
372 'message_text' => __( 'Nothing to save', 'wp-data-access' ),
373 )
374 );
375 $msg->box();
376 } else {
377 $msg = new WPDA_Message_Box(
378 array(
379 'message_text' => __( 'Succesfully saved changes to database', 'wp-data-access' ),
380 )
381 );
382 $msg->box();
383 }
384 }
385
386 $this->model->query();
387 $structure_messages = $this->model->validate();
388 foreach ( $structure_messages as $messages ) {
389 if ( 'ERR' === $messages[0] ) {
390 $msg = new WPDA_Message_Box(
391 array(
392 'message_text' => $messages[1],
393 'message_type' => 'error',
394 'message_is_dismissible' => false,
395 )
396 );
397 $msg->box();
398 } else {
399 $msg = new WPDA_Message_Box(
400 array(
401 'message_text' => $messages[1],
402 )
403 );
404 $msg->box();
405 }
406 }
407 $this->wpda_table_design = $this->model->get_table_design();
408 $this->design_mode = $this->wpda_table_design->design_mode;
409
410 $this->action2 = 'edit'; // Editing mode.
411 } else {
412 $this->action2 = 'new'; // Design new table from scratch.
413 }
414
415 // Remove backticks from schema and table name to prevent sql injection
416 $this->wpda_schema_name = str_replace( '`', '', (string) $this->wpda_schema_name );
417 $this->wpda_table_name = str_replace( '`', '', (string) $this->wpda_table_name );
418
419 if ( null !== $this->wpda_table_name && null !== $this->wpda_schema_name ) {
420 // Check if table name already exists in database.
421 if ( $this->wpda_schema_name === $wpdb->dbname ) {
422 $wp_tables = array_flip( $wpdb->tables( 'all', true ) );
423 if ( isset( $wp_tables[ $this->wpda_table_name ] ) ) {
424 $this->is_wp_table = true;
425 $this->table_exists = true;
426 } else {
427 $this->does_table_exist();
428 }
429 } else {
430 $this->does_table_exist();
431 }
432
433 // Get design structure for real database table.
434 $this->get_table_structure();
435
436 if (
437 'create_table' === $this->action2_posted ||
438 'show_create_table_script' === $this->action2_posted
439 ) {
440 // Perform create table (and indexes) script.
441 $this->create_table();
442 $this->does_table_exist();
443 $this->get_table_structure();
444 } elseif ( 'alter_table' === $this->action2_posted ) {
445 // Generate alter table script and perform script.
446 $this->do_alter_table();
447 $this->get_table_structure();
448 } elseif ( 'show_alter_table_script' === $this->action2_posted ) {
449 // Generate alter table script and show result in overlay.
450 $this->alter_table();
451 } elseif ( 'create_table_index' === $this->action2_posted ) {
452 // Perform create index(es) script.
453 $this->create_index();
454 $this->get_table_structure();
455 } elseif ( 'drop_table' === $this->action2_posted ) {
456 // Perform drop table script.
457 $this->drop_table();
458 $this->does_table_exist();
459 } elseif ( 'drop_table_index' === $this->action2_posted ) {
460 // Perform drop table script.
461 $this->drop_indexes();
462 $this->get_table_structure();
463 }
464
465 if ( $this->table_exists ) {
466 // Get database table structure.
467 $wpda_list_columns = WPDA_List_Columns_Cache::get_list_columns( $this->wpda_schema_name, $this->wpda_table_name );
468 $table_columns = $wpda_list_columns->get_table_columns();
469
470 // Convert indexed array to named array to improve access.
471 foreach ( $table_columns as $table_column ) {
472 $this->table_columns[ $table_column['column_name'] ] = $table_column;
473 }
474 }
475 }
476
477 $this->databases = WPDA_Dictionary_Lists::get_db_schemas();
478
479 if ( ! isset( $_REQUEST['_wpnonce'] ) ) {
480 wp_die( esc_attr__( 'Not authorized', 'wp-data-access' ) );
481 } else {
482 // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
483 if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], "wpda-design-table-form-{$this->wpda_table_name}" )) { // direct form access
484 if (
485 '' === $this->wpda_table_name &&
486 ! wp_verify_nonce( $_REQUEST['_wpnonce'], "wpda-design-table-form-" . WPDA_Design_Table_Model::get_base_table_name() )
487 ) { // insert new table
488 if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], "wpda-alter-{$this->wpda_table_name}" ) ) { // access from alter table button
489 $schema_name = isset( $_POST['wpda_schema_name'] ) ? $_POST['wpda_schema_name'] : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
490 $table_name = isset( $_POST['wpda_table_name'] ) ? $_POST['wpda_table_name'] : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
491 $dsg_tbl = 'wpda-query-' . WPDA_Design_Table_Model::get_base_table_name() . '-' . $schema_name . '-' . $table_name;
492 if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], $dsg_tbl ) ) { // access from list table
493 wp_die( esc_attr__( 'Not authorized', 'wp-data-access' ) );
494 }
495 }
496 }
497 }
498 // phpcs:enable WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
499 }
500 }
501
502 public function prepare_form() {}
503
504 /**
505 * Get table and index structure from design
506 *
507 * @since 2.0.14
508 */
509 private function get_table_structure() {
510 if ( $this->table_exists ) {
511 $get_table_structure = new WPDA_Reverse_Engineering( $this->wpda_table_name, $this->wpda_schema_name );
512 $real_structure = $get_table_structure->get_designer_format( $this->design_mode );
513 $this->real_table = $real_structure['table'];
514 $this->real_indexes = $real_structure['indexes'];
515 }
516 }
517
518 /**
519 * Check if table exists in our database
520 *
521 * @since 2.0.14
522 */
523 private function does_table_exist() {
524 $wpda_dictionary_exists = new WPDA_Dictionary_Exist( $this->wpda_schema_name, $this->wpda_table_name );
525 $this->table_exists = $wpda_dictionary_exists->plain_table_exists();
526 if ( ! $this->table_exists ) {
527 $this->real_table = null;
528 $this->real_indexes = null;
529 }
530 }
531
532 /**
533 * Perform alter table
534 *
535 * Call $this->alter_table() to generate alter table script and process result taken
536 * from $this->create_table_statement.
537 *
538 * @see WPDA_Design_Table_Form::alter_table()
539 * @see WPDA_Design_Table_Form::create_table_statement
540 *
541 * @since 2.0.14
542 */
543 protected function do_alter_table() {
544 $this->alter_table();
545
546 $wpdadb = WPDADB::get_db_connection( $this->wpda_schema_name );
547 if ( null === $wpdadb ) {
548 /* translators: %s = remote database name */
549 wp_die( esc_attr( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), $this->wpda_schema_name ) ) );
550 }
551
552 $suppress = $wpdadb->suppress_errors( true );
553 $create_table_statement = str_replace( self::NEW_LINE, "\n", $this->create_table_statement );
554 if ( '' !== $create_table_statement ) {
555 // Process alter table script (store in $create_table_statement)
556 $sql_end = strpos( $create_table_statement, ";\n" );
557 while ( false !== $sql_end ) {
558 $sql = rtrim( substr( $create_table_statement, 0, $sql_end ) );
559 $create_table_statement = substr( $create_table_statement, strpos( $create_table_statement, $sql ) + strlen( $sql ) + 1 );
560 if ( ! $wpdadb->query( $sql ) ) {
561 $this->wpdb_error = $wpdadb->last_error;
562 $this->alter_table_succeeded = false;
563
564 return;
565 }
566 $sql_end = strpos( $create_table_statement, ";\n" );
567 }
568 }
569 $wpdadb->suppress_errors( $suppress );
570 }
571
572 /**
573 * Generate alter table script
574 *
575 * Alter table script is written to $this->create_table_statement.
576 *
577 * @see WPDA_Design_Table_Form::create_table_statement
578 *
579 * @since 2.0.14
580 */
581 protected function alter_table() {
582 $create_keys_design = array();
583 $create_keys_real = array();
584
585 // Find deleted and changed indexes
586 foreach ( $this->real_indexes as $real_index ) {
587 $drop_real_index = false;
588 $design_index_found = false;
589 foreach ( $this->wpda_table_design->indexes as $design_index ) {
590 if ( $real_index['index_name'] === $design_index->index_name ) {
591 $design_index_found = true;
592 if (
593 $real_index['unique'] != $design_index->unique ||
594 $real_index['column_names'] != $design_index->column_names
595 ) {
596 $drop_real_index = true;
597 break;
598 }
599 break;
600 }
601 }
602 if ( $drop_real_index || ! $design_index_found ) {
603 $this->create_table_statement .=
604 'DROP INDEX `' . str_replace( '`', '', $real_index['index_name'] ) . "` ON `{$this->wpda_table_name}`;" . self::NEW_LINE;
605 }
606 }
607 if ( '' !== $this->create_table_statement ) {
608 $this->create_table_statement .= self::NEW_LINE;
609 }
610
611 // Find new and changed columns
612 foreach ( $this->wpda_table_design->table as $design_column ) {
613 $design_column_found = false;
614 foreach ( $this->real_table as $real_column ) {
615 if ( $real_column->column_name === $design_column->column_name ) {
616 if ( $real_column != $design_column ) {
617 // Modify column
618 $this->alter_table_column( $design_column, 'MODIFY' );
619 }
620 $design_column_found = true;
621 break;
622 }
623 }
624
625 if ( ! $design_column_found ) {
626 // Add new column
627 $this->alter_table_column( $design_column, 'ADD' );
628 }
629
630 if ( 'Yes' === $design_column->key ) {
631 $create_keys_design[] = $design_column->column_name;
632 }
633 }
634
635 // Find deleted columns
636 foreach ( $this->real_table as $real_column ) {
637 $real_column_found = false;
638 foreach ( $this->wpda_table_design->table as $design_column ) {
639 if ( $real_column->column_name === $design_column->column_name ) {
640 $real_column_found = true;
641 break;
642 }
643 }
644
645 if ( ! $real_column_found ) {
646 // Drop column
647 // phpcs:ignore -- 8.1 proof
648 array_push(
649 $this->alter_table_statement,
650 'DROP COLUMN `' . str_replace( '`', '', $real_column->column_name ) . '`,' . self::NEW_LINE
651 );
652 }
653
654 if ( 'Yes' === $real_column->key ) {
655 $create_keys_real[] = $real_column->column_name;
656 }
657 }
658
659 if ( 0 < count( $this->alter_table_statement ) ) { // phpcs:ignore -- 8.1 proof
660 $this->create_table_statement .= "ALTER TABLE `{$this->wpda_table_name}` ";
661 foreach ( $this->alter_table_statement as $sql ) {
662 $this->create_table_statement .= $sql;
663 }
664 $this->create_table_statement =
665 substr( $this->create_table_statement, 0, strrpos( $this->create_table_statement, ',' ) ) .
666 ';' . self::NEW_LINE . self::NEW_LINE;
667
668 $array_difference_1 = array_diff( $create_keys_design, $create_keys_real ); // phpcs:ignore -- 8.1 proof
669 $array_difference_2 = array_diff( $create_keys_real, $create_keys_design ); // phpcs:ignore -- 8.1 proof
670 if ( 0 !== count( $array_difference_1 ) || 0 !== count( $array_difference_2 ) ) { // phpcs:ignore -- 8.1 proof
671 if ( 0 < count( $create_keys_real ) ) { // phpcs:ignore -- 8.1 proof
672 $this->create_table_statement =
673 "ALTER TABLE `{$this->wpda_table_name}` DROP PRIMARY KEY;" .
674 self::NEW_LINE . self::NEW_LINE . $this->create_table_statement;
675 }
676 if ( 0 < count( $create_keys_design ) ) { // phpcs:ignore -- 8.1 proof
677 $alter_table_statement =
678 "ALTER TABLE `{$this->wpda_table_name}` ADD PRIMARY KEY ";
679 foreach ( $create_keys_design as $key ) {
680 $alter_table_statement .= $key === reset( $create_keys_design ) ? '(' : ','; // phpcs:ignore -- 8.1 proof
681 $alter_table_statement .= '`' . str_replace( '`', '', $key ) . '`';
682 }
683 $alter_table_statement .= ');' . self::NEW_LINE . self::NEW_LINE;
684
685 $this->create_table_statement .= $alter_table_statement;
686 }
687 }
688 }
689
690 // Add new and changed indexes
691 foreach ( $this->wpda_table_design->indexes as $design_index ) {
692 $real_index_found = false;
693 $create_new_index = false;
694 foreach ( $this->real_indexes as $real_index ) {
695 if ( $real_index['index_name'] === $design_index->index_name ) {
696 $real_index_found = true;
697 if (
698 $real_index['unique'] != $design_index->unique ||
699 $real_index['column_names'] != $design_index->column_names
700 ) {
701 $create_new_index = true;
702 break;
703 }
704 break;
705 }
706 }
707 if ( ! $real_index_found || $create_new_index ) {
708 $unique = '';
709 if ( 'Yes' === $design_index->unique ) {
710 $unique = 'UNIQUE';
711 } elseif ( 'FULLTEXT' === $design_index->unique ) {
712 if ( 'on' === $this->fulltext_support ) {
713 $unique = 'FULLTEXT';
714 } else {
715 $unique = '';
716 }
717 }
718 $column_names_array = explode( ',', ( string ) $design_index->column_names ); // phpcs:ignore -- 8.1 proof
719 $column_names = '`' . implode( '`,`', $column_names_array ) . '`';
720 $this->create_table_statement .=
721 "CREATE $unique INDEX `" . str_replace( '`', '', $design_index->index_name ) . "` ON `{$this->wpda_table_name}` ($column_names);" .
722 self::NEW_LINE;
723 }
724 }
725 }
726
727 /**
728 * Alter column format
729 *
730 * @param object $design_column Column definition
731 * @param string $keyword ADD or MODIFY
732 *
733 * @since 2.0.14
734 */
735 private function alter_table_column( $design_column, $keyword ) {
736 $alter_table_statement = "$keyword COLUMN `" . str_replace( '`', '', $design_column->column_name ) . '` ';
737 $alter_table_statement .= $design_column->data_type;
738 if ( '' !== $design_column->max_length ) {
739 $alter_table_statement .= "($design_column->max_length)";
740 }
741 if ( 'enum' === $design_column->data_type || 'set' === $design_column->data_type ) {
742 $alter_table_statement .= '(' . $design_column->list . ')';
743 }
744 $alter_table_statement .= ' ';
745 $alter_table_statement .= 'Yes' === $design_column->mandatory ? 'NOT NULL' : 'NULL';
746 if ( '' !== $design_column->default ) {
747 $alter_table_statement .= " DEFAULT {$design_column->default}";
748 }
749 if ( '' !== $design_column->extra ) {
750 $alter_table_statement .= ' ';
751 $alter_table_statement .= $design_column->extra;
752 }
753 $alter_table_statement .= ',' . self::NEW_LINE;
754
755 array_push(
756 $this->alter_table_statement,
757 $alter_table_statement
758 ); // phpcs:ignore -- 8.1 proof
759 }
760
761 /**
762 * Drop database table
763 *
764 * Does not drop WordPress tables.
765 *
766 * @since 2.0.14
767 */
768 protected function drop_table() {
769 if ( $this->is_wp_table ) {
770 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
771 $msg = new WPDA_Message_Box(
772 array(
773 /* translators: %s = table name */
774 'message_text' => sprintf( __( 'Cannot drop WordPress table `%s`', 'wp-data-access' ), $this->wpda_table_name ),
775 'message_type' => 'error',
776 'message_is_dismissible' => false,
777 )
778 );
779 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
780 $msg->box();
781
782 return;
783 }
784
785 if ( $this->table_exists ) {
786 $wpdadb = WPDADB::get_db_connection( $this->wpda_schema_name );
787 if ( null === $wpdadb ) {
788 /* translators: %s = remote database name */
789 wp_die( esc_attr( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), $this->wpda_schema_name ) ) );
790 }
791
792 $suppress = $wpdadb->suppress_errors( true );
793 $drop_table_statement = "DROP TABLE `{$this->wpda_table_name}`";
794 if ( $wpdadb->query( $drop_table_statement ) ) {
795 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
796 $msg = new WPDA_Message_Box(
797 array(
798 /* translators: %s = table name */
799 'message_text' => sprintf( __( 'Table `%s` dropped', 'wp-data-access' ), $this->wpda_table_name ),
800 )
801 );
802 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
803 $msg->box();
804 } else {
805 $msg = new WPDA_Message_Box(
806 array(
807 'message_text' => __( 'DROP TABLE failed', 'wp-data-access' ),
808 'message_type' => 'error',
809 'message_is_dismissible' => false,
810 )
811 );
812 $msg->box();
813 $this->create_table_statement = $drop_table_statement;
814 }
815 $wpdadb->suppress_errors( $suppress );
816 }
817 }
818
819 /**
820 * Perform create table statement
821 *
822 * @since 1.1.0
823 */
824 protected function create_table() {
825 $is_valid = true;
826 $this->create_table_statement = "CREATE TABLE `{$this->wpda_table_name}`" . self::NEW_LINE;
827
828 $create_keys = array();
829 foreach ( $this->wpda_table_design->table as $row ) {
830 $this->create_table_statement .= $row === reset( $this->wpda_table_design->table ) ? '(' : ','; // phpcs:ignore -- 8.1 proof
831 $this->create_table_statement .= '`' . str_replace( '`', '', $row->column_name ) . '`';
832 $this->create_table_statement .= ' ';
833 $this->create_table_statement .= $row->data_type;
834 if ( '' !== $row->max_length ) {
835 $this->create_table_statement .= "($row->max_length)";
836 } else {
837 if (
838 'varchar' === $row->data_type ||
839 'char' === $row->data_type ||
840 'varbinary' === $row->data_type ||
841 'binary' === $row->data_type
842 ) {
843 $is_valid = false;
844 $msg = new WPDA_Message_Box(
845 array(
846 'message_text' => "Column {$row->column_name} of {$row->data_type} must have a max length",
847 'message_type' => 'error',
848 'message_is_dismissible' => false,
849 )
850 );
851 $msg->box();
852 }
853 }
854 if ( '' !== $row->type_attribute ) {
855 $this->create_table_statement .= " {$row->type_attribute} ";
856 }
857 if ( 'enum' === $row->data_type || 'set' === $row->data_type ) {
858 $this->create_table_statement .= '(' . $row->list . ')';
859 }
860 $this->create_table_statement .= ' ';
861 $this->create_table_statement .= 'Yes' === $row->mandatory ? 'NOT NULL' : 'NULL';
862 if ( '' !== $row->default ) {
863 $this->create_table_statement .= " DEFAULT {$row->default}";
864 }
865 if ( '' !== $row->extra ) {
866 $this->create_table_statement .= ' ';
867 $this->create_table_statement .= $row->extra;
868 }
869 if ( 'Yes' === $row->key ) {
870 $create_keys[] = $row->column_name;
871 }
872 $this->create_table_statement .= self::NEW_LINE;
873 }
874 if ( 0 < count( $create_keys ) ) { // phpcs:ignore -- 8.1 proof
875 $this->create_table_statement .= ',PRIMARY KEY ';
876 foreach ( $create_keys as $key ) {
877 $this->create_table_statement .= $key === reset( $create_keys ) ? '(' : ','; // phpcs:ignore -- 8.1 proof
878 $this->create_table_statement .= '`' . str_replace( '`', '', $key ) . '`';
879 }
880 $this->create_table_statement .= ')';
881 $this->create_table_statement .= self::NEW_LINE;
882 }
883 $this->create_table_statement .= ')';
884 if ( isset( $this->wpda_table_design->engine ) && '' !== $this->wpda_table_design->engine ) {
885 $this->create_table_statement .= ' ENGINE ' . $this->wpda_table_design->engine;
886 }
887 if ( isset( $this->wpda_table_design->collation ) && '' !== $this->wpda_table_design->collation ) {
888 $collation = explode( '_', $this->wpda_table_design->collation ); // phpcs:ignore -- 8.1 proof
889 $this->create_table_statement .= ' DEFAULT CHARACTER SET ' . $collation[0] . ' COLLATE=' . $this->wpda_table_design->collation;
890 }
891 $this->create_table_statement .= ';' . self::NEW_LINE . self::NEW_LINE;
892
893 if ( ! $is_valid ) {
894 return;
895 }
896
897 if ( 'show_create_table_script' === $this->action2_posted ) {
898 // Just show CREATE TABLE script!
899 // SQL script is available in $this->create_table_statement and can be shown on the page.
900 $this->create_index();
901 } else {
902 // Create table and indexes.
903 $wpdadb = WPDADB::get_db_connection( $this->wpda_schema_name );
904 if ( null === $wpdadb ) {
905 /* translators: %s = remote database name */
906 wp_die( esc_attr( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), $this->wpda_schema_name ) ) );
907 }
908
909 $suppress = $wpdadb->suppress_errors( true );
910
911 $this->create_table_succeeded = $wpdadb->query( str_replace( self::NEW_LINE, '', $this->create_table_statement ) );
912 if ( $this->create_table_succeeded ) {
913 $msg = new WPDA_Message_Box(
914 array(
915 'message_text' => __( 'Table created', 'wp-data-access' ),
916 )
917 );
918 $msg->box();
919 } else {
920 $msg = new WPDA_Message_Box(
921 array(
922 'message_text' => __( 'CREATE TABLE failed', 'wp-data-access' ),
923 'message_type' => 'error',
924 'message_is_dismissible' => false,
925 )
926 );
927 $msg->box();
928
929 $this->wpdb_error = $wpdadb->last_error;
930 }
931
932 $wpdadb->suppress_errors( $suppress );
933 }
934 }
935
936 /**
937 * Show Data Designer form
938 *
939 * @since 1.1.0
940 */
941 public function show() {
942 ?>
943 <script type='text/javascript'>
944 var row_num = 1;
945 var index_num = 1;
946
947 var table_updated = false;
948 var index_updated = false;
949
950 var no_cols_selected = 'no column(s) selected';
951
952 function disable_page() {
953 jQuery(".wpda_view").prop("readonly", true).prop("disabled", true).addClass("disabled");
954 disable_table();
955 disable_index();
956 disable_create_buttons();
957 jQuery('#reconcile_button').prop("readonly", true).prop("disabled", true).addClass("disabled");
958 jQuery('.design_mode').prop("readonly", true).prop("disabled", true).addClass("disabled");
959 jQuery('.column_names').prop("readonly", true).prop("disabled", true).addClass("disabled");
960 }
961
962 function disable_table() {
963 jQuery(".wpda_view_table").prop("readonly", true).prop("disabled", true).addClass("disabled");
964 }
965
966 function disable_index() {
967 jQuery(".wpda_view_index").prop("readonly", true).prop("disabled", true).addClass("disabled");
968 }
969
970 function disable_create_buttons() {
971 jQuery("#button_show_create_table").prop("readonly", true).prop("disabled", true).addClass("disabled");
972 jQuery("#button_show_alter_table").prop("readonly", true).prop("disabled", true).addClass("disabled");
973 jQuery("#button_create_table").prop("readonly", true).prop("disabled", true).addClass("disabled");
974 jQuery("#button_alter_table").prop("readonly", true).prop("disabled", true).addClass("disabled");
975 jQuery("#button_create_index").prop("readonly", true).prop("disabled", true).addClass("disabled");
976 jQuery("#button_recreate_index").prop("readonly", true).prop("disabled", true).addClass("disabled");
977 }
978
979 function rem_row(e) {
980 var curr_id = e.target.parentNode.parentNode.id;
981 if (confirm("Delete column?")) {
982 jQuery("#" + curr_id).remove();
983 updated_table();
984 }
985 }
986
987 function rem_index(e) {
988 var curr_id = e.target.parentNode.parentNode.id;
989 if (confirm("Delete index?")) {
990 jQuery("#" + curr_id).remove();
991 updated_indexes();
992 }
993 }
994
995 function updated_table() {
996 disable_create_buttons();
997 disable_index();
998 table_updated = true;
999 }
1000
1001 function updated_indexes() {
1002 disable_create_buttons();
1003 disable_table();
1004 index_updated = true;
1005 }
1006
1007 function check_basic_data_type(row_num) {
1008 check_numeric_items_off();
1009 switch (jQuery('#basic_data_type_' + row_num).val()) {
1010 case 'Text':
1011 jQuery('#data_type_' + row_num).val('text');
1012 jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table');
1013 break;
1014 case 'Integer':
1015 jQuery('#data_type_' + row_num).val('int');
1016 jQuery('#max_length_' + row_num).attr('class', 'wpda_digits_only wpda_view_table');
1017 break;
1018 case 'Real':
1019 jQuery('#data_type_' + row_num).val('float');
1020 jQuery('#max_length_' + row_num).attr('class', 'wpda_real wpda_view_table');
1021 break;
1022 case 'List':
1023 jQuery('#data_type_' + row_num).val('enum');
1024 jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table');
1025 break;
1026 case 'Boolean':
1027 jQuery('#data_type_' + row_num).val('tinyint');
1028 jQuery('#max_length_' + row_num).attr('class', 'wpda_digits_only wpda_view_table');
1029 break;
1030 case 'Datetime':
1031 jQuery('#data_type_' + row_num).val('datetime');
1032 jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table');
1033 break;
1034 case 'Binary':
1035 jQuery('#data_type_' + row_num).val('binary');
1036 jQuery('#max_length_' + row_num).attr('class', 'wpda_digits_only wpda_view_table');
1037 break;
1038 case 'Blob':
1039 jQuery('#data_type_' + row_num).val('blob');
1040 jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table');
1041 break;
1042 case '*ID':
1043 jQuery('#data_type_' + row_num).val('int');
1044 jQuery('#key_' + row_num).val('Yes');
1045 jQuery('#mandatory_' + row_num).val('Yes');
1046 jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_digits_only wpda_view_table');
1047 jQuery('#extra_' + row_num).val('AUTO_INCREMENT');
1048 jQuery('#default_' + row_num).val('');
1049 jQuery('#list_' + row_num).val('');
1050 break;
1051 case '*TimestampC':
1052 jQuery('#data_type_' + row_num).val('timestamp');
1053 jQuery('#key_' + row_num).val('No');
1054 jQuery('#mandatory_' + row_num).val('No');
1055 jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table');
1056 jQuery('#extra_' + row_num).val('');
1057 jQuery('#default_' + row_num).val('CURRENT_TIMESTAMP');
1058 jQuery('#list_' + row_num).val('');
1059 break;
1060 case '*TimestampU':
1061 jQuery('#data_type_' + row_num).val('timestamp');
1062 jQuery('#key_' + row_num).val('No');
1063 jQuery('#mandatory_' + row_num).val('No');
1064 jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table');
1065 jQuery('#extra_' + row_num).val('ON UPDATE CURRENT_TIMESTAMP');
1066 jQuery('#default_' + row_num).val('CURRENT_TIMESTAMP');
1067 jQuery('#list_' + row_num).val('');
1068 break;
1069 }
1070 check_numeric_items_on();
1071 }
1072
1073 function check_data_type(row_num) {
1074 check_numeric_items_off();
1075 switch (jQuery('#data_type_' + row_num + ' :selected').parent().attr('label')) {
1076 case 'Integer':
1077 jQuery('#basic_data_type_' + row_num).val('Integer');
1078 jQuery('#max_length_' + row_num).attr('class', 'wpda_digits_only wpda_view_table');
1079 break;
1080 case 'Text':
1081 jQuery('#basic_data_type_' + row_num).val('Text');
1082 switch (jQuery('#data_type_' + row_num + ' :selected').val()) {
1083 case 'char':
1084 case 'varchar':
1085 jQuery('#max_length_' + row_num).attr('class', 'wpda_digits_only wpda_view_table');
1086 break;
1087 default:
1088 jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table');
1089 }
1090 break;
1091 case 'List':
1092 jQuery('#basic_data_type_' + row_num).val('List');
1093 jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table');
1094 break;
1095 case 'Date and Time':
1096 jQuery('#basic_data_type_' + row_num).val('Datetime');
1097 jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table');
1098 break;
1099 case 'Real':
1100 jQuery('#basic_data_type_' + row_num).val('Real');
1101 jQuery('#max_length_' + row_num).attr('class', 'wpda_real wpda_view_table');
1102 break;
1103 case 'Binary':
1104 switch (jQuery('#data_type_' + row_num + ' :selected').val()) {
1105 case 'binary':
1106 case 'varbinary':
1107 jQuery('#basic_data_type_' + row_num).val('Binary');
1108 jQuery('#max_length_' + row_num).attr('class', 'wpda_digits_only wpda_view_table');
1109 break;
1110 default:
1111 jQuery('#basic_data_type_' + row_num).val('Blob');
1112 jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table');
1113 }
1114 break;
1115 case 'Boolean':
1116 jQuery('#basic_data_type_' + row_num).val('Boolean');
1117 jQuery('#max_length_' + row_num).attr('class', 'wpda_digits_only wpda_view_table');
1118 break;
1119 }
1120 check_numeric_items_on();
1121 }
1122
1123 function check_numeric_items_off() {
1124 jQuery('.wpda_digits_only').off('keyup paste');
1125 jQuery('.wpda_real').off('keyup paste');
1126 jQuery('.wpda_nodataentryallowed').off('keyup paste');
1127 }
1128
1129 function check_numeric_items_on() {
1130 jQuery('.wpda_digits_only').on('keyup paste', function () {
1131 this.value = this.value.replace(/[^\d]/g, '');
1132 });
1133 jQuery('.wpda_real').on('keyup paste', function () {
1134 this.value = this.value.replace(/[^0-9,]/g, '');
1135 });
1136 jQuery('.wpda_nodataentryallowed').on('keyup paste', function () {
1137 this.value = '';
1138 });
1139 }
1140
1141 function pre_submit() {
1142 if (jQuery('#wpda_table_name').val() === '') {
1143 alert('<?php echo esc_attr__( 'Table name cannot be empty', 'wp-data-access' ); ?>');
1144 return false;
1145 }
1146 if ('<?php echo esc_attr( $this->action2 ); ?>' === 'new') {
1147 if (wpda_db_table_name[jQuery('#wpda_table_name').val()]) {
1148 alert('<?php echo esc_attr__( 'Table name already used for another table design', 'wp-data-access' ); ?>');
1149 return false;
1150 }
1151 }
1152 var all_columns_entered = true;
1153 jQuery("input[name='column_name[]']").each(function () {
1154 if (jQuery(this).val() === '') {
1155 alert('<?php echo esc_attr__( 'Column names cannot be empty', 'wp-data-access' ); ?>');
1156 all_columns_entered = false;
1157 }
1158 });
1159 if (!all_columns_entered) {
1160 return false;
1161 }
1162 // Enable all listboxes that have been disable to add arguments to request.
1163 jQuery("select[id^='key']").each(function () {
1164 jQuery(this).attr('disabled', false);
1165 });
1166 jQuery("select[id^='mandatory']").each(function () {
1167 jQuery(this).attr('disabled', false);
1168 });
1169 return true;
1170 }
1171
1172 function add_row(design_mode, init = false, column_name = '', basic_data_type = '', data_type = '', type_attribute = '', key = '', mandatory = '', max_length = '', extra = '', default_value = '', list = '', row_action = '') {
1173 var row_class = '';
1174 switch (row_action) {
1175 case 'u':
1176 row_class = 'wpda_column_updated';
1177 break;
1178 case 'i':
1179 row_class = 'wpda_column_inserted';
1180 break;
1181 case 'd':
1182 row_class = 'wpda_column_deleted';
1183 }
1184 var basic_columns = '<td>' +
1185 '<select name="basic_data_type[]" id="basic_data_type_' + row_num + '" onchange="check_basic_data_type(' + row_num + ')" class="wpda_view_table">' +
1186 ' <option value="Text" ' + (basic_data_type === 'Text' ? 'selected' : '') + '>Text</option>' +
1187 ' <option value="Integer" ' + (basic_data_type === 'Integer' ? 'selected' : '') + '>Integer</option>' +
1188 ' <option value="Real" ' + (basic_data_type === 'Real' ? 'selected' : '') + '>Real</option>' +
1189 ' <option value="List" ' + (basic_data_type === 'List' ? 'selected' : '') + '}>List</option>' +
1190 ' <option value="Boolean" ' + (basic_data_type === 'Boolean' ? 'selected' : '') + '>Boolean</option>' +
1191 ' <option value="Datetime" ' + (basic_data_type === 'Datetime' ? 'selected' : '') + '>Datetime</option>' +
1192 ' <option value="Binary" ' + (basic_data_type === 'Binary' ? 'selected' : '') + '>Binary</option>' +
1193 ' <option value="Blob" ' + (basic_data_type === 'Blob' ? 'selected' : '') + '>Blob</option>' +
1194 ' <option value="*ID" ' + (basic_data_type === '*ID' ? 'selected' : '') + '>* Numeric ID (auto increment)</option>' +
1195 ' <option value="*TimestampC" ' + (basic_data_type === '*TimestampC' ? 'selected' : '') + '>* Timestamp (date created)</option>' +
1196 ' <option value="*TimestampU" ' + (basic_data_type === '*TimestampU' ? 'selected' : '') + '>* Timestamp (last updated)</option>' +
1197 '</select>' +
1198 '<input type="hidden" name="data_type[]" id="data_type_' + row_num + '" value="' + data_type + '">' +
1199 '<input type="hidden" name="type_attribute[]" id="type_attribute_' + row_num + '" value="' + type_attribute + '">' +
1200 '</td>';
1201 var advanced_columns = '<td>' +
1202 '<select name="data_type[]" id="data_type_' + row_num + '" onchange="check_data_type(' + row_num + ')" class="wpda_view_table">' +
1203 ' <option value="" ' + (data_type === '' ? 'selected' : '') + '></option>' +
1204 ' <optgroup label="Integer">' +
1205 ' <option value="bit" ' + (data_type === 'bit' ? 'selected' : '') + '>bit</option>' +
1206 ' <option value="tinyint" ' + (data_type === 'tinyint' ? 'selected' : '') + '>tinyint</option>' +
1207 ' <option value="smallint" ' + (data_type === 'smallint' ? 'selected' : '') + '>smallint</option>' +
1208 ' <option value="mediumint" ' + (data_type === 'mediumint' ? 'selected' : '') + '>mediumint</option>' +
1209 ' <option value="int" ' + (data_type === 'int' ? 'selected' : '') + '>int</option>' +
1210 ' <option value="bigint" ' + (data_type === 'bigint' ? 'selected' : '') + '>bigint</option>' +
1211 ' </optgroup>' +
1212 ' <optgroup label="Text">' +
1213 ' <option value="char" ' + (data_type === 'char' ? 'selected' : '') + '>char</option>' +
1214 ' <option value="varchar" ' + (data_type === 'varchar' ? 'selected' : '') + '>varchar</option>' +
1215 ' <option disabled="disabled">-</option>' +
1216 ' <option value="tinytext" ' + (data_type === 'tinytext' ? 'selected' : '') + '>tinytext</option>' +
1217 ' <option value="text" ' + (data_type === 'text' ? 'selected' : '') + '>text</option>' +
1218 ' <option value="mediumtext" ' + (data_type === 'mediumtext' ? 'selected' : '') + '>mediumtext</option>' +
1219 ' <option value="longtext" ' + (data_type === 'longtext' ? 'selected' : '') + '>longtext</option>' +
1220 ' </optgroup>' +
1221 ' <optgroup label="List">' +
1222 ' <option value="enum" ' + (data_type === 'enum' ? 'selected' : '') + '>enum</option>' +
1223 ' <option value="set" ' + (data_type === 'set' ? 'selected' : '') + '>set</option>' +
1224 ' </optgroup>' +
1225 ' <optgroup label="Date and Time">' +
1226 ' <option value="date" ' + (data_type === 'date' ? 'selected' : '') + '>date</option>' +
1227 ' <option value="datetime" ' + (data_type === 'datetime' ? 'selected' : '') + '>datetime</option>' +
1228 ' <option value="timestamp" ' + (data_type === 'timestamp' ? 'selected' : '') + '>timestamp</option>' +
1229 ' <option value="time" ' + (data_type === 'time' ? 'selected' : '') + '>time</option>' +
1230 ' <option value="year" ' + (data_type === 'year' ? 'selected' : '') + '>year</option>' +
1231 ' </optgroup>' +
1232 ' <optgroup label="Real">' +
1233 ' <option value="decimal" ' + (data_type === 'decimal' ? 'selected' : '') + '>decimal</option>' +
1234 ' <option value="double" ' + (data_type === 'double' ? 'selected' : '') + '>double</option>' +
1235 ' <option value="float" ' + (data_type === 'float' ? 'selected' : '') + '>float</option>' +
1236 ' </optgroup>' +
1237 ' <optgroup label="Binary">' +
1238 ' <option value="binary" ' + (data_type === 'binary' ? 'selected' : '') + '>binary</option>' +
1239 ' <option value="varbinary" ' + (data_type === 'varbinary' ? 'selected' : '') + '>varbinary</option>' +
1240 ' <option disabled="disabled">-</option>' +
1241 ' <option value="tinyblob" ' + (data_type === 'tinyblob' ? 'selected' : '') + '>tinyblob</option>' +
1242 ' <option value="blob" ' + (data_type === 'blob' ? 'selected' : '') + '>blob</option>' +
1243 ' <option value="mediumblob" ' + (data_type === 'mediumblob' ? 'selected' : '') + '>mediumblob</option>' +
1244 ' <option value="longblob" ' + (data_type === 'longblob' ? 'selected' : '') + '>longblob</option>' +
1245 ' </optgroup>' +
1246 ' <optgroup label="Boolean">' +
1247 ' <option value="boolean" ' + (data_type === 'boolean' ? 'selected' : '') + '>boolean</option>' +
1248 ' </optgroup>' +
1249 '</select>' +
1250 '<input type="hidden" name="basic_data_type[]" id="basic_data_type_' + row_num + '" value="' + basic_data_type + '">' +
1251 '</td>' +
1252 '<td>' +
1253 ' <select name="type_attribute[]" id="type_attribute_' + row_num + '" class="wpda_view_table">' +
1254 ' <option value=""></option>' +
1255 ' <option value="unsigned" ' + (type_attribute === 'unsigned' ? 'selected' : '') + '>unsigned</option>' +
1256 ' <option value="unsigned zerofill" ' + (type_attribute === 'unsigned zerofill' ? 'selected' : '') + '>unsigned zerofill</option>' +
1257 ' </select>' +
1258 '</td>';
1259 var new_row = '<tr id="row_num_' + row_num + '" class="' + row_class + '">' +
1260 '<td class="wpda-table-structure-first-column-move">' +
1261 ' <span class="dashicons dashicons-move grabbable" style="float:left;"></span>' +
1262 '</td>' +
1263 '<td>' +
1264 ' <input type="text" name="column_name[]" id="column_name_' + row_num + '"' +
1265 ' maxlength="64" value="' + column_name + '" class="wpda_mysql_names wpda_view_table">' +
1266 '</td>' +
1267 (design_mode === 'basic' ? basic_columns : advanced_columns) +
1268 '<td>' +
1269 ' <select name="key[]" id="key_' + row_num + '" class="wpda_view_table">' +
1270 ' <option value="No" ' + (key === 'No' ? 'selected' : '') + '>No</option>' +
1271 ' <option value="Yes" ' + (key === 'Yes' ? 'selected' : '') + '>Yes</option>' +
1272 ' </select>' +
1273 '</td>' +
1274 '<td>' +
1275 ' <select name="mandatory[]" id="mandatory_' + row_num + '" class="wpda_view_table">' +
1276 ' <option value="No"' + (mandatory === 'No' ? 'selected' : '') + '>No</option>' +
1277 ' <option value="Yes"' + (mandatory === 'Yes' ? 'selected' : '') + '>Yes</option>' +
1278 ' </select>' +
1279 '</td>' +
1280 '<td>' +
1281 ' <input type="text" name="max_length[]" id="max_length_' + row_num + '"' +
1282 ' value="' + (max_length === '0' ? '' : max_length) + '" class="' + (basic_data_type === 'Real' ? 'wpda_float' : 'wpda_digits_only') + ' wpda_view_table">' +
1283 '</td>' +
1284 '<td>' +
1285 ' <input type="text" name="extra[]" id="extra_' + row_num + '" value="' + extra + '" class="wpda_view_table">' +
1286 '</td>' +
1287 '<td>' +
1288 ' <input type="text" name="default[]" id="default_' + row_num + '" value="' + default_value + '" class="wpda_view_table">' +
1289 '</td>' +
1290 '<td>' +
1291 ' <input type="text" name="list[]" id="list_' + row_num + '" value="' + list + '" class="wpda_view_table">' +
1292 ' <input type="hidden" name="row_action[]" value="' + row_action + '">' +
1293 '</td>' +
1294 '<td class="wpda-table-structure-last-column">' +
1295 ' <a href="javascript:void(0)" onclick="rem_row(event)" class="dashicons dashicons-trash wpda_view_table wpda_tooltip" title="Remove column from table design"></a>' +
1296 '</td>' +
1297 '</tr>';
1298 if (jQuery("#wpda_table_structure tr").length === 0) {
1299 jQuery("#wpda_table_structure").append(new_row);
1300 } else {
1301 jQuery("#wpda_table_structure tr:last").after(new_row);
1302 }
1303 <?php if ( 'basic' === $this->design_mode ) { ?>
1304 check_basic_data_type(row_num);
1305 <?php } else { ?>
1306 check_data_type(row_num)
1307 <?php } ?>
1308 row_num++;
1309 jQuery('.wpda_mysql_names').off('keyup paste');
1310 jQuery('.wpda_mysql_names').on('keyup paste', function () {
1311 this.value = this.value.replace(/[^\w\_]/g, '');
1312 });
1313 check_numeric_items_off();
1314 check_numeric_items_on();
1315 jQuery(".wpda_view_table").off('change paste keyup', updated_table);
1316 jQuery(".wpda_view_table").on('change paste keyup', updated_table);
1317 jQuery("a.wpda_view_table").off();
1318 jQuery("a.wpda_view_table").on('click', updated_table);
1319 jQuery(".wpda_column_deleted").find("input,select").attr('disabled', true);
1320 if (!init) {
1321 updated_table();
1322 }
1323 }
1324
1325 function select_available(e) {
1326
1327 var option = jQuery("#columns_available option:selected");
1328 var add_to = jQuery("#columns_selected");
1329
1330 option.remove();
1331 new_option = add_to.append(option);
1332
1333 if (jQuery("#columns_selected option[value='']").length > 0) {
1334 // Remove ALL from selected list.
1335 jQuery("#columns_selected option[value='']").remove();
1336 }
1337
1338 jQuery('select#columns_selected option').prop("selected", false);
1339
1340 }
1341
1342 function select_selected(e) {
1343
1344 var option = jQuery("#columns_selected option:selected");
1345 if (option[0].value === '') {
1346 // Cannot remove ALL.
1347 return;
1348 }
1349
1350 var add_to = jQuery("#columns_available");
1351
1352 option.remove();
1353 add_to.append(option);
1354
1355 if (jQuery('select#columns_selected option').length === 0) {
1356 jQuery("#columns_selected").append(jQuery('<option></option>').attr('value', '').text(no_cols_selected));
1357 }
1358
1359 jQuery('select#columns_available option').prop("selected", false);
1360 }
1361
1362 function show_index_dialog(e) {
1363 if ('<?php echo esc_attr( $this->action ); ?>' === 'view') {
1364 return;
1365 }
1366
1367 var item_id = e.target.id;
1368 var index_row_num = item_id.substr(item_id.lastIndexOf("_") + 1);
1369
1370 if (jQuery('#add_columns_' + index_row_num).hasClass('disabled') === true) {
1371 return;
1372 }
1373
1374 var columns_available = jQuery(
1375 '<select id="columns_available" name="columns_available[]" multiple size="8" style="width:200px" onchange="select_available()">' +
1376 '</select>'
1377 );
1378 jQuery("input[name='column_name[]']").each(function () {
1379 columns_available.append(jQuery('<option></option>').attr('value', jQuery(this).val()).text(jQuery(this).val()));
1380 });
1381
1382 var columns_selected = jQuery(
1383 '<select id="columns_selected" name="columns_selected[]" multiple size="8" style="width:200px" onchange="select_selected()">' +
1384 '<option value="">' + no_cols_selected + '</option>' +
1385 '</select>'
1386 );
1387
1388 var dialog_table = jQuery('<table style="width:410px"></table>');
1389 var dialog_table_row = dialog_table.append(jQuery('<tr></tr>'));
1390 dialog_table_row.append(jQuery('<td width="50%"></td>').append(columns_available));
1391 dialog_table_row.append(jQuery('<td width="50%"></td>').append(columns_selected));
1392
1393 // var dialog_table_row_available = dialog_table.append(jQuery('<tr></tr>').append(jQuery('<td width="50%"></td>')));
1394 // dialog_table_row_available.append(columns_available);
1395 //
1396 // var dialog_table_row_selected = dialog_table.append(jQuery('<tr></tr>').append(jQuery('<td width="50%"></td>')));
1397 // dialog_table_row_selected.append(columns_selected);
1398
1399 var dialog_text = jQuery('<div style="width:410px"></div>');
1400 var dialog = jQuery('<div></div>');
1401
1402 dialog.append(dialog_text);
1403 dialog.append(dialog_table);
1404
1405 jQuery(dialog).dialog(
1406 {
1407 dialogClass: 'wp-dialog no-close',
1408 title: 'Add column(s) to index',
1409 modal: true,
1410 autoOpen: true,
1411 closeOnEscape: false,
1412 resizable: false,
1413 width: 'auto',
1414 buttons: {
1415 "Close": function () {
1416 var selected_columns = '';
1417 jQuery("#columns_selected option").each(
1418 function () {
1419 selected_columns += jQuery(this).val() + ',';
1420 }
1421 );
1422
1423 if (selected_columns !== '') {
1424 selected_columns = selected_columns.slice(0, -1);
1425 }
1426 jQuery('#column_names_' + index_row_num).val(selected_columns);
1427
1428 jQuery(this).dialog('destroy').remove();
1429 updated_indexes();
1430 },
1431 "Cancel": function () {
1432 jQuery(this).dialog('destroy').remove();
1433 }
1434 }
1435 }
1436 );
1437
1438 wpda_add_icons_to_dialog_buttons();
1439 jQuery(".ui-button-icon-only").hide();
1440 }
1441
1442 function add_index(init = false, index_name = '', unique = '', column_names = '', row_action = '') {
1443 var row_class = '';
1444 switch (row_action) {
1445 case 'u':
1446 row_class = 'wpda_column_updated';
1447 break;
1448 case 'i':
1449 row_class = 'wpda_column_inserted';
1450 break;
1451 case 'd':
1452 row_class = 'wpda_column_deleted';
1453 }
1454 <?php if ( 'on' === $this->fulltext_support ) { ?>
1455 var fulltext_support = '<option value="FULLTEXT"' + (unique === 'FULLTEXT' ? 'selected' : '') + '>Full Text</option>';
1456 <?php } else { ?>
1457 var fulltext_support = '';
1458 <?php } ?>
1459 var new_index = '<tr id="idx_row_num_' + index_num + '" class="' + row_class + '">' +
1460 '<td style="padding-left:10px;">' +
1461 ' <input type="text" name="index_name[]" id="index_name_' + index_num + '"' +
1462 ' value="' + index_name + '" class="wpda_view_index wpda_mysql_names">' +
1463 '</td>' +
1464 '<td>' +
1465 ' <select name="unique[]" id="unique_' + index_num + '" class="wpda_view_index">' +
1466 ' <option value="No"' + (unique === 'No' ? 'selected' : '') + '>Non unique</option>' +
1467 ' <option value="Yes"' + (unique === 'Yes' ? 'selected' : '') + '>Unique</option>' +
1468 fulltext_support +
1469 ' </select>' +
1470 '</td>' +
1471 '<td>' +
1472 ' <input type="text" name="column_names[]" id="column_names_' + index_num + '" class="column_names"' +
1473 ' value="' + column_names + '" onclick="show_index_dialog(event)" readonly>' +
1474 '</td>' +
1475 '<td>' +
1476 '</td>' +
1477 ' <input type="button" name="add_columns[]" id="add_columns_' + index_num + '"' +
1478 ' value="Add column(s)" onclick="show_index_dialog(event)" class="wpda_view_index">' +
1479 ' <input type="hidden" name="row_action[]" value="' + row_action + '">' +
1480 '</td>' +
1481 '<td class="wpda-table-structure-last-column" style="width:20px;">' +
1482 ' <a href="javascript:void(0)" onclick="rem_index(event)" class="dashicons dashicons-trash wpda_view_index wpda_tooltip" title="Remove index from table design"></a>' +
1483 ' </td>' +
1484 '</tr>';
1485 if (jQuery("#wpda_index_structure tr").length === 0) {
1486 jQuery("#wpda_index_structure").append(new_index);
1487 } else {
1488 jQuery("#wpda_index_structure tr:last").after(new_index);
1489 }
1490 index_num++;
1491 jQuery('.wpda_mysql_names').off('keyup paste');
1492 jQuery('.wpda_mysql_names').on('keyup paste', function () {
1493 this.value = this.value.replace(/[^\w\_]/g, '');
1494 });
1495 jQuery('#submit_indexes').attr('disabled', false);
1496 jQuery(".wpda_view_index").off('change paste keyup', updated_indexes);
1497 jQuery(".wpda_view_index").on('change paste keyup', updated_indexes);
1498 jQuery("a.wpda_view_index").off();
1499 jQuery("a.wpda_view_index").on('click', updated_indexes);
1500 jQuery(".wpda_column_deleted").find("input,select").attr('disabled', true);
1501 if (!init) {
1502 updated_indexes();
1503 }
1504 }
1505
1506 function switch_mode(e) {
1507 if ('new' === '<?php echo esc_attr( $this->action2 ); ?>' && !table_updated && !index_updated) {
1508 if (e.target.value !== '<?php echo esc_attr( $this->design_mode ); ?>') {
1509 if (confirm('Switch to ' + e.target.value + ' mode?')) {
1510 jQuery('#switch_mode_form').submit();
1511 } else {
1512 return false;
1513 }
1514 }
1515 } else if ('edit' === '<?php echo esc_attr( $this->action2 ); ?>' || table_updated || index_updated) {
1516 if (e.target.value !== '<?php echo esc_attr( $this->design_mode ); ?>') {
1517 if (confirm('Switch to ' + e.target.value + ' mode?')) {
1518 jQuery('#design_table_form').submit();
1519 } else {
1520 return false;
1521 }
1522 }
1523 }
1524 }
1525
1526 function pre_submit_re() {
1527 if (wpda_db_table_name[jQuery('select[name="wpda_table_name_re"]').val()]) {
1528 alert('<?php echo esc_attr__( 'Table name already used for another table design', 'wp-data-access' ); ?>');
1529 return false;
1530 }
1531 jQuery('#design_mode_re').val(jQuery('input[name="design_mode"]:checked').val());
1532 return true;
1533 }
1534
1535 function get_tables() {
1536 schema_name = jQuery('#wpda_schema_name_re_list').val();
1537 var url = location.pathname + '?action=wpda_get_tables&hideviews=TRUE';
1538 var data = {
1539 wpdaschema_name: schema_name,
1540 wpda_wpnonce: '<?php echo esc_attr( wp_create_nonce( 'wpda-getdata-access-' . WPDA::get_current_user_login() ) ); ?>'
1541 };
1542 jQuery.post(
1543 url,
1544 data,
1545 function (data) {
1546 jQuery('#wpda_table_name_re_list').find('option').remove();
1547 var jsonData = JSON.parse(data);
1548 for (i = 0; i < jsonData.length; i++) {
1549 jQuery('#wpda_table_name_re_list').append(
1550 jQuery("<option></option>")
1551 .attr("value", jsonData[i]['table_name'])
1552 .text(jsonData[i]['table_name'])
1553 );
1554 }
1555 }
1556 );
1557 }
1558 </script>
1559 <div class="wrap">
1560 <h1>
1561 <a
1562 href="?page=<?php echo '' === $this->caller ? esc_attr( $this->page ) : esc_attr( \WP_Data_Access_Admin::PAGE_MAIN ); ?>"
1563 style="display: inline-block; vertical-align: unset;"
1564 class="dashicons dashicons-arrow-left-alt2 wpda_tooltip"
1565 title="<?php echo esc_attr__( 'List', 'wp-data-access' ); ?>"
1566 ></a>
1567 Data Designer
1568 </h1>
1569 <div style="display:none;">
1570 <form id="wpda_create_table_form"
1571 action="?page=<?php echo esc_attr( $this->page ); ?>"
1572 method="post">
1573 <input type="hidden" name="action" value="edit"/>
1574 <input type="hidden" name="action2" value="create_table"/>
1575 <input type="hidden" name="wpda_table_name"
1576 value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/>
1577 <input type="hidden" name="wpda_schema_name"
1578 value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/>
1579 <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/>
1580 <?php $this->create_nonce(); ?>
1581 </form>
1582 <form id="wpda_alter_table_form"
1583 action="?page=<?php echo esc_attr( $this->page ); ?>"
1584 method="post">
1585 <input type="hidden" name="action" value="edit"/>
1586 <input type="hidden" name="action2" value="alter_table"/>
1587 <input type="hidden" name="wpda_table_name"
1588 value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/>
1589 <input type="hidden" name="wpda_schema_name"
1590 value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/>
1591 <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/>
1592 <?php $this->create_nonce(); ?>
1593 </form>
1594 <form id="wpda_drop_table_form"
1595 action="?page=<?php echo esc_attr( $this->page ); ?>"
1596 method="post">
1597 <input type="hidden" name="action" value="edit"/>
1598 <input type="hidden" name="action2" value="drop_table"/>
1599 <input type="hidden" name="wpda_table_name"
1600 value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/>
1601 <input type="hidden" name="wpda_schema_name"
1602 value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/>
1603 <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/>
1604 <?php $this->create_nonce(); ?>
1605 </form>
1606 <form id="wpda_create_index_form"
1607 action="?page=<?php echo esc_attr( $this->page ); ?>"
1608 method="post" style="margin-left:1px;margin-right:1px;">
1609 <input type="hidden" name="action" value="edit"/>
1610 <input type="hidden" name="action2" value="create_table_index"/>
1611 <input type="hidden" name="wpda_table_name"
1612 value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/>
1613 <input type="hidden" name="wpda_schema_name"
1614 value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/>
1615 <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/>
1616 <?php $this->create_nonce(); ?>
1617 </form>
1618 <form id="wpda_drop_index_form"
1619 action="?page=<?php echo esc_attr( $this->page ); ?>"
1620 method="post" style="margin-left:1px;margin-right:1px;">
1621 <input type="hidden" name="action" value="edit"/>
1622 <input type="hidden" name="action2" value="drop_table_index"/>
1623 <input type="hidden" name="wpda_table_name"
1624 value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/>
1625 <input type="hidden" name="wpda_schema_name"
1626 value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/>
1627 <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/>
1628 <?php $this->create_nonce(); ?>
1629 </form>
1630 </div>
1631 <style>
1632 #overlay_show_create_table {
1633 height: 400px;
1634 width: 600px;
1635 position: fixed;
1636 display: none;
1637 top: 50%;
1638 left: 50%;
1639 transform: translate(-50%, -50%);
1640 -ms-transform: translate(-50%, -50%);
1641 right: 0;
1642 bottom: 0;
1643 background-color: #f9f9f9;
1644 opacity: .95;
1645 border: 1px solid #ccc;
1646 cursor: pointer;
1647 z-index: 1000;
1648 }
1649
1650 #overlay_show_create_table_text {
1651 height: 360px;
1652 width: 400px;
1653 padding: 10px;
1654 position: relative;
1655 top: 50%;
1656 left: 220px;
1657 transform: translate(-50%, -50%);
1658 -ms-transform: translate(-50%, -50%);
1659 color: black;
1660 overflow-y: auto;
1661 background-color: white;
1662 border: 1px solid #ccc;
1663 }
1664 </style>
1665 <div id="overlay_show_create_table">
1666 <div id="overlay_show_create_table_text">
1667 <?php echo $this->create_table_statement . $this->create_index_statement; // phpcs:ignore WordPress.Security.EscapeOutput ?>
1668 </div>
1669 <div style="position: absolute; bottom: 0; right: 0; padding-right: 10px; padding-bottom: 10px;">
1670 <a id="button-copy-clipboard" href="javascript:void(0)" class="button button-secondary"
1671 style="text-align:center;width:150px;"
1672 data-clipboard-text="<?php echo str_replace( self::NEW_LINE, "\n", $this->create_table_statement ) . str_replace( self::NEW_LINE, "\n", $this->create_index_statement ); // phpcs:ignore WordPress.Security.EscapeOutput ?>">
1673 <i class="fas fa-clipboard wpda_icon_on_button"></i>
1674 <?php echo esc_attr__( 'Copy to clipboard', 'wp-data-access' ); ?>
1675 </a>
1676 <br/>
1677 <div style="height: 5px;"></div>
1678 <a href="javascript:void(0)" class="button button-primary"
1679 style="text-align:center;width:150px;"
1680 onclick="jQuery('#overlay_show_create_table').hide()">
1681 <i class="fas fa-times-circle wpda_icon_on_button"></i>
1682 <?php echo esc_attr__( 'Close', 'wp-data-access' ); ?>
1683 </a>
1684 </div>
1685 </div>
1686 <script type='text/javascript'>
1687 jQuery(function () {
1688 <?php if ( 'show_create_table_script' === $this->action2_posted || 'show_alter_table_script' === $this->action2_posted ) { ?>
1689 jQuery('#overlay_show_create_table').show();
1690 <?php } ?>
1691 var sql_to_clipboard = new ClipboardJS('#button-copy-clipboard');
1692 sql_to_clipboard.on('success', function (e) {
1693 jQuery.notify('<?php echo esc_attr__( 'SQL successfully copied to clipboard!', 'wp-data-access' ); ?>','info');
1694 });
1695 sql_to_clipboard.on('error', function (e) {
1696 jQuery.notify('<?php echo esc_attr__( 'Could not copy SQL to clipboard!', 'wp-data-access' ); ?>','error');
1697 });
1698 jQuery('#wpda_table_structure').sortable();
1699 jQuery( '.wpda_tooltip' ).tooltip();
1700 });
1701 </script>
1702 <?php
1703 if ( ! $this->wpda_table_design ) {
1704 // Allow loading table from database into designer (reverse engineering).
1705 $table_list = WPDA_Dictionary_Lists::get_tables( false );
1706 ?>
1707 <div id="wpda_reverse_engineering" style="display: none">
1708 <br/>
1709 <div class="wpda_reverse_engineering">
1710 <form id="wpda_reverse_engineering_form"
1711 action="?page=<?php echo esc_attr( $this->page ); ?>" method="post">
1712 <label><?php echo esc_attr__( 'Load table from database', 'wp-data-access' ); ?> </label>
1713 <select name="wpda_schema_name_re" id="wpda_schema_name_re_list" onchange="get_tables()">
1714 <?php
1715 global $wpdb;
1716 foreach ( $this->databases as $database ) {
1717 if ( null === $this->wpda_schema_name || '' === $this->wpda_schema_name ) {
1718 $dbs = WPDA::get_user_default_scheme();
1719 } else {
1720 $dbs = $this->wpda_schema_name;
1721 }
1722 $selected = $dbs === $database['schema_name'] ? 'selected' : '';
1723 if ( $wpdb->dbname === $database['schema_name'] ) {
1724 $database_printed = "WordPress database ({$database['schema_name']})";
1725 } else {
1726 $database_printed = $database['schema_name'];
1727 }
1728 echo "<option value='{$database['schema_name']}' $selected>{$database_printed}</option>"; // phpcs:ignore WordPress.Security.EscapeOutput
1729 }
1730 ?>
1731 </select>
1732 <select name="wpda_table_name_re" id="wpda_table_name_re_list">
1733 <?php
1734 foreach ( $table_list as $key => $value ) {
1735 echo '<option value="' . esc_attr( $value['table_name'] ) . '">' . esc_attr( $value['table_name'] ) . '</option>';
1736 }
1737 ?>
1738 </select>
1739 <input type="hidden" name="action" value="edit"/>
1740 <input type="hidden" name="action2" value="wpda_reverse_engineering"/>
1741 <input type="hidden" name="wpda_table_name" id="wpda_table_name_re"
1742 value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/>
1743 <input type="hidden" name="wpda_schema_name" id="wpda_schema_name_re"
1744 value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/>
1745 <input type="hidden" id="design_mode_re" name="design_mode_re"/>
1746 <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/>
1747 <input type="submit"
1748 class="button button-primary"
1749 value="Start Reverse Engineering"
1750 onclick="return pre_submit_re()"
1751 >
1752 <a href="javascript:void(0)" onclick="jQuery('#wpda_reverse_engineering').hide()"
1753 class="button"><?php echo esc_attr__( 'Dismiss', 'wp-data-access' ); ?></a>
1754 <?php $this->create_nonce(); ?>
1755 </form>
1756 </div>
1757 </div>
1758 <?php
1759
1760 }
1761 if ( $this->table_exists ) {
1762 // Add reconcile form.
1763 ?>
1764 <div style="display:none;">
1765 <form id="wpda_reconcile_form"
1766 action="?page=<?php echo esc_attr( $this->page ); ?>" method="post">
1767 <input type="hidden" name="action" value="edit"/>
1768 <input type="hidden" name="action2" value="wpda_reconcile"/>
1769 <input type="hidden" name="wpda_table_name_re"
1770 value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/>
1771 <input type="hidden" name="wpda_schema_name_re"
1772 value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/>
1773 <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/>
1774 <?php $this->create_nonce(); ?>
1775 </form>
1776 </div>
1777 <?php
1778 }
1779 ?>
1780 <div style="text-align:right">
1781 <a href="https://docs.legacy.wpdataaccess.com/docs/data-designer-getting-started/"
1782 target="_blank" style="text-decoration:none">
1783 > What is the difference between a table design and a database table?
1784 </a>
1785 </div>
1786 <div>
1787 <div style="display:none;">
1788 <form id="show_create_table_form" action="?page=<?php echo esc_attr( $this->page ); ?>"
1789 method="post">
1790 <input type="hidden" name="action" value="edit"/>
1791 <input type="hidden" name="action2" value="show_create_table_script"/>
1792 <input type="hidden" name="wpda_table_name"
1793 value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/>
1794 <input type="hidden" name="wpda_schema_name"
1795 value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/>
1796 <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/>
1797 <?php $this->create_nonce(); ?>
1798 </form>
1799 <form id="show_alter_table_form" action="?page=<?php echo esc_attr( $this->page ); ?>"
1800 method="post">
1801 <input type="hidden" name="action" value="edit"/>
1802 <input type="hidden" name="action2" value="show_alter_table_script"/>
1803 <input type="hidden" name="wpda_table_name"
1804 value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/>
1805 <input type="hidden" name="wpda_schema_name"
1806 value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/>
1807 <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/>
1808 <?php $this->create_nonce(); ?>
1809 </form>
1810 </div>
1811 <form id="design_table_form"
1812 action="?page=<?php echo esc_attr( $this->page ); ?>"
1813 autocomplete="off"
1814 method="post" onsubmit="return pre_submit()">
1815 <fieldset class="wpda_fieldset">
1816 <legend>
1817 <span>
1818 <?php echo esc_attr__( 'Table definition', 'wp-data-access' ); ?>
1819 <?php
1820 if (
1821 '' !== $this->wpda_schema_name &&
1822 '' !== $this->wpda_table_name &&
1823 null !== $this->wpda_table_design
1824 ) {
1825 $table_structure = json_decode( json_encode( $this->wpda_table_design ), true );
1826 if ( isset( $table_structure['table'] ) ) {
1827 $column_names = array_column( (array) $table_structure['table'], 'column_name' ); // phpcs:ignore -- 8.1 proof
1828 } else {
1829 $column_names = array();
1830 }
1831 // Validate schema, table and column names
1832 $warning = WPDA::validate_names( $this->wpda_schema_name, $this->wpda_table_name, $column_names );
1833 if ( '' !== $warning ) {
1834 echo $warning; // phpcs:ignore WordPress.Security.EscapeOutput
1835 }
1836 }
1837 ?>
1838 </span>
1839 </legend>
1840 <table class="wpda-table-structure">
1841 <thead>
1842 <tr>
1843 <td class="wpda-table-structure-first-column">
1844 <label for "wpda_schema_name"><?php echo esc_attr__( 'Database', 'wp-data-access' ); ?> </label>
1845 </td>
1846 <td>
1847 <select name="wpda_schema_name" id="wpda_schema_name" style="width:100%;max-width:100%;">
1848 <?php
1849 global $wpdb;
1850 foreach ( $this->databases as $database ) {
1851 if ( null === $this->wpda_schema_name || '' === $this->wpda_schema_name ) {
1852 $dbs = WPDA::get_user_default_scheme();
1853 } else {
1854 $dbs = $this->wpda_schema_name;
1855 }
1856 $selected = $dbs === $database['schema_name'] ? 'selected' : '';
1857 if ( $wpdb->dbname === $database['schema_name'] ) {
1858 $database_printed = "WordPress database ({$database['schema_name']})";
1859 } else {
1860 $database_printed = $database['schema_name'];
1861 }
1862 echo "<option value='{$database['schema_name']}' $selected>{$database_printed}</option>"; // phpcs:ignore WordPress.Security.EscapeOutput
1863 }
1864 ?>
1865 </select>
1866 </td>
1867 <td colspan="4">
1868 </td>
1869 <td colspan="4" class="wpda-table-structure-last-column">
1870 <span style="float:right;">
1871 <label>
1872 <input type="radio"
1873 name="design_mode"
1874 value="basic"
1875 class="design_mode"
1876 onclick="return switch_mode(event)"
1877 <?php echo 'basic' === $this->design_mode ? 'checked' : ''; ?>
1878 >
1879 <?php echo esc_attr__( 'Basic Design Mode', 'wp-data-access' ); ?>
1880 </label>
1881 <label>
1882 <input type="radio"
1883 name="design_mode"
1884 value="advanced"
1885 class="design_mode"
1886 onclick="return switch_mode(event)"
1887 <?php echo 'advanced' === $this->design_mode ? 'checked' : ''; ?>
1888 >
1889 <?php echo esc_attr__( 'Advanced Design Mode', 'wp-data-access' ); ?>
1890 </label>
1891 </span>
1892 </td> </tr>
1893 <tr>
1894 <td class="wpda-table-structure-first-column">
1895 <label for "wpda_table_name"><?php echo esc_attr__( 'Table name', 'wp-data-access' ); ?> </label>
1896 </td>
1897 <td>
1898 <input type="text" name="wpda_table_name" id="wpda_table_name" maxlength="64"
1899 style="width:100%;max-width:100%;"
1900 value="<?php echo esc_attr( $this->wpda_table_name ); ?>"
1901 onchange="jQuery('#wpda_table_name_re').val(jQuery(this).val())"
1902 class="wpda_mysql_names wpda_view_table"
1903 />
1904 </td>
1905 <td colspan="4">
1906 <?php
1907 if ( $this->table_exists ) {
1908 if ( $this->is_wp_table ) {
1909 ?>
1910 <span style="vertical-align:-webkit-baseline-middle; cursor:pointer;"
1911 title="<?php echo esc_attr__( 'You cannot use a WordPress table name', 'wp-data-access' ); ?>"
1912 class="dashicons dashicons-flag wpda_tooltip">
1913 </span>
1914 <?php
1915 } else {
1916 ?>
1917 <i title="<?php echo esc_attr__( 'A table with this name already exists in the database', 'wp-data-access' ); ?>"
1918 class="fas fa-info-circle pointer wpda_tooltip" style="padding: 0 5px; font-size: 24px; line-height: 30px"></i>
1919 <?php
1920 }
1921 ?>
1922 <a href="javascript:void(0)"
1923 id="reconcile_button"
1924 onclick="if (confirm('<?php echo esc_attr__( 'Reconcile table? Your current modifications will be lost!', 'wp-data-access' ); ?>')) { jQuery('#wpda_reconcile_form').submit(); }"
1925 class="button wpda_tooltip"
1926 title="Update table design from database table (overwrites current design)">
1927 <i class="fas fa-redo wpda_icon_on_button"></i>
1928 <?php echo esc_attr__( 'Reconcile', 'wp-data-access' ); ?>
1929 </a>
1930 <?php
1931 } else {
1932 $title = __( 'New table', 'wp-data-access' );
1933 ?>
1934 <span style="vertical-align:-webkit-baseline-middle; cursor:pointer;"
1935 title="<?php echo esc_attr( $title ); ?>"
1936 class="dashicons dashicons-warning wpda_tooltip">
1937 </span>
1938 <?php
1939 }
1940 ?>
1941 <input type="hidden" name="wpda_table_name_original"
1942 value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/>
1943 <input type="hidden" name="wpda_schema_name_original"
1944 value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/>
1945 <?php
1946 if ( ! $this->wpda_table_design ) {
1947 ?>
1948 <a href="javascript:void(0)"
1949 onclick="jQuery('#wpda_reverse_engineering').show()"
1950 class="button wpda_view_table wpda_tooltip"
1951 title="<?php echo esc_attr__( 'Load table from database', 'wp-data-access' ); ?>"
1952 >
1953 <?php echo esc_attr__( 'Reverse engineering', 'wp-data-access' ); ?>
1954 </a>
1955 <?php
1956 }
1957 ?>
1958 </td>
1959 <td colspan="4" class="wpda-table-structure-last-column">
1960 </td>
1961 </tr>
1962 <?php if ( 'advanced' === $this->design_mode ) { ?>
1963 <tr>
1964 <td class="wpda-table-structure-first-column">
1965 <label for "engine"><?php echo esc_attr__( 'Engine', 'wp-data-access' ); ?> </label>
1966 </td>
1967 <td>
1968 <select name="engine" id="engine" style="width:100%;max-width:100%;" class="wpda_view_table">
1969 <?php
1970 $engines = WPDA_Dictionary_Lists::get_engines();
1971 $engine_saved = isset( $this->wpda_table_design->engine ) ? $this->wpda_table_design->engine : '';
1972 foreach ( $engines as $engine ) {
1973 $selected_tag = '';
1974 if ( '' === $engine_saved ) {
1975 if ( 'DEFAULT' === $engine['support'] ) {
1976 $selected_tag = 'selected';
1977 }
1978 } else {
1979 if ( $engine_saved === $engine['engine'] ) {
1980 $selected_tag = 'selected';
1981 }
1982 }
1983 ?>
1984 <option value="<?php echo esc_attr( $engine['engine'] ); ?>" <?php echo esc_attr( $selected_tag ); ?>>
1985 <?php echo esc_attr( $engine['engine'] ); ?>
1986 </option>
1987 <?php
1988 }
1989 ?>
1990 </select>
1991 </td>
1992 <td colspan="8" class="wpda-table-structure-last-column">
1993 <span style="float:right;">
1994 <a id="button_show_create_table" href="javascript:void(0)"
1995 class="button button-secondary wpda_tooltip"
1996 title="Generates a create table script from table and index design."
1997 onclick="jQuery('#show_create_table_form').submit();">
1998 <i class="fas fa-code wpda_icon_on_button"></i>
1999 <?php echo esc_attr__( 'Show CREATE TABLE script', 'wp-data-access' ); ?>
2000 </a>
2001 <a id="button_show_alter_table" href="javascript:void(0)"
2002 class="button button-secondary wpda_tooltip"
2003 title="Generates a alter table script from table and index design."
2004 onclick="jQuery('#show_alter_table_form').submit();">
2005 <i class="fas fa-code wpda_icon_on_button"></i>
2006 <?php echo esc_attr__( 'Show ALTER TABLE script', 'wp-data-access' ); ?>
2007 </a>
2008 </span>
2009 </td>
2010 </tr>
2011 <tr>
2012 <td class="wpda-table-structure-first-column">
2013 <label for "collation"><?php echo esc_attr__( 'Collation', 'wp-data-access' ); ?> </label>
2014 </td>
2015 <td>
2016 <select name="collation" id="collation" style="width:100%;max-width:100%;"
2017 class="wpda_view_table">
2018 <?php
2019 $character_set_name = '';
2020 $default_collation = WPDA_Dictionary_Lists::get_default_collation();
2021 $collations = WPDA_Dictionary_Lists::get_collations();
2022 $collation_saved = isset( $this->wpda_table_design->collation ) ? $this->wpda_table_design->collation : '';
2023 foreach ( $collations as $collation ) {
2024 if ( $character_set_name !== $collation['character_set_name'] ) {
2025 if ( '' !== $character_set_name ) {
2026 echo '</optgroup>';
2027 }
2028 $character_set_name = $collation['character_set_name'];
2029 echo '<optgroup label="' . esc_attr( $collation['character_set_name'] ) . '">';
2030 }
2031 $selected_tag = '';
2032 if ( '' === $collation_saved ) {
2033 if ( $collation['collation_name'] === $default_collation[0]['default_collation_name'] ) {
2034 $selected_tag = 'selected';
2035 }
2036 } else {
2037 if ( $collation_saved === $collation['collation_name'] ) {
2038 $selected_tag = 'selected';
2039 }
2040 }
2041 ?>
2042 <option value="<?php echo esc_attr( $collation['collation_name'] ); ?>" <?php echo esc_attr( $selected_tag ); ?>>
2043 <?php echo esc_attr( $collation['collation_name'] ); ?>
2044 </option>
2045 <?php
2046 }
2047 ?>
2048 </select>
2049 <?php echo '</optgroup>'; ?>
2050 </td>
2051 <td colspan="8" class="wpda-table-structure-last-column">
2052 <span style="float:right;">
2053 <label id="checkbox_show_deleted_label">
2054 <input id="checkbox_show_deleted" type="checkbox"
2055 onclick="if (jQuery(this).is(':checked')) { jQuery('.wpda_column_deleted').show(); } else { jQuery('.wpda_column_deleted').hide(); }">
2056 <?php echo esc_attr__( 'Show deleted columns and indexes', 'wp-data-access' ); ?>
2057 </label>
2058 </span>
2059 </td>
2060 </tr>
2061 <?php } ?>
2062 </thead>
2063 </table>
2064 </fieldset>
2065 <br/>
2066 <fieldset class="wpda_fieldset">
2067 <legend>
2068 <span>
2069 <?php echo esc_attr__( 'Add columns', 'wp-data-access' ); ?>
2070 </span>
2071 </legend>
2072 <table class="wpda-table-structure" style="border-collapse: collapse;">
2073 <thead>
2074 <tr>
2075 <th class="wpda-table-structure-first-column-move"></th>
2076 <th>
2077 <?php echo esc_attr__( 'Column name', 'wp-data-access' ); ?>
2078 </th>
2079 <th>
2080 <?php echo esc_attr__( 'Column type', 'wp-data-access' ); ?>
2081 </th>
2082 <?php if ( 'advanced' === $this->design_mode ) { ?>
2083 <th>
2084 <?php echo esc_attr__( 'Type attribute', 'wp-data-access' ); ?>
2085 </th>
2086 <?php } ?>
2087 <th style="min-width:60px;">
2088 <?php echo esc_attr__( 'Key?', 'wp-data-access' ); ?>
2089 </th>
2090 <th style="min-width:90px;">
2091 <?php echo esc_attr__( 'Mandatory?', 'wp-data-access' ); ?>
2092 </th>
2093 <th>
2094 <?php echo esc_attr__( 'Max length', 'wp-data-access' ); ?>
2095 </th>
2096 <th>
2097 <?php echo esc_attr__( 'Extra', 'wp-data-access' ); ?>
2098 <i title="Possible values:
2099
2100 auto_increment
2101 on update current_timestamp (for column types: timestamp and datetime)
2102 virtual generated
2103 virtual stored
2104 default_generated
2105 stored generated
2106
2107 Or combined:
2108 default_generated on update current_timestamp" class="fas fa-circle-question pointer wpda_tooltip"></i>
2109 </th>
2110 <th>
2111 <?php echo esc_attr__( 'Default value', 'wp-data-access' ); ?>
2112 </th>
2113 <th>
2114 <?php echo esc_attr__( 'List values', 'wp-data-access' ); ?>
2115 </th>
2116 <th class="wpda-table-structure-last-column">
2117 <a href="javascript:void(0)"
2118 onclick="add_row('<?php echo esc_attr( $this->design_mode ); ?>')"
2119 style="vertical-align:-webkit-baseline-middle;"
2120 class="dashicons dashicons-plus wpda_view_table wpda_tooltip"
2121 title="Add new column to table design"
2122 ></a>
2123 </th>
2124 </tr>
2125 </thead>
2126 <tbody id="wpda_table_structure"></tbody>
2127 <tfoot>
2128 <tr>
2129 <td colspan="<?php echo 'basic' === $this->design_mode ? '9' : '10'; ?>">
2130 <input type="hidden" name="submitted_changes" value="table"/>
2131 <input type="hidden" name="action" value="edit"/>
2132 <input type="hidden" name="action2"
2133 value="<?php echo esc_attr( $this->action2 ); ?>"
2134 />
2135 <?php if ( 'basic' === $this->design_mode ) { ?>
2136 <input type="hidden" name="engine" id="engine"
2137 value="<?php echo isset( $this->wpda_table_design->engine ) ? esc_attr( $this->wpda_table_design->engine ) : ''; ?>"
2138 />
2139 <input type="hidden" name="collation" id="collation"
2140 value="<?php echo isset( $this->wpda_table_design->collation ) ? esc_attr( $this->wpda_table_design->collation ) : ''; ?>"
2141 />
2142 <?php } ?>
2143 <a href="javascript:void(0)"
2144 title="Creates database table from design. Does not create indexes."
2145 class="button wpda_tooltip wpda_view
2146 <?php
2147 if ( $this->table_exists ) {
2148 echo ' disabled';
2149 }
2150 ?>
2151 "
2152 onclick="if ( confirm('Create database table `<?php echo esc_attr( WPDA::remove_backticks( $this->wpda_schema_name ) ); ?>`.`<?php echo esc_attr( WPDA::remove_backticks( $this->wpda_table_name ) ); ?>`?\nDoes not create indexes!') ) { jQuery('#wpda_create_table_form').submit(); }"
2153 <?php
2154 if ( $this->table_exists || 'new' === strtolower( $this->action2 ) ) {
2155 echo ' readonly disabled';
2156 }
2157 ?>
2158 >
2159 <i class="fas fa-check wpda_icon_on_button"></i>
2160 <?php echo esc_attr__( 'CREATE TABLE', 'wp-data-access' ); ?>
2161 </a>
2162 <a id="button_alter_table" href="javascript:void(0)" class="button wpda_view wpda_tooltip"
2163 title="Writes design changes to database table and indexes."
2164 onclick="if ( confirm('Alter database table `<?php echo esc_attr( WPDA::remove_backticks( $this->wpda_schema_name ) ); ?>`.`<?php echo esc_attr( WPDA::remove_backticks( $this->wpda_table_name ) ); ?>`?\nAlters modified indexes as well!') ) { jQuery('#wpda_alter_table_form').submit(); }"
2165 >
2166 <i class="fas fa-redo wpda_icon_on_button"></i>
2167 <?php echo esc_attr__( 'ALTER TABLE', 'wp-data-access' ); ?>
2168 </a>
2169 <a href="javascript:void(0)"
2170 title="This action drops your database table! Not your table design... This cannot be undone."
2171 class="button wpda_tooltip wpda_view
2172 <?php
2173 if ( ! $this->table_exists ) {
2174 echo ' disabled';
2175 }
2176 ?>
2177 "
2178 onclick="if ( confirm('Drop database table `<?php echo esc_attr( WPDA::remove_backticks( $this->wpda_schema_name ) ); ?>`.`<?php echo esc_attr( WPDA::remove_backticks( $this->wpda_table_name ) ); ?>`?\nTable design will not be deleted!') ) { jQuery('#wpda_drop_table_form').submit(); }"
2179 <?php
2180 if ( ! $this->table_exists ) {
2181 echo ' readonly disabled';
2182 }
2183 ?>
2184 >
2185 <i class="fas fa-trash wpda_icon_on_button"></i>
2186 <?php echo esc_attr__( 'DROP TABLE', 'wp-data-access' ); ?>
2187 </a>
2188 <input type='hidden' name='caller'
2189 value='<?php echo esc_attr( $this->caller ); ?>'/>
2190 <button type="submit" class="button button-primary wpda_view_table wpda_tooltip"
2191 title="This does NOT create the table! Is just saves your table design..."
2192 >
2193 <i class="fas fa-check wpda_icon_on_button"></i>
2194 Save Table Design
2195 </button>
2196 </td>
2197 </tr>
2198 </tfoot>
2199 </table>
2200 </fieldset>
2201 <?php $this->create_nonce(); ?>
2202 </form>
2203 <form id="wpda_reset_form" action="?page=<?php echo esc_attr( $this->page ); ?>" method="post">
2204 <input type="hidden" name="action" value="edit"/>
2205 <?php
2206 if ( 'new' !== $this->action2 ) {
2207 ?>
2208 <input type="hidden" name="wpda_table_name"
2209 value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/>
2210 <input type="hidden" name="wpda_schema_name"
2211 value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/>
2212 <?php
2213 }
2214 ?>
2215 <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/>
2216 <?php $this->create_nonce(); ?>
2217 </form>
2218 <form id="switch_mode_form" method="post"
2219 action="?page=<?php echo esc_attr( $this->page ); ?>">
2220 <input type="hidden" name="design_mode"
2221 value="<?php echo 'basic' === $this->design_mode ? 'advanced' : 'basic'; ?>">
2222 <input type="hidden" name="action" value="edit">
2223 <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/>
2224 <?php $this->create_nonce(); ?>
2225 </form>
2226 </div>
2227 <br/>
2228 <div>
2229 <form id="design_table_form_indexes" action="?page=<?php echo esc_attr( $this->page ); ?>"
2230 method="post">
2231 <fieldset class="wpda_fieldset">
2232 <legend>
2233 <span>
2234 <?php echo esc_attr__( 'Add indexes', 'wp-data-access' ); ?>
2235 </span>
2236 </legend>
2237 <table class="wpda-table-structure" style="border-collapse: collapse;">
2238 <thead>
2239 <tr>
2240 <th style="padding-left:10px;">
2241 <?php echo esc_attr__( 'Index name', 'wp-data-access' ); ?>
2242 </th>
2243 <th>
2244 <?php echo esc_attr__( 'Type?', 'wp-data-access' ); ?>
2245 </th>
2246 <th>
2247 <?php echo esc_attr__( 'Column name(s)', 'wp-data-access' ); ?>
2248 </th>
2249 <th></th>
2250 <th class="wpda-table-structure-last-column" style="width:20px;">
2251 <a href="javascript:void(0)" onclick="add_index()"
2252 style="vertical-align: -webkit-baseline-middle;"
2253 class="dashicons dashicons-plus wpda_view_index wpda_tooltip"
2254 title="Add new index to table design"
2255 ></a>
2256 </th>
2257 </tr>
2258 </thead>
2259 <tbody id="wpda_index_structure">
2260 </tbody>
2261 <tfoot>
2262 <tr>
2263 <td colspan="5">
2264 <input type="hidden" name="submitted_changes" value="indexes"/>
2265 <input type="hidden" name="action" value="edit"/>
2266 <input type="hidden" name="action2"
2267 value="<?php echo esc_attr( $this->action2 ); ?>"/>
2268 <input type="hidden" name="wpda_table_name"
2269 value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/>
2270 <input type="hidden" name="wpda_table_name_original"
2271 value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/>
2272 <input type="hidden" name="wpda_schema_name"
2273 value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/>
2274 <input type="hidden" name="wpda_schema_name_original"
2275 value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/>
2276 <a id="wpda_create_index" href="javascript:void(0)"
2277 title="Drops all indexes and recreates them."
2278 class="button wpda_view wpda_tooltip"
2279 onclick="if ( confirm('<?php echo 'Drop all deleted indexes and recreate all changed indexes for table' . ' `' . esc_attr( $this->wpda_table_name ) . '`?'; ?>') ) { jQuery('#wpda_create_index_form').submit(); }"
2280 >
2281 <i class="fas fa-check wpda_icon_on_button"></i>
2282 <?php echo esc_attr__( '(RE)CREATE INDEXES', 'wp-data-access' ); ?>
2283 </a>
2284 <a id="wpda_drop_index" href="javascript:void(0)"
2285 title="This action drops your indexes from the database! This cannot be undone."
2286 class="button wpda_view wpda_tooltip"
2287 onclick="if ( confirm('<?php echo esc_attr__( 'Drop all indexes for table', 'wp-data-access' ) . ' `' . esc_attr( $this->wpda_table_name ) . '`?\n' . esc_attr__( 'Does not drop primary key indexes and index designs!', 'wp-data-access' ); ?>')) { jQuery('#wpda_drop_index_form').submit(); }"
2288 >
2289 <i class="fas fa-trash wpda_icon_on_button"></i>
2290 <?php echo esc_attr__( 'DROP INDEXES', 'wp-data-access' ); ?>
2291 </a>
2292 <input type='hidden' name='caller'
2293 value='<?php echo esc_attr( $this->caller ); ?>'/>
2294 <a id="submit_indexes" href="javascript:void(0)"
2295 title="Does NOT create indexes! It just saves your index design..."
2296 onclick="if (!jQuery(this).attr('disabled')) { jQuery('#design_table_form_indexes').submit(); } else { alert('<?php echo esc_attr__( 'Save table design changes first!', 'wp-data-access' ); ?>'); }"
2297 class="button button-primary wpda_view_index wpda_tooltip">
2298 <i class="fas fa-check wpda_icon_on_button"></i>
2299 <?php echo esc_attr__( 'Save Indexes', 'wp-data-access' ); ?>
2300 </a>
2301 </td>
2302 </tr>
2303 </tfoot>
2304 </table>
2305 </fieldset>
2306 <?php $this->create_nonce(); ?>
2307 </form>
2308 </div>
2309 <?php
2310 if ( null !== $this->create_table_statement && false === $this->create_table_succeeded ) {
2311 ?>
2312 <br/>
2313 <div class="wpda_design_table">
2314 <table class="wpda-table-structure">
2315 <tfoot>
2316 <tr>
2317 <td>
2318 <h3><?php echo esc_attr__( 'The following CREATE TABLE statement failed', 'wp-data-access' ); ?></h3>
2319 <div>
2320 <div style="padding:10px; text-align: left; width: fit-content; margin: 0 auto;">
2321 <?php echo $this->create_table_statement; // phpcs:ignore WordPress.Security.EscapeOutput ?>
2322 </div>
2323 </div>
2324 <div>
2325 <strong><?php echo esc_attr( $this->wpdb_error ); ?></strong>
2326 </div>
2327 </td>
2328 </tr>
2329 </tfoot>
2330 </table>
2331 </div>
2332 <?php
2333 }
2334 if ( null !== $this->create_index_failed && 0 < count( $this->create_index_failed ) ) { // phpcs:ignore -- 8.1 proof
2335 ?>
2336 <br/>
2337 <div class="wpda_design_table">
2338 <table class="wpda-table-structure">
2339 <tfoot>
2340 <tr>
2341 <td>
2342 <h3><?php echo esc_attr__( 'The following CREATE INDEX statement(s) failed', 'wp-data-access' ); ?></h3>
2343 <div>
2344 <div style="padding:10px; text-align: left; width: fit-content; margin: 0 auto;">
2345 <?php
2346 foreach ( $this->create_index_failed as $index_failed ) {
2347 echo "$index_failed<br/>"; // phpcs:ignore WordPress.Security.EscapeOutput
2348 }
2349 ?>
2350 </div>
2351 </div>
2352 </td>
2353 </tr>
2354 </tfoot>
2355 </table>
2356 </div>
2357 <?php
2358 }
2359 if ( null !== $this->create_table_statement && false === $this->alter_table_succeeded ) {
2360 ?>
2361 <br/>
2362 <div class="wpda_design_table">
2363 <table class="wpda-table-structure">
2364 <tfoot>
2365 <tr>
2366 <td>
2367 <h3><?php echo esc_attr__( 'The following ALTER TABLE statement failed', 'wp-data-access' ); ?></h3>
2368 <div>
2369 <div style="padding:10px; text-align: left; width: fit-content; margin: 0 auto;">
2370 <?php echo $this->create_table_statement; // phpcs:ignore WordPress.Security.EscapeOutput ?>
2371 </div>
2372 </div>
2373 <div>
2374 <strong><?php echo esc_attr( $this->wpdb_error ); ?></strong>
2375 </div>
2376 </td>
2377 </tr>
2378 </tfoot>
2379 </table>
2380 </div>
2381 <?php
2382 }
2383 ?>
2384
2385 </div>
2386 <?php
2387
2388 if ( $this->wpda_table_design ) {
2389 // Display table design.
2390 foreach ( $this->wpda_table_design->table as $design_column ) {
2391 if ( ! $this->table_exists ) {
2392 // New table cannot be compared with real table.
2393 $column_changed = '';
2394 } else {
2395 // Check for table structure changes.
2396 if ( isset( $this->table_columns[ $design_column->column_name ] ) ) {
2397 // Check column arguments.
2398 $column_changed = '';
2399 foreach ( $this->real_table as $real_column ) {
2400 if ( $real_column->column_name === $design_column->column_name ) {
2401 if ( $real_column != $design_column ) {
2402 if ( strtolower( $real_column->extra ) === 'auto_increment' && strtolower( $design_column->extra ) === 'auto_increment' ) {
2403 $tmp_real_column = clone $real_column;
2404 $tmp_design_column = clone $design_column;
2405 unset( $tmp_real_column->extra );
2406 unset( $tmp_design_column->extra );
2407 if ( $tmp_real_column != $tmp_design_column ) {
2408 $column_changed = 'u';
2409 $this->table_altered = true;
2410 }
2411 } else {
2412 $column_changed = 'u';
2413 $this->table_altered = true;
2414 }
2415 }
2416 }
2417 }
2418 } else {
2419 // New column.
2420 $column_changed = 'i';
2421 $this->table_altered = true;
2422 }
2423 }
2424 ?>
2425 <script type='text/javascript'>
2426 add_row(
2427 '<?php echo esc_attr( $this->design_mode ); ?>',
2428 true,
2429 '<?php echo esc_attr( $design_column->column_name ); ?>',
2430 '<?php echo esc_attr( WPDA_Design_Table_Model::datatype2basic( esc_attr( $design_column->data_type ) ) ); ?>',
2431 '<?php echo esc_attr( $design_column->data_type ); ?>',
2432 '<?php echo esc_attr( $design_column->type_attribute ); ?>',
2433 '<?php echo esc_attr( $design_column->key ); ?>',
2434 '<?php echo esc_attr( $design_column->mandatory ); ?>',
2435 '<?php echo esc_attr( $design_column->max_length ); ?>',
2436 '<?php echo esc_attr( $design_column->extra ); ?>',
2437 '<?php echo esc_attr( $design_column->default ); ?>',
2438 '<?php echo esc_attr( $design_column->list ); ?>',
2439 '<?php echo esc_attr( $column_changed ); ?>'
2440 );
2441 </script>
2442 <?php
2443 }
2444 if ( $this->table_exists ) {
2445 // Check for deleted columns.
2446 foreach ( $this->table_columns as $table_column ) {
2447 $column_found = false;
2448 foreach ( $this->wpda_table_design->table as $design_column ) {
2449 if ( $design_column->column_name === $table_column['column_name'] ) {
2450 $column_found = true;
2451 break;
2452 }
2453 }
2454 if ( ! $column_found ) {
2455 break;
2456 }
2457 }
2458
2459 if ( ! $column_found ) {
2460 // Add deleted column(s) to form and mark as readonly.
2461 foreach ( $this->table_columns as $table_column ) {
2462 $column_found = false;
2463 foreach ( $this->wpda_table_design->table as $design_column ) {
2464 if ( $design_column->column_name === $table_column['column_name'] ) {
2465 $column_found = true;
2466 break;
2467 }
2468 }
2469 if ( ! $column_found ) {
2470 foreach ( $this->real_table as $real_column ) {
2471 if ( $real_column->column_name === $table_column['column_name'] ) {
2472 ?>
2473 <script type='text/javascript'>
2474 add_row(
2475 '<?php echo esc_attr( $this->design_mode ); ?>',
2476 true,
2477 '<?php echo esc_attr( $real_column->column_name ); ?>',
2478 '<?php echo esc_attr( WPDA_Design_Table_Model::datatype2basic( esc_attr( $real_column->data_type ) ) ); ?>',
2479 '<?php echo esc_attr( $real_column->data_type ); ?>',
2480 '<?php echo esc_attr( $real_column->type_attribute ); ?>',
2481 '<?php echo esc_attr( $real_column->key ); ?>',
2482 '<?php echo esc_attr( $real_column->mandatory ); ?>',
2483 '<?php echo esc_attr( $real_column->max_length ); ?>',
2484 '<?php echo esc_attr( $real_column->extra ); ?>',
2485 '<?php echo esc_attr( $real_column->default ); ?>',
2486 '<?php echo esc_attr( $real_column->list ); ?>',
2487 'd'
2488 );
2489 </script>
2490 <?php
2491 $this->table_altered = true;
2492 $this->deleted_columns_and_indexes = true;
2493 }
2494 }
2495 }
2496 }
2497 }
2498 }
2499
2500 // Display indexes.
2501 $indexes_found = false;
2502 foreach ( $this->wpda_table_design->indexes as $design_index ) {
2503 $index_changed = '';
2504 if ( $this->table_exists ) {
2505 $index_found = false;
2506 // Check if index was changed or new.
2507 foreach ( $this->real_indexes as $real_index ) {
2508 if ( $design_index->index_name === $real_index['index_name'] ) {
2509 $index_found = true;
2510 if (
2511 $real_index['unique'] != $design_index->unique ||
2512 $real_index['column_names'] != $design_index->column_names
2513 ) {
2514 $index_changed = 'u';
2515 $this->updated_indexes = true;
2516 }
2517 break;
2518 }
2519 }
2520 if ( ! $index_found ) {
2521 $index_changed = 'i';
2522 $this->updated_indexes = true;
2523 }
2524 }
2525 ?>
2526 <script type='text/javascript'>
2527 add_index(
2528 true,
2529 '<?php echo esc_attr( $design_index->index_name ); ?>',
2530 '<?php echo esc_attr( $design_index->unique ); ?>',
2531 '<?php echo esc_attr( $design_index->column_names ); ?>',
2532 '<?php echo esc_attr( $index_changed ); ?>'
2533 );
2534 </script>
2535 <?php
2536 $indexes_found = true;
2537 }
2538
2539 if ( $this->table_exists ) {
2540 foreach ( $this->real_indexes as $real_index ) {
2541 $real_indexes_found = false;
2542 foreach ( $this->wpda_table_design->indexes as $design_index ) {
2543 if ( $real_index['index_name'] === $design_index->index_name ) {
2544 $real_indexes_found = true;
2545 break;
2546 }
2547 }
2548
2549 if ( ! $real_indexes_found ) {
2550 // Index was dropped.
2551 ?>
2552 <script type='text/javascript'>
2553 add_index(
2554 true,
2555 '<?php echo esc_attr( $real_index['index_name'] ); ?>',
2556 '<?php echo esc_attr( $real_index['unique'] ); ?>',
2557 '<?php echo esc_attr( $real_index['column_names'] ); ?>',
2558 '<?php echo 'd'; ?>'
2559 );
2560 </script>
2561 <?php
2562 $this->deleted_columns_and_indexes = true;
2563 $this->updated_indexes = true;
2564 }
2565 }
2566 }
2567
2568 if ( ! $indexes_found ) {
2569 ?>
2570 <script type='text/javascript'>
2571 add_index(true);
2572 </script>
2573 <?php
2574 }
2575 } else {
2576 // Display one empty row.
2577 ?>
2578 <script type='text/javascript'>
2579 add_row('<?php echo esc_attr( $this->design_mode ); ?>', true);
2580 add_index(true);
2581 disable_index();
2582 disable_create_buttons();
2583 </script>
2584 <?php
2585 }
2586
2587 ?>
2588 <script type='text/javascript'>
2589 <?php if ( ! $this->wpda_table_design ) { ?>
2590 jQuery('#button_show_create_table').prop("readonly", true).prop("disabled", true).addClass("disabled");
2591 <?php } ?>
2592 <?php
2593 if ( ! $this->wpda_table_design || ! $this->table_altered ) {
2594 if ( ! $this->updated_indexes ) {
2595 ?>
2596 jQuery("#button_show_alter_table").prop("readonly", true).prop("disabled", true).addClass("disabled");
2597 jQuery("#button_alter_table").prop("readonly", true).prop("disabled", true).addClass("disabled");
2598 <?php
2599 }
2600 }
2601 ?>
2602 <?php if ( ! $this->updated_indexes ) { ?>
2603 jQuery('#wpda_create_index').prop("readonly", true).prop("disabled", true).addClass("disabled");
2604 <?php } ?>
2605 <?php if ( ! $this->real_indexes ) { ?>
2606 jQuery('#wpda_drop_index').prop("readonly", true).prop("disabled", true).addClass("disabled");
2607 <?php } ?>
2608 </script>
2609 <?php
2610
2611 if ( 'view' === $this->action ) {
2612 ?>
2613 <script type='text/javascript'>
2614 disable_page();
2615 </script>
2616 <?php
2617 }
2618
2619 if ( $this->is_wp_table ) {
2620 // WP table names are not allowed: disable create table button.
2621 ?>
2622 <script type='text/javascript'>
2623 disable_page();
2624 </script>
2625 <?php
2626 }
2627
2628 // Save all table names in array table_name check.
2629 ?>
2630 <script type='text/javascript'>
2631 var wpda_db_table_name = [];
2632 <?php
2633 $designer_table_list = WPDA_Design_Table_Model::get_designer_table_list();
2634 foreach ( $designer_table_list as $key => $value ) {
2635 echo 'wpda_db_table_name["' . esc_attr( $value['wpda_table_name'] ) . '"]=true;';
2636 }
2637 if ( ! $this->deleted_columns_and_indexes ) {
2638 echo "jQuery('#checkbox_show_deleted_label').addClass('label_disabled');";
2639 echo "jQuery('#checkbox_show_deleted').attr('disabled', true);";
2640 }
2641 ?>
2642 </script>
2643 <?php
2644 }
2645
2646 private function create_nonce() {
2647 if ( '' === $this->wpda_table_name ) {
2648 return wp_nonce_field( "wpda-design-table-form-" . WPDA_Design_Table_Model::get_base_table_name() );
2649 }
2650 return wp_nonce_field( "wpda-design-table-form-{$this->wpda_table_name}" );
2651 }
2652
2653 /**
2654 * Drop all indexes from database
2655 *
2656 * @since 2.0.14
2657 */
2658 protected function drop_indexes() {
2659 foreach ( $this->real_indexes as $real_index ) {
2660 $this->drop_index( $real_index['index_name'] );
2661 }
2662 }
2663
2664 /**
2665 * Drop a specific index from database
2666 *
2667 * @param string $index_name Name of index to be dropped
2668 *
2669 * @since 2.0.14
2670 */
2671 protected function drop_index( $index_name ) {
2672 $wpdadb = WPDADB::get_db_connection( $this->wpda_schema_name );
2673 if ( null === $wpdadb ) {
2674 /* translators: %s = remote database name */
2675 wp_die( esc_attr( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), $this->wpda_schema_name ) ) );
2676 }
2677
2678 $suppress = $wpdadb->suppress_errors( true );
2679
2680 // Index is deleted from table design: drop index
2681 $drop_index_statement = 'DROP INDEX `' . str_replace( '`', '', $index_name ) . "` ON `{$this->wpda_table_name}`";
2682 if ( $wpdadb->query( $drop_index_statement ) ) {
2683 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
2684 $msg = new WPDA_Message_Box(
2685 array(
2686 /* translators: %s = index name */
2687 'message_text' => sprintf( __( 'Index `%s` dropped', 'wp-data-access' ), esc_attr( $index_name) ),
2688 )
2689 );
2690 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
2691 $msg->box();
2692 } else {
2693 $msg = new WPDA_Message_Box(
2694 array(
2695 'message_text' => __( 'DROP INDEX failed', 'wp-data-access' ),
2696 'message_type' => 'error',
2697 'message_is_dismissible' => false,
2698 )
2699 );
2700 $msg->box();
2701 $this->create_index_failed[] = $drop_index_statement;
2702 }
2703 $wpdadb->suppress_errors( $suppress );
2704 }
2705
2706 /**
2707 * Create indexes from design
2708 *
2709 * @since 2.0.14
2710 */
2711 protected function create_index() {
2712 $wpdadb = WPDADB::get_db_connection( $this->wpda_schema_name );
2713 if ( null === $wpdadb ) {
2714 /* translators: %s = remote database name */
2715 wp_die( esc_attr( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), $this->wpda_schema_name ) ) );
2716 }
2717
2718 $suppress = $wpdadb->suppress_errors( true );
2719
2720 if ( 'show_create_table_script' !== $this->action2_posted ) {
2721 $this->drop_indexes();
2722 }
2723
2724 // Recreate indexes.
2725 foreach ( $this->wpda_table_design->indexes as $index ) {
2726 if ( '' === $index->index_name || '' === $index->column_names ) {
2727 continue;
2728 }
2729
2730 $unique = '';
2731 if ( 'Yes' === $index->unique ) {
2732 $unique = 'UNIQUE';
2733 } elseif ( 'FULLTEXT' === $index->unique ) {
2734 if ( 'on' === $this->fulltext_support ) {
2735 $unique = 'FULLTEXT';
2736 } else {
2737 $unique = '';
2738 }
2739 }
2740 $column_names_array = explode( ',', ( string ) $index->column_names ); // phpcs:ignore -- 8.1 proof
2741 $column_names = '`' . implode( '`,`', $column_names_array ) . '`';
2742 $create_index_statement =
2743 "CREATE $unique INDEX `" . str_replace( '`', '', $index->index_name ) . "` ON `{$this->wpda_table_name}` ($column_names)";
2744 $this->create_index_statement .= $create_index_statement . ';' . self::NEW_LINE;
2745
2746 if ( 'show_create_table_script' === $this->action2_posted ) {
2747 continue;
2748 }
2749
2750 if ( $wpdadb->query( $create_index_statement ) ) {
2751 // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
2752 $msg = new WPDA_Message_Box(
2753 array(
2754 /* translators: %s = index name */
2755 'message_text' => sprintf( __( 'Index `%s` created', 'wp-data-access' ), $index->index_name ),
2756 )
2757 );
2758 // phpcs:enable WordPress.WP.I18n.MissingTranslatorsComment
2759 $msg->box();
2760 } else {
2761 $msg = new WPDA_Message_Box(
2762 array(
2763 'message_text' => __( 'CREATE INDEX failed', 'wp-data-access' ),
2764 'message_type' => 'error',
2765 'message_is_dismissible' => false,
2766 )
2767 );
2768 $msg->box();
2769 $this->create_index_failed[] = $create_index_statement;
2770 }
2771 }
2772
2773 $wpdadb->suppress_errors( $suppress );
2774 }
2775 }
2776
2777 }
2778