<?php /** * Suppress "error - 0 - No summary was found for this file" on phpdoc generation * * @package WPDataAccess\Design_Table */ namespace WPDataAccess\Design_Table { use WPDataAccess\Connection\WPDADB; use WPDataAccess\Data_Dictionary\WPDA_Dictionary_Exist; use WPDataAccess\Data_Dictionary\WPDA_Dictionary_Lists; use WPDataAccess\Data_Dictionary\WPDA_List_Columns_Cache; use WPDataAccess\Utilities\WPDA_Message_Box; use WPDataAccess\Utilities\WPDA_Reverse_Engineering; use WPDataAccess\Plugin_Table_Models\WPDA_Design_Table_Model; use WPDataAccess\WPDA; use WPDataAccess\Dashboard\WPDA_Dashboard; /** * Class WPDA_Design_Table_Form * * This class provides the user interface of the Data Designer tool. The form can be used to create or alter table * and index designs. Tables and indexes can be created from their design by using the appropriate buttons. * * Tables and their indexes can be reverse engineered from the database. This process can be started from scratch * or applied to an existing table design. For the last situation users can use the reconcile button. This will * bring the table and index design in the same state as their physical database counterpart. * * @author Peter Schulz * @since 1.1.0 */ class WPDA_Design_Table_Form { const NEW_LINE = '<br/>'; /** * Page name (wpda_designer) * * @var string|null */ protected $page = null; /** * Action argument * * @var string|null */ protected $action = null; /** * Saved value of action2 argument used for later equations * * @var string|null */ protected $action2 = null; /** * Action2 argument * * @var string|null */ protected $action2_posted = null; /** * Object of type WPDA_Design_Table_Model used for data manipulation * * @var object|null */ protected $model = null; /** * Table name * * @var string|null */ protected $wpda_table_name = null; /** * Schema name * * @var string|null */ protected $wpda_schema_name = null; /** * Table design (taken from WPDA_Design_Table_Model) * * @var array|null */ protected $wpda_table_design = null; /** * Indicates whether the table was found in the database * * @var boolean|null */ protected $table_exists = null; /** * Indicates whether the structure of the database table equals the table design * * @var boolean|null */ protected $table_altered = false; /** * Indicates whether the table is a WordPress table * * @var boolean|null */ protected $is_wp_table = false; /** * Create table statement for the designed table at startup. Updates in the user interface are not immediately * reflected. User needs to reload/save the page. * * @var string */ protected $create_table_statement = ''; /** * Alter table statement for the designed table at startup. Updates in the user interface are not immediately * reflected. User needs to reload/save the page. * * @var array */ protected $alter_table_statement = array(); /** * Create index statement(s) for the designed index(es) at startup. Updates in the user interface are not * immediately reflected. User needs to reload/save the page. * * @var string */ protected $create_index_statement = ''; /** * Indicates where a create table statement succeeded * * @var boolean|null */ protected $create_table_succeeded = null; /** * Indicates where a alter table statement succeeded * * @var boolean|null */ protected $alter_table_succeeded = null; /** * Indicates where a create index statement failed * * @var boolean|null */ protected $create_index_failed = null; /** * Named array holding table columns * * @see WPDA_List_Columns_Cache::get_list_columns() * * @var array|null */ protected $table_columns = null; /** * Allowed values are: Basic or Advanced. Can be supplied as an argument. Taken from WPDA class f no argument * is provided. * * @see WPDA::OPTION_BE_DESIGN_MODE * * @var string|null */ protected $design_mode = null; /** * Last error from wpdb * * @var string|null */ protected $wpdb_error = null; /** * Indicates whether columns or indexes were deleted in the actual design. If true checkbox "Show deleted * columns and indexes" will be accessible. * * @var bool */ protected $deleted_columns_and_indexes = false; /** * Indicates whether indexes were updated in the actual design. * * @var bool */ protected $updated_indexes = false; /** * Holds the structure of the real physical database table * * @var array|null */ protected $real_table = null; /** * Holds the structure of the real physical database indexes * * @var array|null */ protected $real_indexes = null; /** * Indicates whether the design has indexes. * * @var bool */ protected $indexes_found = false; /** * Argument which can be used to jump back to another page than the default table list for table designs. * * @var string */ protected $caller = ''; /** * Holds all available databases * * @var array */ protected $database = array(); protected $databases; protected $fulltext_support = false; /** * WPDA_Design_Table_Form constructor. * * @since 1.1.0 */ public function __construct() { if ( isset( $_REQUEST['page'] ) ) { $this->page = sanitize_text_field( wp_unslash( $_REQUEST['page'] ) ); // input var okay. } else { wp_die( __( 'ERROR: Wrong arguments [page not found]', 'wp-data-access' ) ); } if ( isset( $_REQUEST['action'] ) ) { $this->action = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); // input var okay. } if ( isset( $_REQUEST['action2'] ) ) { $this->action2 = sanitize_text_field( wp_unslash( $_REQUEST['action2'] ) ); // input var okay. $this->action2_posted = $this->action2; } if ( isset( $_REQUEST['design_mode'] ) ) { $this->design_mode = sanitize_text_field( wp_unslash( $_REQUEST['design_mode'] ) ); // input var okay. } else { $this->design_mode = WPDA::get_option( WPDA::OPTION_BE_DESIGN_MODE ); // Default design mode. } if ( isset( $_REQUEST['caller'] ) ) { $this->caller = sanitize_text_field( wp_unslash( $_REQUEST['caller'] ) ); // input var okay. } $this->fulltext_support = get_option( 'wpda_fulltext_support' ); global $wpdb; if ( 'init' === $this->action2 ) { if ( isset( $_REQUEST['wpda_table_name'] ) && isset( $_REQUEST['wpda_schema_name'] ) ) { // Check if table is already in repository. $wpdb->get_results( $wpdb->prepare( 'select * from `%1s` where wpda_schema_name = %s and wpda_table_name = %s ', // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders array( WPDA::remove_backticks( WPDA_Design_Table_Model::get_base_table_name() ), sanitize_text_field( wp_unslash( $_REQUEST['wpda_schema_name'] ) ), sanitize_text_field( wp_unslash( $_REQUEST['wpda_table_name'] ) ), ) ) ); if ( 1 === $wpdb->num_rows ) { // Table already in repository. $this->action2 = 'edit'; } else { // Table not in repository. $this->action2 = 'wpda_reverse_engineering'; } } else { wp_die( __( 'ERROR: Wrong arguments [table not found]', 'wp-data-access' ) ); } } if ( 'wpda_reverse_engineering' === $this->action2 || 'wpda_reconcile' === $this->action2 ) { if ( isset( $_REQUEST['wpda_table_name_re'] ) && isset( $_REQUEST['wpda_schema_name_re'] ) ) { $wpda_table_name_re = sanitize_text_field( wp_unslash( $_REQUEST['wpda_table_name_re'] ) ); $wpda_schema_name_re = sanitize_text_field( wp_unslash( $_REQUEST['wpda_schema_name_re'] ) ); if ( 'wpda_reconcile' === $this->action2 ) { // Before table can be reconciled old table structure must be deleted. $wpdb->query( $wpdb->prepare( 'delete from `%1s` where wpda_table_name = %s and wpda_schema_name = %s ', // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders array( WPDA::remove_backticks( WPDA_Design_Table_Model::get_base_table_name() ), $wpda_table_name_re, $wpda_schema_name_re, ) ) ); } // Start reverse engineering table. $wpda_reverse_engineering = new WPDA_Reverse_Engineering( $wpda_table_name_re, $wpda_schema_name_re ); $this->design_mode = isset( $_REQUEST['design_mode_re'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['design_mode_re'] ) ) : $this->design_mode; // input var okay. $table_structure = $wpda_reverse_engineering->get_designer_format( $this->design_mode ); if ( count( $table_structure ) > 0 ) {//phpcs:ignore - 8.1 proof if ( isset( $_REQUEST['wpda_table_name'] ) && '' !== trim( $_REQUEST['wpda_table_name'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput $this->wpda_table_name = sanitize_text_field( wp_unslash( $_REQUEST['wpda_table_name'] ) ); } else { $this->wpda_table_name = $wpda_table_name_re; } if ( isset( $_REQUEST['wpda_schema_name'] ) && '' !== trim( $_REQUEST['wpda_schema_name'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput $this->wpda_schema_name = sanitize_text_field( wp_unslash( $_REQUEST['wpda_schema_name'] ) ); } else { $this->wpda_schema_name = $wpda_schema_name_re; } $this->wpda_table_design = $table_structure; } else { wp_die( __( 'ERROR: Reverse engineering table failed [invalid structure]', 'wp-data-access' ) ); } if ( ! WPDA_Design_Table_Model::insert_reverse_engineered( $this->wpda_table_name, $this->wpda_schema_name, $this->wpda_table_design ) ) { wp_die( __( 'ERROR: Reverse engineering table failed [insert failed]', 'wp-data-access' ) ); } else { // Convert named array to object (needed to display structure). $this->wpda_table_design = json_decode( json_encode( $table_structure ) ); } $this->action2 = 'edit'; } else { wp_die( __( 'ERROR: Wrong arguments [table not found]', 'wp-data-access' ) ); } } elseif ( isset( $_REQUEST['wpda_table_name'] ) && isset( $_REQUEST['wpda_schema_name'] ) ) { $this->wpda_table_name = sanitize_text_field( wp_unslash( $_REQUEST['wpda_table_name'] ) ); $this->wpda_schema_name = sanitize_text_field( wp_unslash( $_REQUEST['wpda_schema_name'] ) ); $this->model = new WPDA_Design_Table_Model(); if ( 'new' === $this->action2 ) { $insert_result = $this->model->insert(); if ( false === $insert_result || $insert_result < 1 ) { wp_die( __( 'ERROR: Insert failed', 'wp-data-access' ) ); } $this->action2 = 'edit'; // Show saved records and allow editing. } elseif ( 'edit' === $this->action2 && 'init' !== $this->action2_posted ) { $result_update = $this->model->update(); if ( false === $result_update ) { $msg = new WPDA_Message_Box( array( 'message_text' => __( 'Update failed', 'wp-data-access' ), 'message_type' => 'error', 'message_is_dismissible' => false, ) ); $msg->box(); } if ( 0 === $result_update ) { $msg = new WPDA_Message_Box( array( 'message_text' => __( 'Nothing to save', 'wp-data-access' ), ) ); $msg->box(); } else { $msg = new WPDA_Message_Box( array( 'message_text' => __( 'Succesfully saved changes to database', 'wp-data-access' ), ) ); $msg->box(); } } $this->model->query(); $structure_messages = $this->model->validate(); foreach ( $structure_messages as $messages ) { if ( 'ERR' === $messages[0] ) { $msg = new WPDA_Message_Box( array( 'message_text' => $messages[1], 'message_type' => 'error', 'message_is_dismissible' => false, ) ); $msg->box(); } else { $msg = new WPDA_Message_Box( array( 'message_text' => $messages[1], ) ); $msg->box(); } } $this->wpda_table_design = $this->model->get_table_design(); $this->design_mode = $this->wpda_table_design->design_mode; $this->action2 = 'edit'; // Editing mode. } else { $this->action2 = 'new'; // Design new table from scratch. } // Remove backticks from schema and table name to prevent sql injection $this->wpda_schema_name = str_replace( '`', '', (string) $this->wpda_schema_name ); $this->wpda_table_name = str_replace( '`', '', (string) $this->wpda_table_name ); if ( null !== $this->wpda_table_name && null !== $this->wpda_schema_name ) { // Check if table name already exists in database. if ( $this->wpda_schema_name === $wpdb->dbname ) { $wp_tables = array_flip( $wpdb->tables( 'all', true ) ); if ( isset( $wp_tables[ $this->wpda_table_name ] ) ) { $this->is_wp_table = true; $this->table_exists = true; } else { $this->does_table_exist(); } } else { $this->does_table_exist(); } // Get design structure for real database table. $this->get_table_structure(); if ( 'create_table' === $this->action2_posted || 'show_create_table_script' === $this->action2_posted ) { // Perform create table (and indexes) script. $this->create_table(); $this->does_table_exist(); $this->get_table_structure(); } elseif ( 'alter_table' === $this->action2_posted ) { // Generate alter table script and perform script. $this->do_alter_table(); $this->get_table_structure(); } elseif ( 'show_alter_table_script' === $this->action2_posted ) { // Generate alter table script and show result in overlay. $this->alter_table(); } elseif ( 'create_table_index' === $this->action2_posted ) { // Perform create index(es) script. $this->create_index(); $this->get_table_structure(); } elseif ( 'drop_table' === $this->action2_posted ) { // Perform drop table script. $this->drop_table(); $this->does_table_exist(); } elseif ( 'drop_table_index' === $this->action2_posted ) { // Perform drop table script. $this->drop_indexes(); $this->get_table_structure(); } if ( $this->table_exists ) { // Get database table structure. $wpda_list_columns = WPDA_List_Columns_Cache::get_list_columns( $this->wpda_schema_name, $this->wpda_table_name ); $table_columns = $wpda_list_columns->get_table_columns(); // Convert indexed array to named array to improve access. foreach ( $table_columns as $table_column ) { $this->table_columns[ $table_column['column_name'] ] = $table_column; } } } $this->databases = WPDA_Dictionary_Lists::get_db_schemas(); } public function prepare_form() {} /** * Get table and index structure from design * * @since 2.0.14 */ private function get_table_structure() { if ( $this->table_exists ) { $get_table_structure = new WPDA_Reverse_Engineering( $this->wpda_table_name, $this->wpda_schema_name ); $real_structure = $get_table_structure->get_designer_format( $this->design_mode ); $this->real_table = $real_structure['table']; $this->real_indexes = $real_structure['indexes']; } } /** * Check if table exists in our database * * @since 2.0.14 */ private function does_table_exist() { $wpda_dictionary_exists = new WPDA_Dictionary_Exist( $this->wpda_schema_name, $this->wpda_table_name ); $this->table_exists = $wpda_dictionary_exists->plain_table_exists(); if ( ! $this->table_exists ) { $this->real_table = null; $this->real_indexes = null; } } /** * Perform alter table * * Call $this->alter_table() to generate alter table script and process result taken * from $this->create_table_statement. * * @see WPDA_Design_Table_Form::alter_table() * @see WPDA_Design_Table_Form::create_table_statement * * @since 2.0.14 */ protected function do_alter_table() { $this->alter_table(); $wpdadb = WPDADB::get_db_connection( $this->wpda_schema_name ); if ( null === $wpdadb ) { wp_die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->wpda_schema_name ) ) ); } $suppress = $wpdadb->suppress_errors( true ); $create_table_statement = str_replace( self::NEW_LINE, "\n", $this->create_table_statement ); if ( '' !== $create_table_statement ) { // Process alter table script (store in $create_table_statement) $sql_end = strpos( $create_table_statement, ";\n" ); while ( false !== $sql_end ) { $sql = rtrim( substr( $create_table_statement, 0, $sql_end ) ); $create_table_statement = substr( $create_table_statement, strpos( $create_table_statement, $sql ) + strlen( $sql ) + 1 ); if ( ! $wpdadb->query( $sql ) ) { $this->wpdb_error = $wpdadb->last_error; $this->alter_table_succeeded = false; return; } $sql_end = strpos( $create_table_statement, ";\n" ); } } $wpdadb->suppress_errors( $suppress ); } /** * Generate alter table script * * Alter table script is written to $this->create_table_statement. * * @see WPDA_Design_Table_Form::create_table_statement * * @since 2.0.14 */ protected function alter_table() { $create_keys_design = array(); $create_keys_real = array(); // Find deleted and changed indexes foreach ( $this->real_indexes as $real_index ) { $drop_real_index = false; $design_index_found = false; foreach ( $this->wpda_table_design->indexes as $design_index ) { if ( $real_index['index_name'] === $design_index->index_name ) { $design_index_found = true; if ( $real_index['unique'] != $design_index->unique || $real_index['column_names'] != $design_index->column_names ) { $drop_real_index = true; break; } break; } } if ( $drop_real_index || ! $design_index_found ) { $this->create_table_statement .= 'DROP INDEX `' . str_replace( '`', '', $real_index['index_name'] ) . "` ON `{$this->wpda_table_name}`;" . self::NEW_LINE; } } if ( '' !== $this->create_table_statement ) { $this->create_table_statement .= self::NEW_LINE; } // Find new and changed columns foreach ( $this->wpda_table_design->table as $design_column ) { $design_column_found = false; foreach ( $this->real_table as $real_column ) { if ( $real_column->column_name === $design_column->column_name ) { if ( $real_column != $design_column ) { // Modify column $this->alter_table_column( $design_column, 'MODIFY' ); } $design_column_found = true; break; } } if ( ! $design_column_found ) { // Add new column $this->alter_table_column( $design_column, 'ADD' ); } if ( 'Yes' === $design_column->key ) { $create_keys_design[] = $design_column->column_name; } } // Find deleted columns foreach ( $this->real_table as $real_column ) { $real_column_found = false; foreach ( $this->wpda_table_design->table as $design_column ) { if ( $real_column->column_name === $design_column->column_name ) { $real_column_found = true; break; } } if ( ! $real_column_found ) { // Drop column //phpcs:ignore - 8.1 proof array_push( $this->alter_table_statement, 'DROP COLUMN `' . str_replace( '`', '', $real_column->column_name ) . '`,' . self::NEW_LINE ); } if ( 'Yes' === $real_column->key ) { $create_keys_real[] = $real_column->column_name; } } if ( 0 < count( $this->alter_table_statement ) ) {//phpcs:ignore - 8.1 proof $this->create_table_statement .= "ALTER TABLE `{$this->wpda_table_name}` "; foreach ( $this->alter_table_statement as $sql ) { $this->create_table_statement .= $sql; } $this->create_table_statement = substr( $this->create_table_statement, 0, strrpos( $this->create_table_statement, ',' ) ) . ';' . self::NEW_LINE . self::NEW_LINE; $array_difference_1 = array_diff( $create_keys_design, $create_keys_real ); //phpcs:ignore - 8.1 proof $array_difference_2 = array_diff( $create_keys_real, $create_keys_design ); //phpcs:ignore - 8.1 proof if ( 0 !== count( $array_difference_1 ) || 0 !== count( $array_difference_2 ) ) { //phpcs:ignore - 8.1 proof if ( 0 < count( $create_keys_real ) ) { //phpcs:ignore - 8.1 proof $this->create_table_statement = "ALTER TABLE `{$this->wpda_table_name}` DROP PRIMARY KEY;" . self::NEW_LINE . self::NEW_LINE . $this->create_table_statement; } if ( 0 < count( $create_keys_design ) ) { //phpcs:ignore - 8.1 proof $alter_table_statement = "ALTER TABLE `{$this->wpda_table_name}` ADD PRIMARY KEY "; foreach ( $create_keys_design as $key ) { $alter_table_statement .= $key === reset( $create_keys_design ) ? '(' : ',';//phpcs:ignore - 8.1 proof $alter_table_statement .= '`' . str_replace( '`', '', $key ) . '`'; } $alter_table_statement .= ');' . self::NEW_LINE . self::NEW_LINE; $this->create_table_statement .= $alter_table_statement; } } } // Add new and changed indexes foreach ( $this->wpda_table_design->indexes as $design_index ) { $real_index_found = false; $create_new_index = false; foreach ( $this->real_indexes as $real_index ) { if ( $real_index['index_name'] === $design_index->index_name ) { $real_index_found = true; if ( $real_index['unique'] != $design_index->unique || $real_index['column_names'] != $design_index->column_names ) { $create_new_index = true; break; } break; } } if ( ! $real_index_found || $create_new_index ) { $unique = ''; if ( 'Yes' === $design_index->unique ) { $unique = 'UNIQUE'; } elseif ( 'FULLTEXT' === $design_index->unique ) { if ( 'on' === $this->fulltext_support ) { $unique = 'FULLTEXT'; } else { $unique = ''; } } $column_names_array = explode( ',', ( string ) $design_index->column_names );//phpcs:ignore - 8.1 proof $column_names = '`' . implode( '`,`', $column_names_array ) . '`'; $this->create_table_statement .= "CREATE $unique INDEX `" . str_replace( '`', '', $design_index->index_name ) . "` ON `{$this->wpda_table_name}` ($column_names);" . self::NEW_LINE; } } } /** * Alter column format * * @param object $design_column Column definition * @param string $keyword ADD or MODIFY * * @since 2.0.14 */ private function alter_table_column( $design_column, $keyword ) { $alter_table_statement = "$keyword COLUMN `" . str_replace( '`', '', $design_column->column_name ) . '` '; $alter_table_statement .= $design_column->data_type; if ( '' !== $design_column->max_length ) { $alter_table_statement .= "($design_column->max_length)"; } if ( 'enum' === $design_column->data_type || 'set' === $design_column->data_type ) { $alter_table_statement .= '(' . $design_column->list . ')'; } $alter_table_statement .= ' '; $alter_table_statement .= 'Yes' === $design_column->mandatory ? 'NOT NULL' : 'NULL'; if ( '' !== $design_column->default ) { $alter_table_statement .= " DEFAULT {$design_column->default}"; } if ( '' !== $design_column->extra ) { $alter_table_statement .= ' '; $alter_table_statement .= $design_column->extra; } $alter_table_statement .= ',' . self::NEW_LINE; array_push( $this->alter_table_statement, $alter_table_statement );//phpcs:ignore - 8.1 proof } /** * Drop database table * * Does not drop WordPress tables. * * @since 2.0.14 */ protected function drop_table() { if ( $this->is_wp_table ) { $msg = new WPDA_Message_Box( array( 'message_text' => sprintf( __( 'Cannot drop WordPress table `%s`', 'wp-data-access' ), $this->wpda_table_name ), 'message_type' => 'error', 'message_is_dismissible' => false, ) ); $msg->box(); return; } if ( $this->table_exists ) { $wpdadb = WPDADB::get_db_connection( $this->wpda_schema_name ); if ( null === $wpdadb ) { wp_die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->wpda_schema_name ) ) ); } $suppress = $wpdadb->suppress_errors( true ); $drop_table_statement = "DROP TABLE `{$this->wpda_table_name}`"; if ( $wpdadb->query( $drop_table_statement ) ) { $msg = new WPDA_Message_Box( array( 'message_text' => sprintf( __( 'Table `%s` dropped', 'wp-data-access' ), $this->wpda_table_name ), ) ); $msg->box(); } else { $msg = new WPDA_Message_Box( array( 'message_text' => __( 'DROP TABLE failed', 'wp-data-access' ), 'message_type' => 'error', 'message_is_dismissible' => false, ) ); $msg->box(); $this->create_table_statement = $drop_table_statement; } $wpdadb->suppress_errors( $suppress ); } } /** * Perform create table statement * * @since 1.1.0 */ protected function create_table() { $is_valid = true; $this->create_table_statement = "CREATE TABLE `{$this->wpda_table_name}`" . self::NEW_LINE; $create_keys = array(); foreach ( $this->wpda_table_design->table as $row ) { $this->create_table_statement .= $row === reset( $this->wpda_table_design->table ) ? '(' : ',';//phpcs:ignore - 8.1 proof $this->create_table_statement .= '`' . str_replace( '`', '', $row->column_name ) . '`'; $this->create_table_statement .= ' '; $this->create_table_statement .= $row->data_type; if ( '' !== $row->max_length ) { $this->create_table_statement .= "($row->max_length)"; } else { if ( 'varchar' === $row->data_type || 'char' === $row->data_type || 'varbinary' === $row->data_type || 'binary' === $row->data_type ) { $is_valid = false; $msg = new WPDA_Message_Box( array( 'message_text' => "Column {$row->column_name} of {$row->data_type} must have a max length", 'message_type' => 'error', 'message_is_dismissible' => false, ) ); $msg->box(); } } if ( '' !== $row->type_attribute ) { $this->create_table_statement .= " {$row->type_attribute} "; } if ( 'enum' === $row->data_type || 'set' === $row->data_type ) { $this->create_table_statement .= '(' . $row->list . ')'; } $this->create_table_statement .= ' '; $this->create_table_statement .= 'Yes' === $row->mandatory ? 'NOT NULL' : 'NULL'; if ( '' !== $row->default ) { $this->create_table_statement .= " DEFAULT {$row->default}"; } if ( '' !== $row->extra ) { $this->create_table_statement .= ' '; $this->create_table_statement .= $row->extra; } if ( 'Yes' === $row->key ) { $create_keys[] = $row->column_name; } $this->create_table_statement .= self::NEW_LINE; } if ( 0 < count( $create_keys ) ) {//phpcs:ignore - 8.1 proof $this->create_table_statement .= ',PRIMARY KEY '; foreach ( $create_keys as $key ) { $this->create_table_statement .= $key === reset( $create_keys ) ? '(' : ',';//phpcs:ignore - 8.1 proof $this->create_table_statement .= '`' . str_replace( '`', '', $key ) . '`'; } $this->create_table_statement .= ')'; $this->create_table_statement .= self::NEW_LINE; } $this->create_table_statement .= ')'; if ( isset( $this->wpda_table_design->engine ) && '' !== $this->wpda_table_design->engine ) { $this->create_table_statement .= ' ENGINE ' . $this->wpda_table_design->engine; } if ( isset( $this->wpda_table_design->collation ) && '' !== $this->wpda_table_design->collation ) { $collation = explode( '_', $this->wpda_table_design->collation );//phpcs:ignore - 8.1 proof $this->create_table_statement .= ' DEFAULT CHARACTER SET ' . $collation[0] . ' COLLATE=' . $this->wpda_table_design->collation; } $this->create_table_statement .= ';' . self::NEW_LINE . self::NEW_LINE; if ( ! $is_valid ) { return; } if ( 'show_create_table_script' === $this->action2_posted ) { // Just show CREATE TABLE script! // SQL script is available in $this->create_table_statement and can be shown on the page. $this->create_index(); } else { // Create table and indexes. $wpdadb = WPDADB::get_db_connection( $this->wpda_schema_name ); if ( null === $wpdadb ) { wp_die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->wpda_schema_name ) ) ); } $suppress = $wpdadb->suppress_errors( true ); $this->create_table_succeeded = $wpdadb->query( str_replace( self::NEW_LINE, '', $this->create_table_statement ) ); if ( $this->create_table_succeeded ) { $msg = new WPDA_Message_Box( array( 'message_text' => __( 'Table created', 'wp-data-access' ), ) ); $msg->box(); } else { $msg = new WPDA_Message_Box( array( 'message_text' => __( 'CREATE TABLE failed', 'wp-data-access' ), 'message_type' => 'error', 'message_is_dismissible' => false, ) ); $msg->box(); $this->wpdb_error = $wpdadb->last_error; } $wpdadb->suppress_errors( $suppress ); } } /** * Show Data Designer form * * @since 1.1.0 */ public function show() { ?> <script type='text/javascript'> var row_num = 1; var index_num = 1; var table_updated = false; var index_updated = false; var no_cols_selected = 'no column(s) selected'; function disable_page() { jQuery(".wpda_view").prop("readonly", true).prop("disabled", true).addClass("disabled"); disable_table(); disable_index(); disable_create_buttons(); jQuery('#reconcile_button').prop("readonly", true).prop("disabled", true).addClass("disabled"); jQuery('.design_mode').prop("readonly", true).prop("disabled", true).addClass("disabled"); jQuery('.column_names').prop("readonly", true).prop("disabled", true).addClass("disabled"); } function disable_table() { jQuery(".wpda_view_table").prop("readonly", true).prop("disabled", true).addClass("disabled"); } function disable_index() { jQuery(".wpda_view_index").prop("readonly", true).prop("disabled", true).addClass("disabled"); } function disable_create_buttons() { jQuery("#button_show_create_table").prop("readonly", true).prop("disabled", true).addClass("disabled"); jQuery("#button_show_alter_table").prop("readonly", true).prop("disabled", true).addClass("disabled"); jQuery("#button_create_table").prop("readonly", true).prop("disabled", true).addClass("disabled"); jQuery("#button_alter_table").prop("readonly", true).prop("disabled", true).addClass("disabled"); jQuery("#button_create_index").prop("readonly", true).prop("disabled", true).addClass("disabled"); jQuery("#button_recreate_index").prop("readonly", true).prop("disabled", true).addClass("disabled"); } function rem_row(e) { var curr_id = e.target.parentNode.parentNode.id; if (confirm("Delete column?")) { jQuery("#" + curr_id).remove(); updated_table(); } } function rem_index(e) { var curr_id = e.target.parentNode.parentNode.id; if (confirm("Delete index?")) { jQuery("#" + curr_id).remove(); updated_indexes(); } } function updated_table() { disable_create_buttons(); disable_index(); table_updated = true; } function updated_indexes() { disable_create_buttons(); disable_table(); index_updated = true; } function check_basic_data_type(row_num) { check_numeric_items_off(); switch (jQuery('#basic_data_type_' + row_num).val()) { case 'Text': jQuery('#data_type_' + row_num).val('text'); jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table'); break; case 'Integer': jQuery('#data_type_' + row_num).val('int'); jQuery('#max_length_' + row_num).attr('class', 'wpda_digits_only wpda_view_table'); break; case 'Real': jQuery('#data_type_' + row_num).val('float'); jQuery('#max_length_' + row_num).attr('class', 'wpda_real wpda_view_table'); break; case 'List': jQuery('#data_type_' + row_num).val('enum'); jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table'); break; case 'Boolean': jQuery('#data_type_' + row_num).val('tinyint'); jQuery('#max_length_' + row_num).attr('class', 'wpda_digits_only wpda_view_table'); break; case 'Datetime': jQuery('#data_type_' + row_num).val('datetime'); jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table'); break; case 'Binary': jQuery('#data_type_' + row_num).val('binary'); jQuery('#max_length_' + row_num).attr('class', 'wpda_digits_only wpda_view_table'); break; case 'Blob': jQuery('#data_type_' + row_num).val('blob'); jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table'); break; case '*ID': jQuery('#data_type_' + row_num).val('int'); jQuery('#key_' + row_num).val('Yes'); jQuery('#mandatory_' + row_num).val('Yes'); jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_digits_only wpda_view_table'); jQuery('#extra_' + row_num).val('AUTO_INCREMENT'); jQuery('#default_' + row_num).val(''); jQuery('#list_' + row_num).val(''); break; case '*TimestampC': jQuery('#data_type_' + row_num).val('timestamp'); jQuery('#key_' + row_num).val('No'); jQuery('#mandatory_' + row_num).val('No'); jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table'); jQuery('#extra_' + row_num).val(''); jQuery('#default_' + row_num).val('CURRENT_TIMESTAMP'); jQuery('#list_' + row_num).val(''); break; case '*TimestampU': jQuery('#data_type_' + row_num).val('timestamp'); jQuery('#key_' + row_num).val('No'); jQuery('#mandatory_' + row_num).val('No'); jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table'); jQuery('#extra_' + row_num).val('ON UPDATE CURRENT_TIMESTAMP'); jQuery('#default_' + row_num).val('CURRENT_TIMESTAMP'); jQuery('#list_' + row_num).val(''); break; } check_numeric_items_on(); } function check_data_type(row_num) { check_numeric_items_off(); switch (jQuery('#data_type_' + row_num + ' :selected').parent().attr('label')) { case 'Integer': jQuery('#basic_data_type_' + row_num).val('Integer'); jQuery('#max_length_' + row_num).attr('class', 'wpda_digits_only wpda_view_table'); break; case 'Text': jQuery('#basic_data_type_' + row_num).val('Text'); switch (jQuery('#data_type_' + row_num + ' :selected').val()) { case 'char': case 'varchar': jQuery('#max_length_' + row_num).attr('class', 'wpda_digits_only wpda_view_table'); break; default: jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table'); } break; case 'List': jQuery('#basic_data_type_' + row_num).val('List'); jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table'); break; case 'Date and Time': jQuery('#basic_data_type_' + row_num).val('Datetime'); jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table'); break; case 'Real': jQuery('#basic_data_type_' + row_num).val('Real'); jQuery('#max_length_' + row_num).attr('class', 'wpda_real wpda_view_table'); break; case 'Binary': switch (jQuery('#data_type_' + row_num + ' :selected').val()) { case 'binary': case 'varbinary': jQuery('#basic_data_type_' + row_num).val('Binary'); jQuery('#max_length_' + row_num).attr('class', 'wpda_digits_only wpda_view_table'); break; default: jQuery('#basic_data_type_' + row_num).val('Blob'); jQuery('#max_length_' + row_num).val('').attr('class', 'wpda_nodataentryallowed wpda_view_table'); } break; case 'Boolean': jQuery('#basic_data_type_' + row_num).val('Boolean'); jQuery('#max_length_' + row_num).attr('class', 'wpda_digits_only wpda_view_table'); break; } check_numeric_items_on(); } function check_numeric_items_off() { jQuery('.wpda_digits_only').off('keyup paste'); jQuery('.wpda_real').off('keyup paste'); jQuery('.wpda_nodataentryallowed').off('keyup paste'); } function check_numeric_items_on() { jQuery('.wpda_digits_only').on('keyup paste', function () { this.value = this.value.replace(/[^\d]/g, ''); }); jQuery('.wpda_real').on('keyup paste', function () { this.value = this.value.replace(/[^0-9,]/g, ''); }); jQuery('.wpda_nodataentryallowed').on('keyup paste', function () { this.value = ''; }); } function pre_submit() { if (jQuery('#wpda_table_name').val() === '') { alert('<?php echo __( 'Table name cannot be empty' ); ?>'); return false; } if ('<?php echo esc_attr( $this->action2 ); ?>' === 'new') { if (wpda_db_table_name[jQuery('#wpda_table_name').val()]) { alert('<?php echo __( 'Table name already used for another table design' ); ?>'); return false; } } var all_columns_entered = true; jQuery("input[name='column_name[]']").each(function () { if (jQuery(this).val() === '') { alert('<?php echo __( 'Column names cannot be empty' ); ?>'); all_columns_entered = false; } }); if (!all_columns_entered) { return false; } // Enable all listboxes that have been disable to add arguments to request. jQuery("select[id^='key']").each(function () { jQuery(this).attr('disabled', false); }); jQuery("select[id^='mandatory']").each(function () { jQuery(this).attr('disabled', false); }); return true; } 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 = '') { var row_class = ''; switch (row_action) { case 'u': row_class = 'wpda_column_updated'; break; case 'i': row_class = 'wpda_column_inserted'; break; case 'd': row_class = 'wpda_column_deleted'; } var basic_columns = '<td>' + '<select name="basic_data_type[]" id="basic_data_type_' + row_num + '" onchange="check_basic_data_type(' + row_num + ')" class="wpda_view_table">' + ' <option value="Text" ' + (basic_data_type === 'Text' ? 'selected' : '') + '>Text</option>' + ' <option value="Integer" ' + (basic_data_type === 'Integer' ? 'selected' : '') + '>Integer</option>' + ' <option value="Real" ' + (basic_data_type === 'Real' ? 'selected' : '') + '>Real</option>' + ' <option value="List" ' + (basic_data_type === 'List' ? 'selected' : '') + '}>List</option>' + ' <option value="Boolean" ' + (basic_data_type === 'Boolean' ? 'selected' : '') + '>Boolean</option>' + ' <option value="Datetime" ' + (basic_data_type === 'Datetime' ? 'selected' : '') + '>Datetime</option>' + ' <option value="Binary" ' + (basic_data_type === 'Binary' ? 'selected' : '') + '>Binary</option>' + ' <option value="Blob" ' + (basic_data_type === 'Blob' ? 'selected' : '') + '>Blob</option>' + ' <option value="*ID" ' + (basic_data_type === '*ID' ? 'selected' : '') + '>* Numeric ID (auto increment)</option>' + ' <option value="*TimestampC" ' + (basic_data_type === '*TimestampC' ? 'selected' : '') + '>* Timestamp (date created)</option>' + ' <option value="*TimestampU" ' + (basic_data_type === '*TimestampU' ? 'selected' : '') + '>* Timestamp (last updated)</option>' + '</select>' + '<input type="hidden" name="data_type[]" id="data_type_' + row_num + '" value="' + data_type + '">' + '<input type="hidden" name="type_attribute[]" id="type_attribute_' + row_num + '" value="' + type_attribute + '">' + '</td>'; var advanced_columns = '<td>' + '<select name="data_type[]" id="data_type_' + row_num + '" onchange="check_data_type(' + row_num + ')" class="wpda_view_table">' + ' <option value="" ' + (data_type === '' ? 'selected' : '') + '></option>' + ' <optgroup label="Integer">' + ' <option value="bit" ' + (data_type === 'bit' ? 'selected' : '') + '>bit</option>' + ' <option value="tinyint" ' + (data_type === 'tinyint' ? 'selected' : '') + '>tinyint</option>' + ' <option value="smallint" ' + (data_type === 'smallint' ? 'selected' : '') + '>smallint</option>' + ' <option value="mediumint" ' + (data_type === 'mediumint' ? 'selected' : '') + '>mediumint</option>' + ' <option value="int" ' + (data_type === 'int' ? 'selected' : '') + '>int</option>' + ' <option value="bigint" ' + (data_type === 'bigint' ? 'selected' : '') + '>bigint</option>' + ' </optgroup>' + ' <optgroup label="Text">' + ' <option value="char" ' + (data_type === 'char' ? 'selected' : '') + '>char</option>' + ' <option value="varchar" ' + (data_type === 'varchar' ? 'selected' : '') + '>varchar</option>' + ' <option disabled="disabled">-</option>' + ' <option value="tinytext" ' + (data_type === 'tinytext' ? 'selected' : '') + '>tinytext</option>' + ' <option value="text" ' + (data_type === 'text' ? 'selected' : '') + '>text</option>' + ' <option value="mediumtext" ' + (data_type === 'mediumtext' ? 'selected' : '') + '>mediumtext</option>' + ' <option value="longtext" ' + (data_type === 'longtext' ? 'selected' : '') + '>longtext</option>' + ' </optgroup>' + ' <optgroup label="List">' + ' <option value="enum" ' + (data_type === 'enum' ? 'selected' : '') + '>enum</option>' + ' <option value="set" ' + (data_type === 'set' ? 'selected' : '') + '>set</option>' + ' </optgroup>' + ' <optgroup label="Date and Time">' + ' <option value="date" ' + (data_type === 'date' ? 'selected' : '') + '>date</option>' + ' <option value="datetime" ' + (data_type === 'datetime' ? 'selected' : '') + '>datetime</option>' + ' <option value="timestamp" ' + (data_type === 'timestamp' ? 'selected' : '') + '>timestamp</option>' + ' <option value="time" ' + (data_type === 'time' ? 'selected' : '') + '>time</option>' + ' <option value="year" ' + (data_type === 'year' ? 'selected' : '') + '>year</option>' + ' </optgroup>' + ' <optgroup label="Real">' + ' <option value="decimal" ' + (data_type === 'decimal' ? 'selected' : '') + '>decimal</option>' + ' <option value="double" ' + (data_type === 'double' ? 'selected' : '') + '>double</option>' + ' <option value="float" ' + (data_type === 'float' ? 'selected' : '') + '>float</option>' + ' </optgroup>' + ' <optgroup label="Binary">' + ' <option value="binary" ' + (data_type === 'binary' ? 'selected' : '') + '>binary</option>' + ' <option value="varbinary" ' + (data_type === 'varbinary' ? 'selected' : '') + '>varbinary</option>' + ' <option disabled="disabled">-</option>' + ' <option value="tinyblob" ' + (data_type === 'tinyblob' ? 'selected' : '') + '>tinyblob</option>' + ' <option value="blob" ' + (data_type === 'blob' ? 'selected' : '') + '>blob</option>' + ' <option value="mediumblob" ' + (data_type === 'mediumblob' ? 'selected' : '') + '>mediumblob</option>' + ' <option value="longblob" ' + (data_type === 'longblob' ? 'selected' : '') + '>longblob</option>' + ' </optgroup>' + ' <optgroup label="Boolean">' + ' <option value="boolean" ' + (data_type === 'boolean' ? 'selected' : '') + '>boolean</option>' + ' </optgroup>' + '</select>' + '<input type="hidden" name="basic_data_type[]" id="basic_data_type_' + row_num + '" value="' + basic_data_type + '">' + '</td>' + '<td>' + ' <select name="type_attribute[]" id="type_attribute_' + row_num + '" class="wpda_view_table">' + ' <option value=""></option>' + ' <option value="unsigned" ' + (type_attribute === 'unsigned' ? 'selected' : '') + '>unsigned</option>' + ' <option value="unsigned zerofill" ' + (type_attribute === 'unsigned zerofill' ? 'selected' : '') + '>unsigned zerofill</option>' + ' </select>' + '</td>'; var new_row = '<tr id="row_num_' + row_num + '" class="' + row_class + '">' + '<td class="wpda-table-structure-first-column-move">' + ' <span class="dashicons dashicons-move grabbable" style="float:left;"></span>' + '</td>' + '<td>' + ' <input type="text" name="column_name[]" id="column_name_' + row_num + '"' + ' maxlength="64" value="' + column_name + '" class="wpda_mysql_names wpda_view_table">' + '</td>' + (design_mode === 'basic' ? basic_columns : advanced_columns) + '<td>' + ' <select name="key[]" id="key_' + row_num + '" class="wpda_view_table">' + ' <option value="No" ' + (key === 'No' ? 'selected' : '') + '>No</option>' + ' <option value="Yes" ' + (key === 'Yes' ? 'selected' : '') + '>Yes</option>' + ' </select>' + '</td>' + '<td>' + ' <select name="mandatory[]" id="mandatory_' + row_num + '" class="wpda_view_table">' + ' <option value="No"' + (mandatory === 'No' ? 'selected' : '') + '>No</option>' + ' <option value="Yes"' + (mandatory === 'Yes' ? 'selected' : '') + '>Yes</option>' + ' </select>' + '</td>' + '<td>' + ' <input type="text" name="max_length[]" id="max_length_' + row_num + '"' + ' value="' + (max_length === '0' ? '' : max_length) + '" class="' + (basic_data_type === 'Real' ? 'wpda_float' : 'wpda_digits_only') + ' wpda_view_table">' + '</td>' + '<td>' + ' <input type="text" name="extra[]" id="extra_' + row_num + '" value="' + extra + '" class="wpda_view_table">' + '</td>' + '<td>' + ' <input type="text" name="default[]" id="default_' + row_num + '" value="' + default_value + '" class="wpda_view_table">' + '</td>' + '<td>' + ' <input type="text" name="list[]" id="list_' + row_num + '" value="' + list + '" class="wpda_view_table">' + ' <input type="hidden" name="row_action[]" value="' + row_action + '">' + '</td>' + '<td class="wpda-table-structure-last-column">' + ' <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>' + '</td>' + '</tr>'; if (jQuery("#wpda_table_structure tr").length === 0) { jQuery("#wpda_table_structure").append(new_row); } else { jQuery("#wpda_table_structure tr:last").after(new_row); } <?php if ( 'basic' === $this->design_mode ) { ?> check_basic_data_type(row_num); <?php } else { ?> check_data_type(row_num) <?php } ?> row_num++; jQuery('.wpda_mysql_names').off('keyup paste'); jQuery('.wpda_mysql_names').on('keyup paste', function () { this.value = this.value.replace(/[^\w\_]/g, ''); }); check_numeric_items_off(); check_numeric_items_on(); jQuery(".wpda_view_table").off('change paste keyup', updated_table); jQuery(".wpda_view_table").on('change paste keyup', updated_table); jQuery("a.wpda_view_table").off(); jQuery("a.wpda_view_table").on('click', updated_table); jQuery(".wpda_column_deleted").find("input,select").attr('disabled', true); if (!init) { updated_table(); } } function select_available(e) { var option = jQuery("#columns_available option:selected"); var add_to = jQuery("#columns_selected"); option.remove(); new_option = add_to.append(option); if (jQuery("#columns_selected option[value='']").length > 0) { // Remove ALL from selected list. jQuery("#columns_selected option[value='']").remove(); } jQuery('select#columns_selected option').prop("selected", false); } function select_selected(e) { var option = jQuery("#columns_selected option:selected"); if (option[0].value === '') { // Cannot remove ALL. return; } var add_to = jQuery("#columns_available"); option.remove(); add_to.append(option); if (jQuery('select#columns_selected option').length === 0) { jQuery("#columns_selected").append(jQuery('<option></option>').attr('value', '').text(no_cols_selected)); } jQuery('select#columns_available option').prop("selected", false); } function show_index_dialog(e) { if ('<?php echo esc_attr( $this->action ); ?>' === 'view') { return; } var item_id = e.target.id; var index_row_num = item_id.substr(item_id.lastIndexOf("_") + 1); if (jQuery('#add_columns_' + index_row_num).hasClass('disabled') === true) { return; } var columns_available = jQuery( '<select id="columns_available" name="columns_available[]" multiple size="8" style="width:200px" onchange="select_available()">' + '</select>' ); jQuery("input[name='column_name[]']").each(function () { columns_available.append(jQuery('<option></option>').attr('value', jQuery(this).val()).text(jQuery(this).val())); }); var columns_selected = jQuery( '<select id="columns_selected" name="columns_selected[]" multiple size="8" style="width:200px" onchange="select_selected()">' + '<option value="">' + no_cols_selected + '</option>' + '</select>' ); var dialog_table = jQuery('<table style="width:410px"></table>'); var dialog_table_row = dialog_table.append(jQuery('<tr></tr>')); dialog_table_row.append(jQuery('<td width="50%"></td>').append(columns_available)); dialog_table_row.append(jQuery('<td width="50%"></td>').append(columns_selected)); // var dialog_table_row_available = dialog_table.append(jQuery('<tr></tr>').append(jQuery('<td width="50%"></td>'))); // dialog_table_row_available.append(columns_available); // // var dialog_table_row_selected = dialog_table.append(jQuery('<tr></tr>').append(jQuery('<td width="50%"></td>'))); // dialog_table_row_selected.append(columns_selected); var dialog_text = jQuery('<div style="width:410px"></div>'); var dialog = jQuery('<div></div>'); dialog.append(dialog_text); dialog.append(dialog_table); jQuery(dialog).dialog( { dialogClass: 'wp-dialog no-close', title: 'Add column(s) to index', modal: true, autoOpen: true, closeOnEscape: false, resizable: false, width: 'auto', buttons: { "Close": function () { var selected_columns = ''; jQuery("#columns_selected option").each( function () { selected_columns += jQuery(this).val() + ','; } ); if (selected_columns !== '') { selected_columns = selected_columns.slice(0, -1); } jQuery('#column_names_' + index_row_num).val(selected_columns); jQuery(this).dialog('destroy').remove(); updated_indexes(); }, "Cancel": function () { jQuery(this).dialog('destroy').remove(); } } } ); wpda_add_icons_to_dialog_buttons(); jQuery(".ui-button-icon-only").hide(); } function add_index(init = false, index_name = '', unique = '', column_names = '', row_action = '') { var row_class = ''; switch (row_action) { case 'u': row_class = 'wpda_column_updated'; break; case 'i': row_class = 'wpda_column_inserted'; break; case 'd': row_class = 'wpda_column_deleted'; } <?php if ( 'on' === $this->fulltext_support ) { ?> var fulltext_support = '<option value="FULLTEXT"' + (unique === 'FULLTEXT' ? 'selected' : '') + '>Full Text</option>'; <?php } else { ?> var fulltext_support = ''; <?php } ?> var new_index = '<tr id="idx_row_num_' + index_num + '" class="' + row_class + '">' + '<td style="padding-left:10px;">' + ' <input type="text" name="index_name[]" id="index_name_' + index_num + '"' + ' value="' + index_name + '" class="wpda_view_index wpda_mysql_names">' + '</td>' + '<td>' + ' <select name="unique[]" id="unique_' + index_num + '" class="wpda_view_index">' + ' <option value="No"' + (unique === 'No' ? 'selected' : '') + '>Non unique</option>' + ' <option value="Yes"' + (unique === 'Yes' ? 'selected' : '') + '>Unique</option>' + fulltext_support + ' </select>' + '</td>' + '<td>' + ' <input type="text" name="column_names[]" id="column_names_' + index_num + '" class="column_names"' + ' value="' + column_names + '" onclick="show_index_dialog(event)" readonly>' + '</td>' + '<td>' + '</td>' + ' <input type="button" name="add_columns[]" id="add_columns_' + index_num + '"' + ' value="Add column(s)" onclick="show_index_dialog(event)" class="wpda_view_index">' + ' <input type="hidden" name="row_action[]" value="' + row_action + '">' + '</td>' + '<td class="wpda-table-structure-last-column" style="width:20px;">' + ' <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>' + ' </td>' + '</tr>'; if (jQuery("#wpda_index_structure tr").length === 0) { jQuery("#wpda_index_structure").append(new_index); } else { jQuery("#wpda_index_structure tr:last").after(new_index); } index_num++; jQuery('.wpda_mysql_names').off('keyup paste'); jQuery('.wpda_mysql_names').on('keyup paste', function () { this.value = this.value.replace(/[^\w\_]/g, ''); }); jQuery('#submit_indexes').attr('disabled', false); jQuery(".wpda_view_index").off('change paste keyup', updated_indexes); jQuery(".wpda_view_index").on('change paste keyup', updated_indexes); jQuery("a.wpda_view_index").off(); jQuery("a.wpda_view_index").on('click', updated_indexes); jQuery(".wpda_column_deleted").find("input,select").attr('disabled', true); if (!init) { updated_indexes(); } } function switch_mode(e) { if ('new' === '<?php echo esc_attr( $this->action2 ); ?>' && !table_updated && !index_updated) { if (e.target.value !== '<?php echo esc_attr( $this->design_mode ); ?>') { if (confirm('Switch to ' + e.target.value + ' mode?')) { jQuery('#switch_mode_form').submit(); } else { return false; } } } else if ('edit' === '<?php echo esc_attr( $this->action2 ); ?>' || table_updated || index_updated) { if (e.target.value !== '<?php echo esc_attr( $this->design_mode ); ?>') { if (confirm('Switch to ' + e.target.value + ' mode?')) { jQuery('#design_table_form').submit(); } else { return false; } } } } function pre_submit_re() { if (wpda_db_table_name[jQuery('select[name="wpda_table_name_re"]').val()]) { alert('<?php echo __( 'Table name already used for another table design' ); ?>'); return false; } jQuery('#design_mode_re').val(jQuery('input[name="design_mode"]:checked').val()); return true; } function get_tables() { schema_name = jQuery('#wpda_schema_name_re_list').val(); var url = location.pathname + '?action=wpda_get_tables&hideviews=TRUE'; var data = { wpdaschema_name: schema_name, wpda_wpnonce: '<?php echo esc_attr( wp_create_nonce( 'wpda-getdata-access-' . WPDA::get_current_user_login() ) ); ?>' }; jQuery.post( url, data, function (data) { jQuery('#wpda_table_name_re_list').find('option').remove(); var jsonData = JSON.parse(data); for (i = 0; i < jsonData.length; i++) { jQuery('#wpda_table_name_re_list').append( jQuery("<option></option>") .attr("value", jsonData[i]['table_name']) .text(jsonData[i]['table_name']) ); } } ); } </script> <div class="wrap"> <h1> <a href="?page=<?php echo '' === $this->caller ? esc_attr( $this->page ) : esc_attr( \WP_Data_Access_Admin::PAGE_MAIN ); ?>" style="display: inline-block; vertical-align: unset;" class="dashicons dashicons-arrow-left-alt2 wpda_tooltip" title="<?php echo __( 'List', 'wp-data-access' ); ?>" ></a> Data Designer </h1> <div style="display:none;"> <form id="wpda_create_table_form" action="?page=<?php echo esc_attr( $this->page ); ?>" method="post"> <input type="hidden" name="action" value="edit"/> <input type="hidden" name="action2" value="create_table"/> <input type="hidden" name="wpda_table_name" value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/> <input type="hidden" name="wpda_schema_name" value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/> <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/> </form> <form id="wpda_alter_table_form" action="?page=<?php echo esc_attr( $this->page ); ?>" method="post"> <input type="hidden" name="action" value="edit"/> <input type="hidden" name="action2" value="alter_table"/> <input type="hidden" name="wpda_table_name" value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/> <input type="hidden" name="wpda_schema_name" value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/> <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/> </form> <form id="wpda_drop_table_form" action="?page=<?php echo esc_attr( $this->page ); ?>" method="post"> <input type="hidden" name="action" value="edit"/> <input type="hidden" name="action2" value="drop_table"/> <input type="hidden" name="wpda_table_name" value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/> <input type="hidden" name="wpda_schema_name" value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/> <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/> </form> <form id="wpda_create_index_form" action="?page=<?php echo esc_attr( $this->page ); ?>" method="post" style="margin-left:1px;margin-right:1px;"> <input type="hidden" name="action" value="edit"/> <input type="hidden" name="action2" value="create_table_index"/> <input type="hidden" name="wpda_table_name" value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/> <input type="hidden" name="wpda_schema_name" value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/> <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/> </form> <form id="wpda_drop_index_form" action="?page=<?php echo esc_attr( $this->page ); ?>" method="post" style="margin-left:1px;margin-right:1px;"> <input type="hidden" name="action" value="edit"/> <input type="hidden" name="action2" value="drop_table_index"/> <input type="hidden" name="wpda_table_name" value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/> <input type="hidden" name="wpda_schema_name" value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/> <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/> </form> </div> <style> #overlay_show_create_table { height: 400px; width: 600px; position: fixed; display: none; top: 50%; left: 50%; transform: translate(-50%, -50%); -ms-transform: translate(-50%, -50%); right: 0; bottom: 0; background-color: #f9f9f9; opacity: .95; border: 1px solid #ccc; cursor: pointer; z-index: 1000; } #overlay_show_create_table_text { height: 360px; width: 400px; padding: 10px; position: relative; top: 50%; left: 220px; transform: translate(-50%, -50%); -ms-transform: translate(-50%, -50%); color: black; overflow-y: auto; background-color: white; border: 1px solid #ccc; } </style> <div id="overlay_show_create_table"> <div id="overlay_show_create_table_text"> <?php echo $this->create_table_statement . $this->create_index_statement; // phpcs:ignore WordPress.Security.EscapeOutput ?> </div> <div style="position: absolute; bottom: 0; right: 0; padding-right: 10px; padding-bottom: 10px;"> <a id="button-copy-clipboard" href="javascript:void(0)" class="button button-secondary" style="text-align:center;width:150px;" 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 ?>"> <i class="fas fa-clipboard wpda_icon_on_button"></i> <?php echo __( 'Copy to clipboard' ); ?> </a> <br/> <div style="height: 5px;"></div> <a href="javascript:void(0)" class="button button-primary" style="text-align:center;width:150px;" onclick="jQuery('#overlay_show_create_table').hide()"> <i class="fas fa-times-circle wpda_icon_on_button"></i> <?php echo __( 'Close' ); ?> </a> </div> </div> <script type='text/javascript'> jQuery(function () { <?php if ( 'show_create_table_script' === $this->action2_posted || 'show_alter_table_script' === $this->action2_posted ) { ?> jQuery('#overlay_show_create_table').show(); <?php } ?> var sql_to_clipboard = new ClipboardJS('#button-copy-clipboard'); sql_to_clipboard.on('success', function (e) { jQuery.notify('<?php echo __( 'SQL successfully copied to clipboard!' ); ?>','info'); }); sql_to_clipboard.on('error', function (e) { jQuery.notify('<?php echo __( 'Could not copy SQL to clipboard!' ); ?>','error'); }); jQuery('#wpda_table_structure').sortable(); jQuery( '.wpda_tooltip' ).tooltip(); }); </script> <?php if ( ! $this->wpda_table_design ) { // Allow loading table from database into designer (reverse engineering). $table_list = WPDA_Dictionary_Lists::get_tables( false ); ?> <div id="wpda_reverse_engineering" style="display: none"> <br/> <div class="wpda_reverse_engineering"> <form id="wpda_reverse_engineering_form" action="?page=<?php echo esc_attr( $this->page ); ?>" method="post"> <label><?php echo __( 'Load table from database' ); ?> </label> <select name="wpda_schema_name_re" id="wpda_schema_name_re_list" onchange="get_tables()"> <?php global $wpdb; foreach ( $this->databases as $database ) { if ( null === $this->wpda_schema_name || '' === $this->wpda_schema_name ) { $dbs = WPDA::get_user_default_scheme(); } else { $dbs = $this->wpda_schema_name; } $selected = $dbs === $database['schema_name'] ? 'selected' : ''; if ( $wpdb->dbname === $database['schema_name'] ) { $database_printed = "WordPress database ({$database['schema_name']})"; } else { $database_printed = $database['schema_name']; } echo "<option value='{$database['schema_name']}' $selected>{$database_printed}</option>"; // phpcs:ignore WordPress.Security.EscapeOutput } ?> </select> <select name="wpda_table_name_re" id="wpda_table_name_re_list"> <?php foreach ( $table_list as $key => $value ) { echo '<option value="' . esc_attr( $value['table_name'] ) . '">' . esc_attr( $value['table_name'] ) . '</option>'; } ?> </select> <input type="hidden" name="action" value="edit"/> <input type="hidden" name="action2" value="wpda_reverse_engineering"/> <input type="hidden" name="wpda_table_name" id="wpda_table_name_re" value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/> <input type="hidden" name="wpda_schema_name" id="wpda_schema_name_re" value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/> <input type="hidden" id="design_mode_re" name="design_mode_re"/> <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/> <input type="submit" class="button button-primary" value="Start Reverse Engineering" onclick="return pre_submit_re()" > <a href="javascript:void(0)" onclick="jQuery('#wpda_reverse_engineering').hide()" class="button"><?php echo __( 'Dismiss' ); ?></a> </form> </div> </div> <?php } if ( $this->table_exists ) { // Add reconcile form. ?> <div style="display:none;"> <form id="wpda_reconcile_form" action="?page=<?php echo esc_attr( $this->page ); ?>" method="post"> <input type="hidden" name="action" value="edit"/> <input type="hidden" name="action2" value="wpda_reconcile"/> <input type="hidden" name="wpda_table_name_re" value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/> <input type="hidden" name="wpda_schema_name_re" value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/> <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/> </form> </div> <?php } ?> <div style="text-align:right"> <a href="https://wpdataaccess.com/docs/data-designer/data-designer-getting-started/" target="_blank" style="text-decoration:none"> > What is the difference between a table design and a database table? </a> </div> <div> <div style="display:none;"> <form id="show_create_table_form" action="?page=<?php echo esc_attr( $this->page ); ?>" method="post"> <input type="hidden" name="action" value="edit"/> <input type="hidden" name="action2" value="show_create_table_script"/> <input type="hidden" name="wpda_table_name" value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/> <input type="hidden" name="wpda_schema_name" value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/> <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/> </form> <form id="show_alter_table_form" action="?page=<?php echo esc_attr( $this->page ); ?>" method="post"> <input type="hidden" name="action" value="edit"/> <input type="hidden" name="action2" value="show_alter_table_script"/> <input type="hidden" name="wpda_table_name" value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/> <input type="hidden" name="wpda_schema_name" value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/> <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/> </form> </div> <form id="design_table_form" action="?page=<?php echo esc_attr( $this->page ); ?>" autocomplete="off" method="post" onsubmit="return pre_submit()"> <fieldset class="wpda_fieldset"> <legend> <span> <?php echo __( 'Table definition', 'wp-data-access' ); ?> <?php if ( '' !== $this->wpda_schema_name && '' !== $this->wpda_table_name && null !== $this->wpda_table_design ) { $table_structure = json_decode( json_encode( $this->wpda_table_design ), true ); if ( isset( $table_structure['table'] ) ) { $column_names = array_column( (array) $table_structure['table'], 'column_name' ); //phpcs:ignore - 8.1 proof } else { $column_names = array(); } // Validate schema, table and column names $warning = WPDA::validate_names( $this->wpda_schema_name, $this->wpda_table_name, $column_names ); if ( '' !== $warning ) { echo $warning; // phpcs:ignore WordPress.Security.EscapeOutput } } ?> </span> </legend> <table class="wpda-table-structure"> <thead> <tr> <td class="wpda-table-structure-first-column"> <label for "wpda_schema_name"><?php echo __( 'Database' ); ?> </label> </td> <td> <select name="wpda_schema_name" id="wpda_schema_name" style="width:100%;max-width:100%;"> <?php global $wpdb; foreach ( $this->databases as $database ) { if ( null === $this->wpda_schema_name || '' === $this->wpda_schema_name ) { $dbs = WPDA::get_user_default_scheme(); } else { $dbs = $this->wpda_schema_name; } $selected = $dbs === $database['schema_name'] ? 'selected' : ''; if ( $wpdb->dbname === $database['schema_name'] ) { $database_printed = "WordPress database ({$database['schema_name']})"; } else { $database_printed = $database['schema_name']; } echo "<option value='{$database['schema_name']}' $selected>{$database_printed}</option>"; // phpcs:ignore WordPress.Security.EscapeOutput } ?> </select> </td> <td colspan="4"> </td> <td colspan="4" class="wpda-table-structure-last-column"> <span style="float:right;"> <label> <input type="radio" name="design_mode" value="basic" class="design_mode" onclick="return switch_mode(event)" <?php echo 'basic' === $this->design_mode ? 'checked' : ''; ?> > <?php echo __( 'Basic Design Mode' ); ?> </label> <label> <input type="radio" name="design_mode" value="advanced" class="design_mode" onclick="return switch_mode(event)" <?php echo 'advanced' === $this->design_mode ? 'checked' : ''; ?> > <?php echo __( 'Advanced Design Mode' ); ?> </label> </span> </td> </tr> <tr> <td class="wpda-table-structure-first-column"> <label for "wpda_table_name"><?php echo __( 'Table name' ); ?> </label> </td> <td> <input type="text" name="wpda_table_name" id="wpda_table_name" maxlength="64" style="width:100%;max-width:100%;" value="<?php echo esc_attr( $this->wpda_table_name ); ?>" onchange="jQuery('#wpda_table_name_re').val(jQuery(this).val())" class="wpda_mysql_names wpda_view_table" /> </td> <td colspan="4"> <?php if ( $this->table_exists ) { if ( $this->is_wp_table ) { ?> <span style="vertical-align:-webkit-baseline-middle; cursor:pointer;" title="<?php echo __( 'You cannot use a WordPress table name', 'wp-data-access' ); ?>" class="dashicons dashicons-flag wpda_tooltip"> </span> <?php } else { ?> <i title="<?php echo __( 'A table with this name already exists in the database', 'wp-data-access' ); ?>" class="fas fa-info-circle pointer wpda_tooltip" style="padding: 0 5px; font-size: 24px; line-height: 30px"></i> <?php } ?> <a href="javascript:void(0)" id="reconcile_button" onclick="if (confirm('<?php echo __( 'Reconcile table? Your current modifications will be lost!' ); ?>')) { jQuery('#wpda_reconcile_form').submit(); }" class="button wpda_tooltip" title="Update table design from database table (overwrites current design)"> <i class="fas fa-redo wpda_icon_on_button"></i> <?php echo __( 'Reconcile' ); ?> </a> <?php } else { $title = __( 'New table', 'wp-data-access' ); ?> <span style="vertical-align:-webkit-baseline-middle; cursor:pointer;" title="<?php echo $title; // phpcs:ignore WordPress.Security.EscapeOutput ?>" class="dashicons dashicons-warning wpda_tooltip"> </span> <?php } ?> <input type="hidden" name="wpda_table_name_original" value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/> <input type="hidden" name="wpda_schema_name_original" value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/> <?php if ( ! $this->wpda_table_design ) { ?> <a href="javascript:void(0)" onclick="jQuery('#wpda_reverse_engineering').show()" class="button wpda_view_table wpda_tooltip" title="<?php echo __( 'Load table from database' ); ?>" > <?php echo __( 'Reverse engineering' ); ?> </a> <?php } ?> </td> <td colspan="4" class="wpda-table-structure-last-column"> </td> </tr> <?php if ( 'advanced' === $this->design_mode ) { ?> <tr> <td class="wpda-table-structure-first-column"> <label for "engine"><?php echo __( 'Engine' ); ?> </label> </td> <td> <select name="engine" id="engine" style="width:100%;max-width:100%;" class="wpda_view_table"> <?php $engines = WPDA_Dictionary_Lists::get_engines(); $engine_saved = isset( $this->wpda_table_design->engine ) ? $this->wpda_table_design->engine : ''; foreach ( $engines as $engine ) { $selected_tag = ''; if ( '' === $engine_saved ) { if ( 'DEFAULT' === $engine['support'] ) { $selected_tag = 'selected'; } } else { if ( $engine_saved === $engine['engine'] ) { $selected_tag = 'selected'; } } ?> <option value="<?php echo esc_attr( $engine['engine'] ); ?>" <?php echo esc_attr( $selected_tag ); ?>> <?php echo esc_attr( $engine['engine'] ); ?> </option> <?php } ?> </select> </td> <td colspan="8" class="wpda-table-structure-last-column"> <span style="float:right;"> <a id="button_show_create_table" href="javascript:void(0)" class="button button-secondary wpda_tooltip" title="Generates a create table script from table and index design." onclick="jQuery('#show_create_table_form').submit();"> <i class="fas fa-code wpda_icon_on_button"></i> <?php echo __( 'Show CREATE TABLE script' ); ?> </a> <a id="button_show_alter_table" href="javascript:void(0)" class="button button-secondary wpda_tooltip" title="Generates a alter table script from table and index design." onclick="jQuery('#show_alter_table_form').submit();"> <i class="fas fa-code wpda_icon_on_button"></i> <?php echo __( 'Show ALTER TABLE script' ); ?> </a> </span> </td> </tr> <tr> <td class="wpda-table-structure-first-column"> <label for "collation"><?php echo __( 'Collation' ); ?> </label> </td> <td> <select name="collation" id="collation" style="width:100%;max-width:100%;" class="wpda_view_table"> <?php $character_set_name = ''; $default_collation = WPDA_Dictionary_Lists::get_default_collation(); $collations = WPDA_Dictionary_Lists::get_collations(); $collation_saved = isset( $this->wpda_table_design->collation ) ? $this->wpda_table_design->collation : ''; foreach ( $collations as $collation ) { if ( $character_set_name !== $collation['character_set_name'] ) { if ( '' !== $character_set_name ) { echo '</optgroup>'; } $character_set_name = $collation['character_set_name']; echo '<optgroup label="' . esc_attr( $collation['character_set_name'] ) . '">'; } $selected_tag = ''; if ( '' === $collation_saved ) { if ( $collation['collation_name'] === $default_collation[0]['default_collation_name'] ) { $selected_tag = 'selected'; } } else { if ( $collation_saved === $collation['collation_name'] ) { $selected_tag = 'selected'; } } ?> <option value="<?php echo esc_attr( $collation['collation_name'] ); ?>" <?php echo esc_attr( $selected_tag ); ?>> <?php echo esc_attr( $collation['collation_name'] ); ?> </option> <?php } ?> </select> <?php echo '</optgroup>'; ?> </td> <td colspan="8" class="wpda-table-structure-last-column"> <span style="float:right;"> <label id="checkbox_show_deleted_label"> <input id="checkbox_show_deleted" type="checkbox" onclick="if (jQuery(this).is(':checked')) { jQuery('.wpda_column_deleted').show(); } else { jQuery('.wpda_column_deleted').hide(); }"> <?php echo __( 'Show deleted columns and indexes' ); ?> </label> </span> </td> </tr> <?php } ?> </thead> </table> </fieldset> <br/> <fieldset class="wpda_fieldset"> <legend> <span> <?php echo __( 'Add columns', 'wp-data-access' ); ?> </span> </legend> <table class="wpda-table-structure" style="border-collapse: collapse;"> <thead> <tr> <th class="wpda-table-structure-first-column-move"></th> <th> <?php echo __( 'Column name' ); ?> </th> <th> <?php echo __( 'Column type' ); ?> </th> <?php if ( 'advanced' === $this->design_mode ) { ?> <th> <?php echo __( 'Type attribute' ); ?> </th> <?php } ?> <th style="min-width:60px;"> <?php echo __( 'Key?' ); ?> </th> <th style="min-width:90px;"> <?php echo __( 'Mandatory?' ); ?> </th> <th> <?php echo __( 'Max length' ); ?> </th> <th> <?php echo __( 'Extra' ); ?> <i title="Possible values: auto_increment on update current_timestamp (for column types: timestamp and datetime) virtual generated virtual stored default_generated stored generated Or combined: default_generated on update current_timestamp" class="fas fa-circle-question pointer wpda_tooltip"></i> </th> <th> <?php echo __( 'Default value' ); ?> </th> <th> <?php echo __( 'List values' ); ?> </th> <th class="wpda-table-structure-last-column"> <a href="javascript:void(0)" onclick="add_row('<?php echo esc_attr( $this->design_mode ); ?>')" style="vertical-align:-webkit-baseline-middle;" class="dashicons dashicons-plus wpda_view_table wpda_tooltip" title="Add new column to table design" ></a> </th> </tr> </thead> <tbody id="wpda_table_structure"></tbody> <tfoot> <tr> <td colspan="<?php echo 'basic' === $this->design_mode ? '9' : '10'; ?>"> <input type="hidden" name="submitted_changes" value="table"/> <input type="hidden" name="action" value="edit"/> <input type="hidden" name="action2" value="<?php echo esc_attr( $this->action2 ); ?>" /> <?php if ( 'basic' === $this->design_mode ) { ?> <input type="hidden" name="engine" id="engine" value="<?php echo isset( $this->wpda_table_design->engine ) ? esc_attr( $this->wpda_table_design->engine ) : ''; ?>" /> <input type="hidden" name="collation" id="collation" value="<?php echo isset( $this->wpda_table_design->collation ) ? esc_attr( $this->wpda_table_design->collation ) : ''; ?>" /> <?php } ?> <a href="javascript:void(0)" title="Creates database table from design. Does not create indexes." class="button wpda_tooltip wpda_view <?php if ( $this->table_exists ) { echo ' disabled'; } ?> " onclick="if ( confirm('Create database table `<?php echo WPDA::remove_backticks( $this->wpda_schema_name ); // phpcs:ignore WordPress.Security.EscapeOutput ?>`.`<?php echo WPDA::remove_backticks( $this->wpda_table_name ); // phpcs:ignore WordPress.Security.EscapeOutput ?>`?\nDoes not create indexes!') ) { jQuery('#wpda_create_table_form').submit(); }" <?php if ( $this->table_exists || 'new' === strtolower( $this->action2 ) ) { echo ' readonly disabled'; } ?> > <i class="fas fa-check wpda_icon_on_button"></i> <?php echo __( 'CREATE TABLE', 'wp-data-access' ); ?> </a> <a id="button_alter_table" href="javascript:void(0)" class="button wpda_view wpda_tooltip" title="Writes design changes to database table and indexes." onclick="if ( confirm('Alter database table `<?php echo WPDA::remove_backticks( $this->wpda_schema_name ); // phpcs:ignore WordPress.Security.EscapeOutput ?>`.`<?php echo WPDA::remove_backticks( $this->wpda_table_name ); // phpcs:ignore WordPress.Security.EscapeOutput ?>`?\nAlters modified indexes as well!') ) { jQuery('#wpda_alter_table_form').submit(); }" > <i class="fas fa-redo wpda_icon_on_button"></i> <?php echo __( 'ALTER TABLE', 'wp-data-access' ); ?> </a> <a href="javascript:void(0)" title="This action drops your database table! Not your table design... This cannot be undone." class="button wpda_tooltip wpda_view <?php if ( ! $this->table_exists ) { echo ' disabled'; } ?> " onclick="if ( confirm('Drop database table `<?php echo WPDA::remove_backticks( $this->wpda_schema_name ); // phpcs:ignore WordPress.Security.EscapeOutput ?>`.`<?php echo WPDA::remove_backticks( $this->wpda_table_name ); // phpcs:ignore WordPress.Security.EscapeOutput ?>`?\nTable design will not be deleted!') ) { jQuery('#wpda_drop_table_form').submit(); }" <?php if ( ! $this->table_exists ) { echo ' readonly disabled'; } ?> > <i class="fas fa-trash wpda_icon_on_button"></i> <?php echo __( 'DROP TABLE', 'wp-data-access' ); ?> </a> <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/> <button type="submit" class="button button-primary wpda_view_table wpda_tooltip" title="This does NOT create the table! Is just saves your table design..." > <i class="fas fa-check wpda_icon_on_button"></i> Save Table Design </button> </td> </tr> </tfoot> </table> </fieldset> </form> <form id="wpda_reset_form" action="?page=<?php echo esc_attr( $this->page ); ?>" method="post"> <input type="hidden" name="action" value="edit"/> <?php if ( 'new' !== $this->action2 ) { ?> <input type="hidden" name="wpda_table_name" value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/> <input type="hidden" name="wpda_schema_name" value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/> <?php } ?> <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/> </form> <form id="switch_mode_form" method="post" action="?page=<?php echo esc_attr( $this->page ); ?>"> <input type="hidden" name="design_mode" value="<?php echo 'basic' === $this->design_mode ? 'advanced' : 'basic'; // phpcs:ignore WordPress.Security.EscapeOutput ?>"> <input type="hidden" name="action" value="edit"> <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/> </form> </div> <br/> <div> <form id="design_table_form_indexes" action="?page=<?php echo esc_attr( $this->page ); ?>" method="post"> <fieldset class="wpda_fieldset"> <legend> <span> <?php echo __( 'Add indexes' ); ?> </span> </legend> <table class="wpda-table-structure" style="border-collapse: collapse;"> <thead> <tr> <th style="padding-left:10px;"> <?php echo __( 'Index name' ); ?> </th> <th> <?php echo __( 'Type?' ); ?> </th> <th> <?php echo __( 'Column name(s)' ); ?> </th> <th></th> <th class="wpda-table-structure-last-column" style="width:20px;"> <a href="javascript:void(0)" onclick="add_index()" style="vertical-align: -webkit-baseline-middle;" class="dashicons dashicons-plus wpda_view_index wpda_tooltip" title="Add new index to table design" ></a> </th> </tr> </thead> <tbody id="wpda_index_structure"> </tbody> <tfoot> <tr> <td colspan="5"> <input type="hidden" name="submitted_changes" value="indexes"/> <input type="hidden" name="action" value="edit"/> <input type="hidden" name="action2" value="<?php echo esc_attr( $this->action2 ); ?>"/> <input type="hidden" name="wpda_table_name" value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/> <input type="hidden" name="wpda_table_name_original" value="<?php echo esc_attr( $this->wpda_table_name ); ?>"/> <input type="hidden" name="wpda_schema_name" value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/> <input type="hidden" name="wpda_schema_name_original" value="<?php echo esc_attr( $this->wpda_schema_name ); ?>"/> <a id="wpda_create_index" href="javascript:void(0)" title="Drops all indexes and recreates them." class="button wpda_view wpda_tooltip" onclick="if ( confirm('<?php echo __( 'Drop all deleted indexes and recreate all changed indexes for table' . ' `' . $this->wpda_table_name . '`?' ); ?>') ) { jQuery('#wpda_create_index_form').submit(); }" > <i class="fas fa-check wpda_icon_on_button"></i> <?php echo __( '(RE)CREATE INDEXES', 'wp-data-access' ); ?> </a> <a id="wpda_drop_index" href="javascript:void(0)" title="This action drops your indexes from the database! This cannot be undone." class="button wpda_view wpda_tooltip" onclick="if ( confirm('<?php echo __( 'Drop all indexes for table' . ' `' . $this->wpda_table_name . '`?\n' . __( 'Does not drop primary key indexes and index designs!' ) ); ?>')) { jQuery('#wpda_drop_index_form').submit(); }" > <i class="fas fa-trash wpda_icon_on_button"></i> <?php echo __( 'DROP INDEXES', 'wp-data-access' ); ?> </a> <input type='hidden' name='caller' value='<?php echo esc_attr( $this->caller ); ?>'/> <a id="submit_indexes" href="javascript:void(0)" title="Does NOT create indexes! It just saves your index design..." onclick="if (!jQuery(this).attr('disabled')) { jQuery('#design_table_form_indexes').submit(); } else { alert('<?php echo __( 'Save table design changes first!' ); ?>'); }" class="button button-primary wpda_view_index wpda_tooltip"> <i class="fas fa-check wpda_icon_on_button"></i> <?php echo __( 'Save Indexes' ); ?> </a> </td> </tr> </tfoot> </table> </fieldset> </form> </div> <?php if ( null !== $this->create_table_statement && false === $this->create_table_succeeded ) { ?> <br/> <div class="wpda_design_table"> <table class="wpda-table-structure"> <tfoot> <tr> <td> <h3><?php echo __( 'The following CREATE TABLE statement failed' ); ?></h3> <div> <div style="padding:10px; text-align: left; width: fit-content; margin: 0 auto;"> <?php echo $this->create_table_statement; // phpcs:ignore WordPress.Security.EscapeOutput ?> </div> </div> <div> <strong><?php echo $this->wpdb_error; // phpcs:ignore WordPress.Security.EscapeOutput ?></strong> </div> </td> </tr> </tfoot> </table> </div> <?php } if ( null !== $this->create_index_failed && 0 < count( $this->create_index_failed ) ) {//phpcs:ignore - 8.1 proof ?> <br/> <div class="wpda_design_table"> <table class="wpda-table-structure"> <tfoot> <tr> <td> <h3><?php echo __( 'The following CREATE INDEX statement(s) failed' ); ?></h3> <div> <div style="padding:10px; text-align: left; width: fit-content; margin: 0 auto;"> <?php foreach ( $this->create_index_failed as $index_failed ) { echo "$index_failed<br/>"; // phpcs:ignore WordPress.Security.EscapeOutput } ?> </div> </div> </td> </tr> </tfoot> </table> </div> <?php } if ( null !== $this->create_table_statement && false === $this->alter_table_succeeded ) { ?> <br/> <div class="wpda_design_table"> <table class="wpda-table-structure"> <tfoot> <tr> <td> <h3><?php echo __( 'The following ALTER TABLE statement failed' ); ?></h3> <div> <div style="padding:10px; text-align: left; width: fit-content; margin: 0 auto;"> <?php echo $this->create_table_statement; // phpcs:ignore WordPress.Security.EscapeOutput ?> </div> </div> <div> <strong><?php echo $this->wpdb_error; // phpcs:ignore WordPress.Security.EscapeOutput ?></strong> </div> </td> </tr> </tfoot> </table> </div> <?php } ?> </div> <?php if ( $this->wpda_table_design ) { // Display table design. foreach ( $this->wpda_table_design->table as $design_column ) { if ( ! $this->table_exists ) { // New table cannot be compared with real table. $column_changed = ''; } else { // Check for table structure changes. if ( isset( $this->table_columns[ $design_column->column_name ] ) ) { // Check column arguments. $column_changed = ''; foreach ( $this->real_table as $real_column ) { if ( $real_column->column_name === $design_column->column_name ) { if ( $real_column != $design_column ) { if ( strtolower( $real_column->extra ) === 'auto_increment' && strtolower( $design_column->extra ) === 'auto_increment' ) { $tmp_real_column = clone $real_column; $tmp_design_column = clone $design_column; unset( $tmp_real_column->extra ); unset( $tmp_design_column->extra ); if ( $tmp_real_column != $tmp_design_column ) { $column_changed = 'u'; $this->table_altered = true; } } else { $column_changed = 'u'; $this->table_altered = true; } } } } } else { // New column. $column_changed = 'i'; $this->table_altered = true; } } ?> <script type='text/javascript'> add_row( '<?php echo esc_attr( $this->design_mode ); ?>', true, '<?php echo esc_attr( $design_column->column_name ); ?>', '<?php echo esc_attr( WPDA_Design_Table_Model::datatype2basic( esc_attr( $design_column->data_type ) ) ); ?>', '<?php echo esc_attr( $design_column->data_type ); ?>', '<?php echo esc_attr( $design_column->type_attribute ); ?>', '<?php echo esc_attr( $design_column->key ); ?>', '<?php echo esc_attr( $design_column->mandatory ); ?>', '<?php echo esc_attr( $design_column->max_length ); ?>', '<?php echo esc_attr( $design_column->extra ); ?>', '<?php echo esc_attr( $design_column->default ); ?>', '<?php echo esc_attr( $design_column->list ); ?>', '<?php echo esc_attr( $column_changed ); ?>' ); </script> <?php } if ( $this->table_exists ) { // Check for deleted columns. foreach ( $this->table_columns as $table_column ) { $column_found = false; foreach ( $this->wpda_table_design->table as $design_column ) { if ( $design_column->column_name === $table_column['column_name'] ) { $column_found = true; break; } } if ( ! $column_found ) { break; } } if ( ! $column_found ) { // Add deleted column(s) to form and mark as readonly. foreach ( $this->table_columns as $table_column ) { $column_found = false; foreach ( $this->wpda_table_design->table as $design_column ) { if ( $design_column->column_name === $table_column['column_name'] ) { $column_found = true; break; } } if ( ! $column_found ) { foreach ( $this->real_table as $real_column ) { if ( $real_column->column_name === $table_column['column_name'] ) { ?> <script type='text/javascript'> add_row( '<?php echo esc_attr( $this->design_mode ); ?>', true, '<?php echo esc_attr( $real_column->column_name ); ?>', '<?php echo esc_attr( WPDA_Design_Table_Model::datatype2basic( esc_attr( $real_column->data_type ) ) ); ?>', '<?php echo esc_attr( $real_column->data_type ); ?>', '<?php echo esc_attr( $real_column->type_attribute ); ?>', '<?php echo esc_attr( $real_column->key ); ?>', '<?php echo esc_attr( $real_column->mandatory ); ?>', '<?php echo esc_attr( $real_column->max_length ); ?>', '<?php echo esc_attr( $real_column->extra ); ?>', '<?php echo esc_attr( $real_column->default ); ?>', '<?php echo esc_attr( $real_column->list ); ?>', 'd' ); </script> <?php $this->table_altered = true; $this->deleted_columns_and_indexes = true; } } } } } } // Display indexes. $indexes_found = false; foreach ( $this->wpda_table_design->indexes as $design_index ) { $index_changed = ''; if ( $this->table_exists ) { $index_found = false; // Check if index was changed or new. foreach ( $this->real_indexes as $real_index ) { if ( $design_index->index_name === $real_index['index_name'] ) { $index_found = true; if ( $real_index['unique'] != $design_index->unique || $real_index['column_names'] != $design_index->column_names ) { $index_changed = 'u'; $this->updated_indexes = true; } break; } } if ( ! $index_found ) { $index_changed = 'i'; $this->updated_indexes = true; } } ?> <script type='text/javascript'> add_index( true, '<?php echo esc_attr( $design_index->index_name ); ?>', '<?php echo esc_attr( $design_index->unique ); ?>', '<?php echo esc_attr( $design_index->column_names ); ?>', '<?php echo $index_changed; // phpcs:ignore WordPress.Security.EscapeOutput ?>' ); </script> <?php $indexes_found = true; } if ( $this->table_exists ) { foreach ( $this->real_indexes as $real_index ) { $real_indexes_found = false; foreach ( $this->wpda_table_design->indexes as $design_index ) { if ( $real_index['index_name'] === $design_index->index_name ) { $real_indexes_found = true; break; } } if ( ! $real_indexes_found ) { // Index was dropped. ?> <script type='text/javascript'> add_index( true, '<?php echo esc_attr( $real_index['index_name'] ); ?>', '<?php echo esc_attr( $real_index['unique'] ); ?>', '<?php echo esc_attr( $real_index['column_names'] ); ?>', '<?php echo 'd'; ?>' ); </script> <?php $this->deleted_columns_and_indexes = true; $this->updated_indexes = true; } } } if ( ! $indexes_found ) { ?> <script type='text/javascript'> add_index(true); </script> <?php } } else { // Display one empty row. ?> <script type='text/javascript'> add_row('<?php echo esc_attr( $this->design_mode ); ?>', true); add_index(true); disable_index(); disable_create_buttons(); </script> <?php } ?> <script type='text/javascript'> <?php if ( ! $this->wpda_table_design ) { ?> jQuery('#button_show_create_table').prop("readonly", true).prop("disabled", true).addClass("disabled"); <?php } ?> <?php if ( ! $this->wpda_table_design || ! $this->table_altered ) { if ( ! $this->updated_indexes ) { ?> jQuery("#button_show_alter_table").prop("readonly", true).prop("disabled", true).addClass("disabled"); jQuery("#button_alter_table").prop("readonly", true).prop("disabled", true).addClass("disabled"); <?php } } ?> <?php if ( ! $this->updated_indexes ) { ?> jQuery('#wpda_create_index').prop("readonly", true).prop("disabled", true).addClass("disabled"); <?php } ?> <?php if ( ! $this->real_indexes ) { ?> jQuery('#wpda_drop_index').prop("readonly", true).prop("disabled", true).addClass("disabled"); <?php } ?> </script> <?php if ( 'view' === $this->action ) { ?> <script type='text/javascript'> disable_page(); </script> <?php } if ( $this->is_wp_table ) { // WP table names are not allowed: disable create table button. ?> <script type='text/javascript'> disable_page(); </script> <?php } // Save all table names in array table_name check. ?> <script type='text/javascript'> var wpda_db_table_name = []; <?php $designer_table_list = WPDA_Design_Table_Model::get_designer_table_list(); foreach ( $designer_table_list as $key => $value ) { echo 'wpda_db_table_name["' . esc_attr( $value['wpda_table_name'] ) . '"]=true;'; } if ( ! $this->deleted_columns_and_indexes ) { echo "jQuery('#checkbox_show_deleted_label').addClass('label_disabled');"; echo "jQuery('#checkbox_show_deleted').attr('disabled', true);"; } ?> </script> <?php } /** * Drop all indexes from database * * @since 2.0.14 */ protected function drop_indexes() { foreach ( $this->real_indexes as $real_index ) { $this->drop_index( $real_index['index_name'] ); } } /** * Drop a specific index from database * * @param string $index_name Name of index to be dropped * * @since 2.0.14 */ protected function drop_index( $index_name ) { $wpdadb = WPDADB::get_db_connection( $this->wpda_schema_name ); if ( null === $wpdadb ) { wp_die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->wpda_schema_name ) ) ); } $suppress = $wpdadb->suppress_errors( true ); // Index is deleted from table design: drop index $drop_index_statement = 'DROP INDEX `' . str_replace( '`', '', $index_name ) . "` ON `{$this->wpda_table_name}`"; if ( $wpdadb->query( $drop_index_statement ) ) { $msg = new WPDA_Message_Box( array( 'message_text' => sprintf( __( 'Index `%s` dropped', 'wp-data-access' ), esc_attr( $index_name) ), ) ); $msg->box(); } else { $msg = new WPDA_Message_Box( array( 'message_text' => __( 'DROP INDEX failed', 'wp-data-access' ), 'message_type' => 'error', 'message_is_dismissible' => false, ) ); $msg->box(); $this->create_index_failed[] = $drop_index_statement; } $wpdadb->suppress_errors( $suppress ); } /** * Create indexes from design * * @since 2.0.14 */ protected function create_index() { $wpdadb = WPDADB::get_db_connection( $this->wpda_schema_name ); if ( null === $wpdadb ) { wp_die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->wpda_schema_name ) ) ); } $suppress = $wpdadb->suppress_errors( true ); if ( 'show_create_table_script' !== $this->action2_posted ) { $this->drop_indexes(); } // Recreate indexes. foreach ( $this->wpda_table_design->indexes as $index ) { if ( '' === $index->index_name || '' === $index->column_names ) { continue; } $unique = ''; if ( 'Yes' === $index->unique ) { $unique = 'UNIQUE'; } elseif ( 'FULLTEXT' === $index->unique ) { if ( 'on' === $this->fulltext_support ) { $unique = 'FULLTEXT'; } else { $unique = ''; } } $column_names_array = explode( ',', ( string ) $index->column_names );//phpcs:ignore - 8.1 proof $column_names = '`' . implode( '`,`', $column_names_array ) . '`'; $create_index_statement = "CREATE $unique INDEX `" . str_replace( '`', '', $index->index_name ) . "` ON `{$this->wpda_table_name}` ($column_names)"; $this->create_index_statement .= $create_index_statement . ';' . self::NEW_LINE; if ( 'show_create_table_script' === $this->action2_posted ) { continue; } if ( $wpdadb->query( $create_index_statement ) ) { $msg = new WPDA_Message_Box( array( 'message_text' => sprintf( __( 'Index `%s` created', 'wp-data-access' ), $index->index_name ), ) ); $msg->box(); } else { $msg = new WPDA_Message_Box( array( 'message_text' => __( 'CREATE INDEX failed', 'wp-data-access' ), 'message_type' => 'error', 'message_is_dismissible' => false, ) ); $msg->box(); $this->create_index_failed[] = $create_index_statement; } } $wpdadb->suppress_errors( $suppress ); } } }