PluginProbe
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards / 5.5.79
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards v5.5.79
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 5.5.43 All 159 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.79, at WPDataAccess/Design_Table/WPDA_Design_Table_Form.php

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