<?php /** * Suppress "error - 0 - No summary was found for this file" on phpdoc generation * * @package WPDataAccess\List_Table */ namespace WPDataAccess\List_Table; use WP_Data_Access_Admin; use WPDataAccess\Connection\WPDADB; use WPDataAccess\Data_Dictionary\WPDA_Dictionary_Access; use WPDataAccess\Data_Dictionary\WPDA_Dictionary_Exist; use WPDataAccess\Data_Dictionary\WPDA_Dictionary_Lists; use WPDataAccess\Data_Dictionary\WPDA_List_Columns_Cache; use WPDataAccess\Plugin_Table_Models\WPDA_Media_Model; use WPDataAccess\Plugin_Table_Models\WPDA_Table_Settings_Model; use WPDataAccess\Plugin_Table_Models\WPDA_User_Menus_Model; use WPDataAccess\Utilities\WPDA_Favourites; use WPDataAccess\Utilities\WPDA_Import_Multi; use WPDataAccess\Utilities\WPDA_Message_Box; use WPDataAccess\WPDA; /** * Class WPDA_List_Table_Menu * * This class implements the Data Explorer shown in the plugin menu. * * WPDA_List_Table_Menu extends WPDA_List_Table. Although both list tables basically offer the same functionality * WPDA_List_Table_Menu selects data from MySQL view 'information_schema.tables', where WPDA_List_Table selects * data from tables that are located in the WordPress database schema. The view that serves as the 'base table' * for WPDA_List_Table_Menu is not updatable. A data entry form is therefore not available for WPDA_List_Table_Menu. * * Export functionality word WPDA_List_Table_Menu differs from WPDA_List_Table as well. WPDA_List_Table_Menu allows * to export single tables, as well as multiple tables at once as s bulk action. * * When the user clicks on 'view', a list table for the selected table of view is shown. * * @author Peter Schulz * @since 1.0.0 */ class WPDA_List_Table_Menu extends WPDA_List_Table { const LOADING = 'Loading...'; const WPDACHK = 'wp-data-access-premium-data-services'; /** * Holds tables marked as favourite * * @var array|null */ protected $favourites = null; /** * Show, hide or empty * * @var string|null */ protected $wpda_main_favourites = null; /** * Indicates whether innodb_file_per_table is enabled or disabled * * @var bool */ protected $innodb_file_per_table = true; /** * Can user create database * * @var bool */ protected $user_can_create_db = false; /** * Hint button create db * * @var string */ protected $user_create_db_hint = ''; /** * Can user drop database * * @var bool */ protected $user_can_drop_db = false; /** * Hint button drop db * * @var string */ protected $user_drop_db_hint = ''; /** * Used to switch to another schema (after create/drop db) * * @var null */ protected $switch_schema_name = null; /** * WPDA_List_Table_Menu constructor * * Constructor calls constructor of WPDA_List_Table. Before calling constructor of WPDA_List_Table the * table name is set to {@see WPDA_List_Table::LIST_BASE_TABLE} which gives us the base table for the data * explorer. * * Column headers and columns queried are defined hardcoded as this class handles only one base table and it's * columns (table specific implementation). * * @param array $args See {@see WPDA_List_Table::__construct()}. * * @see WPDA_List_Table * * @since 1.0.0 */ public function __construct( $args = array() ) { global $wpdb; $args['table_name'] = WPDA_List_Table::LIST_BASE_TABLE; // Add column labels. $args['column_headers'] = self::column_headers_labels(); $args['title'] = 'Data Explorer'; $args['subtitle'] = ''; // Define an empty subtitle to prevent WPDA_List_Table from adding one. if ( isset( $_REQUEST['action'] ) ) { // Process create, drop and edit table actions before calling parent constructor to allow to redirect to // another database. if ( 'create_db' === $_REQUEST['action'] ) { $this->process_bulk_action_create_db(); } elseif ( 'drop_db' === $_REQUEST['action'] ) { $this->process_bulk_action_drop_db(); } elseif ( 'edit_db' === $_REQUEST['action'] ) { $this->process_bulk_action_edit_db(); } } parent::__construct( $args ); $this->set_columns_queried( array( 'table_name AS table_name', 'if (find_in_set(table_name,\'' . implode( ',', WPDA::get_wp_tables() ) . '\') , \'' . WPDA::get_table_type_text( WPDA::TABLE_TYPE_WP ) . '\' , if (find_in_set(table_name,\'' . implode( ',', WPDA::get_wpda_tables() ) . '\') , \'' . WPDA::get_table_type_text( WPDA::TABLE_TYPE_WPDA ) . '\' , lower(table_type) ) ) AS table_type', 'create_time AS create_time', 'table_rows AS table_rows', 'auto_increment AS auto_increment', 'engine AS engine', 'data_length+index_length AS total_size', 'data_length AS data_size', 'index_length AS index_size', 'data_free AS overhead', 'table_collation AS table_collation', 'table_type AS table_type_db' ) ); $this->favourites = get_option( WPDA_Favourites::FAVOURITES_OPTION_NAME ); if ( null === $this->switch_schema_name ) { $this->schema_name = $this->get_schema_name(); } else { $this->schema_name = $this->switch_schema_name; } $this->wpda_main_favourites = $this->get_favourites(); // Instantiate WPDA_Import. $this->wpda_import = new WPDA_Import_Multi("?page={$this->page}", $this->schema_name); $wpdadb = WPDADB::get_db_connection( $this->schema_name ); if ( null === $wpdadb ) { wp_die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) ); } $result = $wpdadb->get_row( "show session variables like 'innodb_file_per_table'" ); if ( !empty( $result ) ) { $this->innodb_file_per_table = 'ON' === $result->Value; } // Can user create/drop databases? $this->user_can_create_db = WPDA_Dictionary_Access::can_create_db(); if ( $this->user_can_create_db ) { $this->user_create_db_hint = __( 'Create local database/add remote database connection', 'wp-data-access' ); } else { $this->user_create_db_hint = __( 'Add remote database connection (not authorized to create local database)', 'wp-data-access' ); } $this->user_can_drop_db = WPDA_Dictionary_Access::can_drop_db(); if ( $this->user_can_drop_db ) { if ( $wpdb->dbname === $this->schema_name ) { $this->user_can_drop_db = false; $this->user_drop_db_hint = __( 'Cannot drop WordPress database', 'wp-data-access' ); } elseif ( 'sys' === $this->schema_name || 'mysql' === $this->schema_name || 'information_schema' === $this->schema_name || 'performance_schema' === $this->schema_name || '' === $this->schema_name ) { $this->user_can_drop_db = false; $this->user_drop_db_hint = __( 'Cannot drop MySQL database', 'wp-data-access' ); } else { if ( 'rdb:' === substr( $this->schema_name, 0, 4 ) ) { $this->user_drop_db_hint = __( "Delete remote database connection.\nDoes not drop the database! Just deletes the connection definition.", 'wp-data-access' ); } else { $this->user_drop_db_hint = __( 'Drop selected database', 'wp-data-access' ); } } } else { if ( 'rdb:' === substr( $this->schema_name, 0, 4 ) ) { $this->user_can_drop_db = true; $this->user_drop_db_hint = __( "Delete remote database connection.\nDoes not drop the database! Just deletes the connection definition.", 'wp-data-access' ); } else { $this->user_drop_db_hint = __( 'Cannot drop selected database [not authorized]', 'wp-data-access' ); } } } /** * Overwrite method to add structure to the row below. * * @param object $item Item info. * * @since 1.5.0 */ public function single_row( $item ) { list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info(); //phpcs:ignore - 8.1 proof $columns_not_shown = count( (array) $hidden ); //phpcs:ignore - 8.1 proof just to be sure echo '<tr id="rownum_' . esc_attr( self::$list_number ) . '">'; $this->single_row_columns( $item ); echo '</tr>'; if ( $this->bulk_actions_enabled ) { echo '<tr style="height:0 !important; padding: 0 !important;"><td id="rownum_' . esc_attr( self::$list_number ) . '_x1" colspan="' . esc_attr( 13 - $columns_not_shown ) . '" style="padding:0 !important;"></tr>'; // Fake! Maintain odd/even colors. echo '<tr style="padding: 0 !important;"><td style="padding: 0 !important;"></td><td id="rownum_' . esc_attr( self::$list_number ) . '_x2" colspan="' . esc_attr( 12 - $columns_not_shown ) . '" style="padding:0 10px 10px 0 !important;">'; } else { echo '<tr style="height:0 !important; padding: 0 !important;"><td id="rownum_' . esc_attr( self::$list_number ) . '_x1" colspan="' . esc_attr( 12 - $columns_not_shown ) . '" style="padding:0 !important;"></tr>'; // Fake! Maintain odd/even colors. echo '<tr style="padding: 0 !important;"><td id="rownum_' . esc_attr( self::$list_number ) . '_x2" colspan="' . esc_attr( 12 - $columns_not_shown ) . '" style="padding:0 10px 10px 10px !important;">'; } echo '<div id="wpda_admin_menu_actions_' . esc_attr( self::$list_number - 1 ) . '" style="width:100%;display:none;padding-right:20px !important;">' . esc_attr( self::LOADING ) . '</div>'; echo '</td></tr>'; } /** * Override column_default * * We need to override this method as our table is in fact a view and has no real columns: * $this->wpda_list_columns->get_table_columns() returns no results * * We'll use jquery to write html forms to a container to make them accessible inside our list table. * See WPDA_List_Table->column_default for further explanation. * * @param array $item Item info. * @param string $column_name Column name. * * @return mixed Actions for the current row. * @see WPDA_List_Table::column_default() * * @since 1.0.0 */ public function column_default( $item, $column_name ) { if ( 'icons' === $column_name ) { // Dummy column where icons are shown (favourites and row admin menu). $table_name = $item['table_name']; $favourite_class = 'dashicons-star-empty'; $favourite_title = 'Add to favourites'; if ( null !== $this->favourites && false !== $this->favourites ) { $favourites_array = $this->favourites; if ( isset( $favourites_array[$table_name] ) ) { $favourite_class = 'dashicons-star-filled'; $favourite_title = 'Remove from favourites'; } } $favourites_menu = "<a href=\"javascript:void( 0 )\" class=\"wpda_tooltip\" title=\"{$favourite_title}\" onclick=\"wpda_list_table_favourite( '{$this->schema_name}', '{$table_name}' )\">\n\t\t\t\t\t\t<span id=\"span_favourites_{$table_name}\" class=\"dashicons {$favourite_class}\"></span>\n\t\t\t\t\t</a>"; return $favourites_menu; } if ( 'table_rows' === $column_name ) { if ( is_scalar( $item['engine'] ) && is_scalar( $item['table_type'] ) && ('federated' === strtolower( $item['engine'] ) || 'connect' === strtolower( $item['engine'] ) || 'innodb' === strtolower( $item['engine'] ) || stripos( strtolower( $item['table_type'] ), 'view' ) !== false) ) { $settings_db = WPDA_Table_Settings_Model::query( $item['table_name'], $this->schema_name ); if ( isset( $settings_db[0]['wpda_table_settings'] ) && '' !== $settings_db[0]['wpda_table_settings'] ) { $settings = json_decode( $settings_db[0]['wpda_table_settings'] ); } else { $settings = (object) null; } $row_count_estimate = WPDA::get_row_count_estimate( $this->schema_name, $item['table_name'], $settings ); if ( !$row_count_estimate['do_real_count'] ) { if ( $row_count_estimate['is_estimate'] ) { return stripslashes( '~' . $row_count_estimate['row_count'] ); } else { return stripslashes( $row_count_estimate['row_count'] ); } } else { return stripslashes( $this->count_rows( $item['table_name'] ) ); } } else { return stripslashes( (string) $item[$column_name] ); } } if ( 'total_size' === $column_name || 'data_size' === $column_name || 'index_size' === $column_name ) { if ( '' === stripslashes( (string) $item[$column_name] ) ) { return stripslashes( (string) $item[$column_name] ); } else { if ( $item[$column_name] / (1024 * 1024) > 1 ) { return number_format( stripslashes( $item[$column_name] / (1024 * 1024) ), 2, '.', ',' ) . ' MB'; } elseif ( $item[$column_name] / 1024 > 1 ) { return number_format( $item[$column_name] / 1024, 2, '.', ',' ) . ' KB'; } return number_format( stripslashes( $item[$column_name] ), 2, '.', ',' ) . ' bytes'; } } if ( 'overhead' === $column_name ) { if ( 'InnoDB' === $item['engine'] && !$this->innodb_file_per_table ) { return '-'; } else { if ( '' === stripslashes( (string) $item[$column_name] ) ) { return ''; } else { if ( $item[$column_name] > 0 && $item['data_size'] > 0 && $item[$column_name] / $item['data_size'] > 0.2 ) { $msg = __( "Fragmentation for this table is high.\nConsider: Manage>Actions>Optimize", 'wp-data-access' ); $approx = " <span style='font-size:15px;' class='dashicons dashicons-warning wpda_tooltip' title='{$msg}' style'cursor:pointer;'></span>"; } else { $approx = ''; } if ( $item[$column_name] / (1024 * 1024) > 1 ) { return number_format( stripslashes( $item[$column_name] / (1024 * 1024) ), 2, '.', ',' ) . ' MB' . $approx; } elseif ( $item[$column_name] / 1024 > 1 ) { return number_format( stripslashes( $item[$column_name] / 1024 ), 2, '.', ',' ) . ' KB' . $approx; } return number_format( stripslashes( $item[$column_name] ), 2, '.', ',' ) . ' bytes' . $approx; } } } if ( 'table_name' === $column_name ) { // Get table name of the current row (= key). $table_name = $item[$column_name]; $admin_actions = array(); // Array containing admin actions. if ( WPDA::can_manage() ) { // Add manage table/view line. $wp_nonce_action_table_actions = "wpda-actions-{$table_name}"; $wp_nonce_table_actions = wp_create_nonce( $wp_nonce_action_table_actions ); $table_actions_title = sprintf( __( 'Table %s settings', 'wp-data-access' ), $table_name ); $actions['wpda_manage'] = "<a href=\"javascript:void( 0 )\" class='wpda_tooltip' title='{$table_actions_title}' onclick=\"wpda_show_table_actions( '{$this->schema_name}', '{$table_name}', '" . self::$list_number . "', '{$wp_nonce_table_actions}', '{$item['table_type_db']}', '" . self::LOADING . "' ); this.blur();\">" . '<i class="fas fa-gears wpda_icon_on_button"></i> ' . __( 'Manage', 'wp-data-access' ) . '</a>'; } self::$list_number++; // Prepare type checking for editing. $check_view_access = 'true'; if ( 'on' === WPDA::get_option( WPDA::OPTION_BE_CONFIRM_VIEW ) ) { if ( 'VIEW' !== strtoupper( $item['table_type_db'] ) && 'SYSTEM VIEW' !== strtoupper( $item['table_type_db'] ) ) { if ( strtolower( WPDA::TABLE_TYPE_WP ) === strtolower( $item['table_type'] ) ) { // WordPress table. $msg = __( 'You are about to edit a WordPress table! Changing this table might result in corrupting the WordPress database. Are you sure you want to continue?', 'wp-data-access' ); } elseif ( strtolower( WPDA::TABLE_TYPE_WPDA ) === strtolower( $item['table_type'] ) ) { // Plugin table. $msg = __( 'You are about to edit a plugin table! Changing this table might result in corrupting the WP Data Access database. Are you sure you want to continue?', 'wp-data-access' ); } else { // User table (other than WordPress or plugin). $msg = __( 'You are about to edit a table of an external application! Changing this table might result in corrupting the external database. Are you sure you want to continue?', 'wp-data-access' ); } $check_view_access = 'confirm(\'' . $msg . '\')'; } } $esc_attr = 'esc_attr'; $form_name = 'explore_' . self::$list_number; $url = "?page={$this->page}"; $form = <<<EOT \t\t\t\t<form id='{$esc_attr( $form_name )}' action='{$esc_attr( $url )}' method='post'> \t\t\t\t\t<input type='hidden' name='wpdaschema_name' value='{$esc_attr( $this->schema_name )}' /> \t\t\t\t\t<input type='hidden' name='table_name' value='{$esc_attr( $table_name )}' /> \t\t\t\t\t<input type='hidden' name='action' value='listtable' /> \t\t\t\t</form> EOT; $explore = str_replace( array("\n", "\r"), '', $form ); ?> <script type='text/javascript'> jQuery("#wpda_invisible_container").append("<?php echo $explore; // phpcs:ignore WordPress.Security.EscapeOutput ?>"); </script> <?php $action_view = ' <i class="fas fa-table-list wpda_icon_on_button"></i> ' . __( 'Explore', 'wp-data-access' ); $table_view_title = sprintf( __( 'Explore %s table', 'wp-data-access' ), $table_name ); $actions['wpda_listtable'] = sprintf( '<a href="javascript:void(0)" title="%s" class="view wpda_tooltip" onclick="if (%s) jQuery(\'#%s\').submit()"> %s </a>', $table_view_title, $check_view_access, $form_name, $action_view ); if ( strtolower( WPDA::TABLE_TYPE_WP ) === strtolower( $item['table_type'] ) ) { ?> <script type='text/javascript'> wpda_bulk.push('<?php echo esc_attr( $table_name ); ?>'); </script> <?php } if ( strtolower( WPDA::TABLE_TYPE_WP ) !== strtolower( $item['table_type'] ) && strtolower( WPDA::TABLE_TYPE_WPDA ) !== strtolower( $item['table_type'] ) ) { // Validate schema, table and column names $warning = WPDA::validate_names( $this->schema_name, $table_name ); } else { $warning = ''; } return sprintf( '%1$s %2$s %3$s', $table_name . $warning, "<span class=\"nobr\">{$this->row_actions( $actions )}</span>", "<span id=\"span_admin_menu_{$table_name}\" class=\"nobr\" style=\"display:none;width:auto;float:clear;\">{$this->row_actions( $admin_actions )}</span>" ); } if ( 'create_time' === $column_name ) { if ( null !== $item[$column_name] ) { return date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), strtotime( $item[$column_name] ) ); } else { return ''; } } return stripslashes( (string) $item[$column_name] ); } /** * Count the number of rows in a table. * * This method is used to get the number of rows in an InnoDB table as the table_rows column of * information_schema.tables only return an estimate. * * @param string $table_name Database table name. * * @return integer|null Number of rows in $table_name. * @since 1.5.1 */ protected function count_rows( $table_name ) { $wpdadb = WPDADB::get_db_connection( $this->schema_name ); if ( null === $wpdadb ) { wp_die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) ); } if ( '' === $this->schema_name ) { $query = "\n\t\t\t\t\tselect count(*)\n\t\t\t\t\tfrom `{$table_name}`\n\t\t\t\t"; } else { $query = "\n\t\t\t\t\tselect count(*)\n\t\t\t\t\tfrom `{$wpdadb->dbname}`.`{$table_name}`\n\t\t\t\t"; } $suppress = $wpdadb->suppress_errors( true ); $count = @$wpdadb->get_var( $query ); // phpcs:ignore Standard.Category.SniffName.ErrorCode if ( !is_numeric( $count ) ) { $count = '<span class="dashicons dashicons-flag wpda_tooltip" style="color:red;padding-left:5px" title="' . str_replace( '"', "'", $wpdadb->last_error ) . '"></span>'; } $wpdadb->suppress_errors( $suppress ); return $count; } /** * Override get_columns * * @return array * @see WPDA_List_Table::get_columns() * * @since 1.0.0 */ public function get_columns() { $columns = array(); if ( $this->bulk_actions_enabled ) { $columns = array( 'cb' => '<input type="checkbox" />', ); } return array_merge( $columns, $this->column_headers ); //phpcs:ignore - 8.1 proof } /** * Override get_sortable_columns() * * Type is not sortable as it is not in the view. * * @return array * @see WPDA_List_Table::get_sortable_columns() * * @since 1.0.0 */ public function get_sortable_columns() { $columns = array(); $columns['table_name'] = array('table_name', false); $columns['table_type'] = array('table_type', false); $columns['engine'] = array('engine', false); $columns['create_time'] = array('create_time', false); $columns['table_rows'] = array('table_rows', false); $columns['auto_increment'] = array('auto_increment', false); $columns['total_size'] = array('total_size', false); $columns['data_size'] = array('data_size', false); $columns['index_size'] = array('index_size', false); $columns['overhead'] = array('overhead', false); $columns['table_collation'] = array('table_collation', false); return $columns; } /** * Override column_cb * * @param array $item Column info. * * @return string * @since 1.0.0 * * @see WPDA_List_Table::column_cb() */ public function column_cb( $item ) { if ( !$this->bulk_actions_enabled ) { // Bulk actions disabled. return ''; } if ( 'VIEW' === $item['table_type_db'] ) { $title = 'title="Performs only drop actions on views"'; $class = 'class="wpda_tooltip"'; } else { $title = ''; $class = ''; } return "<input type='checkbox' name='bulk-selected[]' value='{$item['table_name']}' {$title} {$class} />"; } /** * Override get_bulk_actions * * @return array * @see WPDA_List_Table::get_bulk_actions() * * @since 1.0.0 */ public function get_bulk_actions() { if ( !$this->bulk_actions_enabled ) { // Bulk actions disabled. return ''; } $actions = array(); $actions['bulk-export'] = __( 'Export Table(s)', 'wp-data-access' ); $actions['bulk-drop'] = __( 'Drop Table(s)/View(s) (does not drop WordPress tables)', 'wp-data-access' ); $actions['bulk-truncate'] = __( 'Truncate Table(s) (does not truncate WordPress tables)', 'wp-data-access' ); return $actions; } /** * Override process_bulk_action() * * @since 1.0.0 * * @see WPDA_List_Table::process_bulk_action() */ public function process_bulk_action() { switch ( $this->current_action() ) { case 'bulk-export': $this->process_bulk_action_bulk_export(); break; case 'bulk-drop': $this->process_bulk_action_bulk_drop(); break; case 'bulk-truncate': $this->process_bulk_action_bulk_truncate(); break; case 'drop': $this->process_bulk_action_drop(); break; case 'truncate': $this->process_bulk_action_truncate(); break; case 'rename-table': $this->process_bulk_action_rename_table(); break; case 'copy-table': $this->process_bulk_action_copy_table(); break; case 'optimize-table': $this->process_bulk_action_optimize_table(); break; case 'refresh-table': $this->process_bulk_action_refresh_table(); break; case 'c2wp-table': $this->process_bulk_action_c2wp_table(); break; } } protected function process_bulk_action_refresh_table() { } protected function process_bulk_action_drop_db() { if ( !$this->process_bulk_action_check_wpnonce( 'wpda-drop-db-from-data-explorer-' . WPDA::get_current_user_login(), '_wpnoncedropdb' ) ) { return; } if ( !isset( $_REQUEST['database'] ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Cannot drop database [missing argument]', 'wp-data-access' ) ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); return; } global $wpdb; $database = str_replace( '`', '', sanitize_text_field( wp_unslash( $_REQUEST['database'] ) ) ); // input var okay. if ( 'rdb:' === substr( $database, 0, 4 ) ) { // Delete remote database if ( false === WPDADB::get_remote_database( $database ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Cannot delete remote database connection `%s` [remote database connection not found]', 'wp-data-access' ), $database ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); } else { if ( false === WPDADB::del_remote_database( $database ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Cannot delete remote database connection `%s`', 'wp-data-access' ), $database ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); } else { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Remove database `%s` deleted', 'wp-data-access' ), $database ), )); $msg->box(); $this->switch_schema_name = $wpdb->dbname; } } } else { // Drop local database if ( $wpdb->dbname === $database ) { $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Cannot drop WordPress database', 'wp-data-access' ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); return; } if ( 'sys' === $database || 'mysql' === $database || 'information_schema' === $database || 'performance_schema' === $database || '' === $database ) { $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Cannot drop MySQL database', 'wp-data-access' ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); return; } if ( false === $wpdb->query( $wpdb->prepare( 'drop database `%1s`', // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders array(WPDA::remove_backticks( $database )) ) ) ) { // db call ok; no-cache ok. $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Error dropping database `%s`', 'wp-data-access' ), $database ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); } else { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Database `%s` dropped', 'wp-data-access' ), $database ), )); $msg->box(); $this->switch_schema_name = $wpdb->dbname; } } } protected function process_bulk_action_edit_db() { if ( !$this->process_bulk_action_check_wpnonce( 'wpda-edit-db-from-data-explorer-' . WPDA::get_current_user_login(), '_wpnonceeditdb' ) ) { return; } if ( !isset( $_REQUEST['edit_remote_database'] ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Cannot update remote database connection [missing argument]', 'wp-data-access' ) ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); return; } $database = sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_database'] ) ); // input var okay. $database_old = sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_database_old'] ) ); // input var okay. if ( $database !== $database_old ) { // Update database connection name if ( false === WPDADB::get_remote_database( $database_old ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Cannot update remote database connection [remote database connection not found]', 'wp-data-access' ) ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); return; } } else { // Update database connection information if ( false === WPDADB::get_remote_database( $database ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Cannot update remote database connection [remote database connection not found]', 'wp-data-access' ) ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); return; } } $host = ( isset( $_REQUEST['edit_remote_host'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_host'] ) ) : '' ); // input var okay. $user = ( isset( $_REQUEST['edit_remote_user'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_user'] ) ) : '' ); // input var okay. $passwd = ( isset( $_REQUEST['edit_remote_passwd'] ) ? wp_unslash( $_REQUEST['edit_remote_passwd'] ) : '' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $port = ( isset( $_REQUEST['edit_remote_port'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_port'] ) ) : '' ); // input var okay. $schema = ( isset( $_REQUEST['edit_remote_schema'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_schema'] ) ) : '' ); // input var okay. $ssl = ( isset( $_REQUEST['edit_remote_ssl'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_ssl'] ) ) : 'off' ); // input var okay. $ssl_key = ( isset( $_REQUEST['edit_remote_client_key'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_client_key'] ) ) : '' ); // input var okay. $ssl_cert = ( isset( $_REQUEST['edit_remote_client_certificate'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_client_certificate'] ) ) : '' ); // input var okay. $ssl_ca = ( isset( $_REQUEST['edit_remote_ca_certificate'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_ca_certificate'] ) ) : '' ); // input var okay. $ssl_path = ( isset( $_REQUEST['edit_remote_certificate_path'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_certificate_path'] ) ) : '' ); // input var okay. $ssl_cipher = ( isset( $_REQUEST['edit_remote_specified_cipher'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['edit_remote_specified_cipher'] ) ) : '' ); // input var okay. if ( '' === $database || '' === $host || '' === $user || '' === $schema ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Cannot edit remote database connection [missing arguments]', 'wp-data-access' ) ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); return; } if ( !WPDADB::upd_remote_database( $database, $host, $user, $passwd, $port, $schema, false, $database_old, $ssl, $ssl_key, $ssl_cert, $ssl_ca, $ssl_path, $ssl_cipher ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Cannot update remote database connection `%s`', 'wp-data-access' ), $database ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); } else { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Remote database connection `%s` updated', 'wp-data-access' ), $database ), )); $msg->box(); if ( $database !== $database_old ) { $this->switch_schema_name = $database; } } } protected function process_bulk_action_create_db() { if ( !$this->process_bulk_action_check_wpnonce( 'wpda-create-db-from-data-explorer-' . WPDA::get_current_user_login(), '_wpnoncecreatedb' ) ) { return; } if ( isset( $_REQUEST['database_location'] ) && 'local' === $_REQUEST['database_location'] ) { // Add local database if ( !isset( $_REQUEST['local_database'] ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Cannot create database [missing argument]', 'wp-data-access' ) ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); return; } $database = str_replace( '`', '', sanitize_text_field( wp_unslash( $_REQUEST['local_database'] ) ) ); // input var okay. global $wpdb; if ( false === $wpdb->query( $wpdb->prepare( 'create database `%1s`', // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders array(WPDA::remove_backticks( $database )) ) ) ) { // db call ok; no-cache ok. $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Error creating database `%s`', 'wp-data-access' ), $database ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); } else { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Database `%s` created', 'wp-data-access' ), $database ), )); $msg->box(); $this->switch_schema_name = $database; } } else { // Add remote database $database = ( isset( $_REQUEST['remote_database'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_database'] ) ) : '' ); // input var okay. if ( false !== WPDADB::get_remote_database( $database ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Remote database connection already exists', 'wp-data-access' ) ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); return; } $host = ( isset( $_REQUEST['remote_host'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_host'] ) ) : '' ); // input var okay. $user = ( isset( $_REQUEST['remote_user'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_user'] ) ) : '' ); // input var okay. $passwd = ( isset( $_REQUEST['remote_passwd'] ) ? wp_unslash( $_REQUEST['remote_passwd'] ) : '' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $port = ( isset( $_REQUEST['remote_port'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_port'] ) ) : '' ); // input var okay. $schema = ( isset( $_REQUEST['remote_schema'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_schema'] ) ) : '' ); // input var okay. $ssl = ( isset( $_REQUEST['remote_ssl'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_ssl'] ) ) : 'off' ); // input var okay. $ssl_key = ( isset( $_REQUEST['remote_client_key'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_client_key'] ) ) : '' ); // input var okay. $ssl_cert = ( isset( $_REQUEST['remote_client_certificate'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_client_certificate'] ) ) : '' ); // input var okay. $ssl_ca = ( isset( $_REQUEST['remote_ca_certificate'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_ca_certificate'] ) ) : '' ); // input var okay. $ssl_path = ( isset( $_REQUEST['remote_certificate_path'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_certificate_path'] ) ) : '' ); // input var okay. $ssl_cipher = ( isset( $_REQUEST['remote_specified_cipher'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['remote_specified_cipher'] ) ) : '' ); // input var okay. if ( '' === $database || '' === $host || '' === $user || '' === $schema ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Cannot add remote database connection [missing argument]', 'wp-data-access' ) ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); return; } if ( 'rdb:' === $database ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Invalid database name [enter a valid database name, for example rdb:remotedb]', 'wp-data-access' ) ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); return; } if ( !WPDADB::add_remote_database( $database, $host, $user, $passwd, $port, $schema, $ssl, $ssl_key, $ssl_cert, $ssl_ca, $ssl_path, $ssl_cipher ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Cannot add remote database connection', 'wp-data-access' ) ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); } else { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Remote database connection `%s` added', 'wp-data-access' ), $database ), )); $msg->box(); $this->switch_schema_name = $database; } } } /** * Performs optimize table. */ protected function process_bulk_action_optimize_table() { if ( isset( $_REQUEST['optimize_table_name'] ) ) { $optimize_table_name = str_replace( '`', '', sanitize_text_field( wp_unslash( $_REQUEST['optimize_table_name'] ) ) ); // input var okay. if ( $this->process_bulk_action_check_wpnonce( "wpda-optimize-{$optimize_table_name}", '_wpnonce' ) ) { $dbo_type = $this->get_dbo_type( $optimize_table_name ); if ( false === $dbo_type || 'BASE TABLE' !== $dbo_type ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Cannot optimize `%s`', 'wp-data-access' ), $optimize_table_name ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); } else { // Optimize table. $wpdadb = WPDADB::get_db_connection( $this->schema_name ); if ( null === $wpdadb ) { wp_die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) ); } $wpdadb->query( "optimize table `{$optimize_table_name}`" ); // db call ok; no-cache ok. $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Table `%s` optimized', 'wp-data-access' ), $optimize_table_name ), )); $msg->box(); } } } } /** * Performs rename table/view. * * @since 1.6.6 */ protected function process_bulk_action_rename_table() { // Check arguments. if ( !$this->process_bulk_action_check_action( 'rename_table_name_old', __( 'Missing old table name', 'wp-data-access' ) ) ) { return; } if ( $this->process_bulk_action_check_action( 'rename_table_name_new', __( 'Missing new table name', 'wp-data-access' ) ) ) { // Rename table is not allowed for WordPress tables (double check). $rename_table_name_old = sanitize_text_field( wp_unslash( $_REQUEST['rename_table_name_old'] ) ); // input var okay. $rename_table_name_new = sanitize_text_field( wp_unslash( $_REQUEST['rename_table_name_new'] ) ); // input var okay. $err_txt = ' ' . sprintf( __( '[cannot rename WordPress table `%s`]', 'wp-data-access' ), $rename_table_name_old ); if ( '' === $rename_table_name_old ) { $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Missing old table name value', 'wp-data-access' ), )); $msg->box(); return; } if ( '' === $rename_table_name_new ) { $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Missing new table name value', 'wp-data-access' ), )); $msg->box(); return; } if ( $this->process_bulk_action_check_is_wp_table( $rename_table_name_old, $err_txt ) ) { // Check if table exists. if ( $this->process_bulk_action_check_table_exists( $rename_table_name_old ) ) { // Check if rename is allowed. if ( $this->process_bulk_action_check_wpnonce( "wpda-rename-{$rename_table_name_old}", '_wpnonce' ) ) { $dbo_type = $this->get_dbo_type( $rename_table_name_old ); if ( false === $dbo_type || 'SYSTEM VIEW' === $dbo_type ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Cannot rename `%s`', 'wp-data-access' ), $rename_table_name_old ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); } else { // Rename table/view. if ( false === $this->rename_table( $rename_table_name_old, $rename_table_name_new ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Cannot rename', 'wp-data-access' ) . ' ' . strtolower( $dbo_type ) . ' `' . $rename_table_name_old . '`', 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); } else { $msg = new WPDA_Message_Box(array( 'message_text' => strtoupper( substr( $dbo_type, 0, 1 ) ) . strtolower( substr( $dbo_type, 1 ) ) . ' `' . $rename_table_name_old . '` ' . __( 'renamed to', 'wp-data-access' ) . ' `' . $rename_table_name_new . '` ', )); $msg->box(); } } } } } } } /** * Rename table/view. * * @param string $rename_table_name_old Old table name. * @param string $rename_table_name_new New table name. * * @return false|int * @since 1.6.6 */ protected function rename_table( $rename_table_name_old, $rename_table_name_new ) { if ( WPDA::is_wp_table( $rename_table_name_old ) ) { // Never ever allow renaming a WordPress table! $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Not authorized [cannot rename WordPress tables]', 'wp-data-access' ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); return false; } $wpdadb = WPDADB::get_db_connection( $this->schema_name ); if ( null === $wpdadb ) { wp_die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) ); } return $wpdadb->query( 'rename table `' . str_replace( '`', '', $rename_table_name_old ) . '` to `' . str_replace( '`', '', $rename_table_name_new ) . '`' ); // db call ok; no-cache ok. } /** * Performs copying a table. * * @since 1.6.6 */ protected function process_bulk_action_copy_table() { // Check arguments. if ( !$this->process_bulk_action_check_action( 'copy_table_name_src', __( 'Missing source table name', 'wp-data-access' ) ) ) { return; } if ( $this->process_bulk_action_check_action( 'copy_table_name_dst', __( 'Missing destination table name', 'wp-data-access' ) ) ) { // copy table is not allowed for WordPress tables (double check). $copy_schema_name_src = sanitize_text_field( wp_unslash( $_REQUEST['copy_schema_name_src'] ) ); // input var okay. $copy_table_name_src = sanitize_text_field( wp_unslash( $_REQUEST['copy_table_name_src'] ) ); // input var okay. $copy_schema_name_dst = sanitize_text_field( wp_unslash( $_REQUEST['copy_schema_name_dst'] ) ); // input var okay. $copy_table_name_dst = sanitize_text_field( wp_unslash( $_REQUEST['copy_table_name_dst'] ) ); // input var okay. if ( '' === $copy_schema_name_src ) { $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Missing source schema name', 'wp-data-access' ), )); $msg->box(); return; } if ( '' === $copy_table_name_src ) { $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Missing source table name', 'wp-data-access' ), )); $msg->box(); return; } if ( '' === $copy_schema_name_dst ) { $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Missing destination schema name', 'wp-data-access' ), )); $msg->box(); return; } if ( '' === $copy_table_name_dst ) { $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Missing destination table name', 'wp-data-access' ), )); $msg->box(); return; } // Check if table exists. if ( $this->process_bulk_action_check_table_exists( $copy_table_name_src ) ) { // Check if copy is allowed. if ( $this->process_bulk_action_check_wpnonce( "wpda-copy-{$copy_table_name_src}", '_wpnonce' ) ) { $dbo_type = $this->get_dbo_type( $copy_table_name_src ); if ( false === $dbo_type || 'SYSTEM VIEW' === $dbo_type ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Cannot copy `%s`', 'wp-data-access' ), $copy_table_name_src ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); } else { $include_data = ( isset( $_REQUEST['copy-table-data'] ) ? 'on' : 'off' ); // Copy table/view. if ( false === $this->copy_table( $copy_schema_name_src, $copy_table_name_src, $copy_schema_name_dst, $copy_table_name_dst, $include_data ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Cannot copy', 'wp-data-access' ) . ' ' . strtolower( $dbo_type ) . ' ' . $copy_table_name_src, 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); } else { $msg = new WPDA_Message_Box(array( 'message_text' => strtoupper( substr( $dbo_type, 0, 1 ) ) . strtolower( substr( $dbo_type, 1 ) ) . " `{$copy_schema_name_src}`.`{$copy_table_name_src}` " . __( 'copied to', 'wp-data-access' ) . " `{$copy_schema_name_dst}`.`{$copy_table_name_dst}` ", )); $msg->box(); } } } } } } /** * Copy table. * * @param string $copy_schema_name_src Source schema name. * @param string $copy_table_name_src Source table name. * @param string $copy_schema_name_dst Destination schema name. * @param string $copy_table_name_dst Destination table name. * @param string $include_data 'on' = include data. * * @return false|int * @since 1.6.6 */ protected function copy_table( $copy_schema_name_src, $copy_table_name_src, $copy_schema_name_dst, $copy_table_name_dst, $include_data ) { $copy_schema_name_src = WPDA::remove_backticks( $copy_schema_name_src ); $copy_table_name_src = WPDA::remove_backticks( $copy_table_name_src ); $copy_schema_name_dst = WPDA::remove_backticks( $copy_schema_name_dst ); $copy_table_name_dst = WPDA::remove_backticks( $copy_table_name_dst ); $wpdadb_src = WPDADB::get_db_connection( $copy_schema_name_src ); if ( null === $wpdadb_src ) { return false; } $wpdadb_dst = WPDADB::get_db_connection( $copy_schema_name_dst ); if ( null === $wpdadb_dst ) { return false; } $suppress_wpdadb_src = $wpdadb_src->suppress_errors; $wpdadb_src->suppress_errors = true; $suppress_wpdadb_dst = $wpdadb_dst->suppress_errors; $wpdadb_dst->suppress_errors = true; // Get create table statement from $wpdadb_src. $wpdadb_src->query( "SET sql_mode = 'NO_TABLE_OPTIONS'" ); $query = "show create table `{$copy_table_name_src}`"; $ctcmd = $wpdadb_src->get_results( $query, 'ARRAY_A' ); // phpcs:ignore Standard.Category.SniffName.ErrorCode if ( '' !== $wpdadb_src->last_error || !isset( $ctcmd[0]['Create Table'] ) ) { $wpdadb_src->suppress_errors( $suppress_wpdadb_src ); $wpdadb_dst->suppress_errors( $suppress_wpdadb_dst ); return false; } $create_table_statement = $ctcmd[0]['Create Table']; if ( $copy_table_name_src !== $copy_table_name_dst ) { // Modify create table statement $pos = strpos( $create_table_statement, $copy_table_name_src ); if ( $pos !== false ) { $create_table_statement = substr_replace( $create_table_statement, $copy_table_name_dst, $pos, strlen( $copy_table_name_src ) ); } } // Create new table in $wpdadb_dst $wpdadb_dst->query( $create_table_statement ); if ( '' !== $wpdadb_dst->last_error ) { $wpdadb_src->suppress_errors( $suppress_wpdadb_src ); $wpdadb_dst->suppress_errors( $suppress_wpdadb_dst ); return false; } if ( 'on' !== $include_data ) { return true; } if ( $copy_schema_name_src === $copy_schema_name_dst ) { // No need for buffering if source database === destination database $result = $wpdadb_dst->query( "insert `{$copy_table_name_dst}` select * from `{$copy_table_name_src}`" ); // db call ok; no-cache ok. $wpdadb_src->suppress_errors( $suppress_wpdadb_src ); $wpdadb_dst->suppress_errors( $suppress_wpdadb_dst ); return $result; } // Check if buffering is needed for this table $settings_db = WPDA_Table_Settings_Model::query( $copy_table_name_src, $copy_schema_name_src ); if ( isset( $settings_db[0]['wpda_table_settings'] ) ) { $settings_db_custom = json_decode( $settings_db[0]['wpda_table_settings'] ); if ( isset( $settings_db_custom->table_settings->query_buffer_size ) ) { $query_buffer_size = $settings_db_custom->table_settings->query_buffer_size; } else { $query_buffer_size = 0; } } else { $query_buffer_size = 0; } // Copy table data from $wpdadb to $wpdb $query = "select * from `{$copy_table_name_src}`"; if ( is_numeric( $query_buffer_size ) && $query_buffer_size > 0 ) { set_time_limit( 0 ); ?> <p class="wpda_pds_msg_create"> Copy table `<?php echo esc_attr( $copy_schema_name_src ); ?>`.`<?php echo esc_attr( $copy_table_name_src ); ?>` </p> <p class="wpda_pds_msg_create"> To `<?php echo esc_attr( $copy_schema_name_dst ); ?>`.`<?php echo esc_attr( $copy_table_name_dst ); ?>` </p> <p class="wpda_pds_msg_create"> Hang on... </p> <p class="wpda_pds_msg_create"> Table `<?php echo esc_attr( $copy_table_name_dst ); ?>` created... </p> <p class="wpda_pds_msg_create"> Copying data... </p> <?php ob_flush(); flush(); // Create array for fast column_name based access. $wpda_list_columns = WPDA_List_Columns_Cache::get_list_columns( $copy_schema_name_src, $copy_table_name_src ); $table_columns = $wpda_list_columns->get_table_columns(); $column_data_types = array(); foreach ( $table_columns as $column_value ) { $column_data_types[$column_value['column_name']] = $column_value['data_type']; } $i = 0; $total = 0; $sql = $query . ' limit ' . $query_buffer_size; $rows = $wpdadb_src->get_results( $sql, 'ARRAY_A' ); // phpcs:ignore Standard.Category.SniffName.ErrorCode while ( $wpdadb_src->num_rows > 0 ) { $total += $wpdadb_src->num_rows; foreach ( $rows as $row ) { $wpdadb_dst->insert( $copy_table_name_dst, $row ); $wpdadb_dst->flush(); $wpdadb_dst->queries = null; } echo '<script>jQuery("p.wpda_pds_msg").remove();</script>'; echo '<p class="wpda_pds_msg">Copied ' . number_format( $total ) . ' rows...</p>'; ob_flush(); flush(); $i++; $wpdadb_src->flush(); $wpdadb_src->queries = null; $sql = $query . ' limit ' . $query_buffer_size . ' offset ' . $i * $query_buffer_size; $rows = $wpdadb_src->get_results( $sql, 'ARRAY_A' ); // phpcs:ignore Standard.Category.SniffName.ErrorCode } echo '<script>jQuery("p.wpda_pds_msg_create, p.wpda_pds_msg").css("display", "none");</script>'; } else { $rows = $wpdadb_src->get_results( $query, 'ARRAY_A' ); // phpcs:ignore Standard.Category.SniffName.ErrorCode foreach ( $rows as $row ) { $wpdadb_dst->insert( $copy_table_name_dst, $row ); } } $wpdadb_src->suppress_errors( $suppress_wpdadb_src ); $wpdadb_dst->suppress_errors( $suppress_wpdadb_dst ); return true; } /** * Performs bulk export. * * @since 1.5.0 */ protected function process_bulk_action_bulk_export() { // Check is there is anything to export. if ( $this->process_bulk_action_check_action( 'bulk-selected', __( 'Empty bulk selected', 'wp-data-access' ) ) ) { // Check if export is allowed. if ( $this->process_bulk_action_check_wpnonce( 'wpda-export-' . json_encode( $this->table_name ), '_wpnonce' ) ) { // Get arguments. $bulk_tabs = ( isset( $_REQUEST['bulk-selected'] ) ? $_REQUEST['bulk-selected'] : '' ); // input var okay; sanitization okay. $wp_nonce = wp_create_nonce( 'wpda-export-' . WPDA::get_current_user_login() ); $cnt = 0; $selected_tables = array(); foreach ( $bulk_tabs as $table_name ) { $export_table_name = sanitize_text_field( wp_unslash( $table_name ) ); // input var okay. $err_txt = ' ' . sprintf( __( '[table `%s`]', 'wp-data-access' ), $export_table_name ); if ( $this->process_bulk_action_check_table_exists( $export_table_name, $err_txt ) ) { $dbo_type = $this->get_dbo_type( $export_table_name ); if ( false === $dbo_type || 'VIEW' === $dbo_type || 'SYSTEM VIEW' === $dbo_type ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Cannot export `%s`', 'wp-data-access' ), $export_table_name ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); } else { $selected_tables[] = $export_table_name; $cnt++; } } } if ( 0 < $cnt ) { ?> <script type="application/javascript"> jQuery(function() { wpda_main_export( '<?php echo esc_attr( $this->schema_name ); ?>', '<?php echo esc_attr( $wp_nonce ); ?>', '<?php echo json_encode( $selected_tables ); ?>' ); }); </script> <?php } } } } /** * Checks request argument needed for (bulk) action to be performed. * * @param string $argument_name Request argument name. * @param string $msg Message on failure. * * @return bool TRUE = argument found in request. * @since 1.5.0 */ protected function process_bulk_action_check_action( $argument_name, $msg ) { if ( !isset( $_REQUEST[$argument_name] ) ) { // Nothing export. $msg = new WPDA_Message_Box(array( 'message_text' => $msg, )); $msg->box(); return false; } return true; } /** * Checks wpnonce against a specific action. * * @param string $wp_nonce_action Nonce action. * @param string $wp_nonce_arg Nonce argument. * * @return bool TRUE = action allowed. * @since 1.5.0 */ protected function process_bulk_action_check_wpnonce( $wp_nonce_action, $wp_nonce_arg ) { $wp_nonce = ( isset( $_REQUEST[$wp_nonce_arg] ) ? sanitize_text_field( wp_unslash( $_REQUEST[$wp_nonce_arg] ) ) : '' ); // input var okay. if ( !wp_verify_nonce( $wp_nonce, $wp_nonce_action ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Not authorized', 'wp-data-access' ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); return false; } return true; } /** * Checks if table exists. * * @param string $table_name Database table name. * @param string $err_txt Additional error text/info. * * @return bool TRUE = table exists. * @since 1.5.0 */ protected function process_bulk_action_check_table_exists( $table_name, $err_txt = '' ) { $wpda_dictionary = new WPDA_Dictionary_Exist($this->schema_name, $table_name); if ( !$wpda_dictionary->table_exists() ) { $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Not authorized [table not found]', 'wp-data-access' ) . $err_txt, 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); return false; } return true; } /** * Get database object type (VIEW, BASE_TABLE, SYSTEM VIEW). * * @param string $dbo_name Table or view name. * * @return string|boolean Database object type or false. * @since 1.5.0 */ protected function get_dbo_type( $dbo_name ) { $wpdadb = WPDADB::get_db_connection( $this->schema_name ); if ( null === $wpdadb ) { return false; } $query = $wpdadb->prepare( ' SELECT table_type AS table_type FROM information_schema.tables WHERE table_schema = %s AND table_name = %s ', array($wpdadb->dbname, $dbo_name) ); // db call ok; no-cache ok. $result = $wpdadb->get_results( $query, 'ARRAY_A' ); // phpcs:ignore Standard.Category.SniffName.ErrorCode if ( 1 === $wpdadb->num_rows ) { return $result[0]['table_type']; } else { return false; } } /** * Performs a drop table or view * * Method does not drop WordPress tables. */ protected function process_bulk_action_bulk_drop() { // Check is there is anything to drop. if ( $this->process_bulk_action_check_action( 'bulk-selected', __( 'Empty bulk selected', 'wp-data-access' ) ) ) { // Check if drop is allowed. if ( $this->process_bulk_action_check_wpnonce( 'wpda-drop-' . WPDA::get_current_user_login(), '_wpnonce3' ) ) { $bulk_tabs = ( isset( $_REQUEST['bulk-selected'] ) ? $_REQUEST['bulk-selected'] : '' ); // input var okay; sanitization okay. foreach ( $bulk_tabs as $table_name ) { // Drop table is not allowed for WordPress tables (double check). $drop_table_name = sanitize_text_field( wp_unslash( $table_name ) ); // input var okay. $err_txt = ' ' . sprintf( __( '[cannot drop WordPress table `%s`]', 'wp-data-access' ), $drop_table_name ); if ( $this->process_bulk_action_check_is_wp_table( $drop_table_name, $err_txt ) ) { // Check if table exists. $err_txt = ' ' . sprintf( __( '[table `%s`]', 'wp-data-access' ), $drop_table_name ); if ( $this->process_bulk_action_check_table_exists( $drop_table_name, $err_txt ) ) { $dbo_type = $this->get_dbo_type( $drop_table_name ); if ( false === $dbo_type || 'SYSTEM VIEW' === $dbo_type ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Cannot drop `%s`', 'wp-data-access' ), $drop_table_name ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); } else { if ( 'VIEW' === $dbo_type ) { // Drop view. if ( $this->drop_view( $drop_table_name ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'View `%s` dropped', 'wp-data-access' ), $drop_table_name ), )); $msg->box(); } } else { // Drop table. if ( $this->drop_table( $drop_table_name ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Table `%s` dropped', 'wp-data-access' ), $drop_table_name ), )); $msg->box(); $this->post_drop_table( $drop_table_name ); } } } } } } } } } /** * Silently delete table related info from repository (called after drop table) * * @param string $drop_table_name Database table name */ protected function post_drop_table( $drop_table_name ) { global $wpdb; $suppress = $wpdb->suppress_errors( true ); // Table settings... $wpdb->query( $wpdb->prepare( 'delete from `%1s` where wpda_schema_name = %s and wpda_table_name = %s ', // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders array(WPDA::remove_backticks( WPDA_Table_Settings_Model::get_base_table_name() ), $this->schema_name, $drop_table_name) ) ); // WordPress media library columns... $wpdb->query( $wpdb->prepare( 'delete from `%1s` where media_schema_name = %s and media_table_name = %s ', // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders array(WPDA::remove_backticks( WPDA_Media_Model::get_base_table_name() ), $this->schema_name, $drop_table_name) ) ); // Data menus... $wpdb->query( $wpdb->prepare( 'delete from `%1s` where menu_schema_name = %s and menu_table_name = %s ', // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders array(WPDA::remove_backticks( WPDA_User_Menus_Model::get_base_table_name() ), $this->schema_name, $drop_table_name) ) ); $wpdb->suppress_errors( $suppress ); } /** * Checks if table is a WordPress table. * * @param string $table_name Database table name. * @param string $err_txt Additional error text/info. * * @return bool TRUE = table is WordPress table. * @since 1.5.0 */ protected function process_bulk_action_check_is_wp_table( $table_name, $err_txt = '' ) { if ( WPDA::is_wp_table( $table_name ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Not authorized', 'wp-data-access' ) . $err_txt, 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); return false; } return true; } /** * Performs drop view. * * @param string $view_name Database view name. * * @return bool TRUE = view dropped. * @since 1.5.0 */ protected function drop_view( $view_name ) { $wpdadb = WPDADB::get_db_connection( $this->schema_name ); if ( null === $wpdadb ) { wp_die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) ); } return $wpdadb->query( 'drop view `' . str_replace( '`', '', $view_name ) . '`' ); // db call ok; no-cache ok. } /** * Performs drop table. * * @param string $table_name Database table name. * * @return bool TRUE = table dropped. * @since 1.5.0 */ protected function drop_table( $table_name ) { $wpdadb = WPDADB::get_db_connection( $this->schema_name ); if ( null === $wpdadb ) { wp_die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) ); } if ( WPDA::is_wp_table( $table_name ) ) { // Never ever allow dropping a WordPress table! $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Not authorized [cannot drop WordPress tables]', 'wp-data-access' ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); return false; } return $wpdadb->query( 'drop table `' . str_replace( '`', '', $table_name ) . '`' ); // db call ok; no-cache ok. } /** * Performs a truncate table * * Method does not truncate WordPress tables. */ protected function process_bulk_action_bulk_truncate() { // Check is there is anything to truncate. if ( $this->process_bulk_action_check_action( 'bulk-selected', __( 'No table defined', 'wp-data-access' ) ) ) { // Check if truncate is allowed. if ( $this->process_bulk_action_check_wpnonce( 'wpda-truncate-' . WPDA::get_current_user_login(), '_wpnonce4' ) ) { $bulk_tabs = ( isset( $_REQUEST['bulk-selected'] ) ? $_REQUEST['bulk-selected'] : '' ); // input var okay; sanitization okay. foreach ( $bulk_tabs as $table_name ) { // Truncate table is not allowed for WordPress tables (double check). $truncate_table_name = sanitize_text_field( wp_unslash( $table_name ) ); // input var okay. $err_txt = ' ' . sprintf( __( '[cannot truncate WordPress table `%s`]', 'wp-data-access' ), $truncate_table_name ); if ( $this->process_bulk_action_check_is_wp_table( $truncate_table_name, $err_txt ) ) { // Check if table exists. $err_txt = ' ' . sprintf( __( '[table `%s`]', 'wp-data-access' ), $truncate_table_name ); if ( $this->process_bulk_action_check_table_exists( $truncate_table_name, $err_txt ) ) { $dbo_type = $this->get_dbo_type( $truncate_table_name ); if ( false === $dbo_type || 'VIEW' === $dbo_type || 'SYSTEM VIEW' === $dbo_type ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Cannot truncate `%s`', 'wp-data-access' ), $truncate_table_name ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); } else { // Truncate table. if ( $this->truncate_table( $truncate_table_name ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Table `%s` truncated', 'wp-data-access' ), $truncate_table_name ), )); $msg->box(); } } } } } } } } /** * Performs truncate table. * * @param string $table_name Database table name. * * @return bool TRUE = table truncated. * @since 1.5.0 */ protected function truncate_table( $table_name ) { $wpdadb = WPDADB::get_db_connection( $this->schema_name ); if ( null === $wpdadb ) { wp_die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) ); } if ( WPDA::is_wp_table( $table_name ) ) { // Never ever allow truncating a WordPress table! $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Not authorized [cannot truncate WordPress tables]', 'wp-data-access' ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); return false; } return $wpdadb->query( $wpdadb->prepare( 'truncate table `%1s`', // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders [WPDA::remove_backticks( $table_name )] ) ); // db call ok; no-cache ok. } /** * Processes drop table request. * * @since 1.5.0 */ protected function process_bulk_action_drop() { // Check is there is anything to drop. if ( $this->process_bulk_action_check_action( 'drop_table_name', __( 'No table defined', 'wp-data-access' ) ) ) { // Drop table is not allowed for WordPress tables (double check). $drop_table_name = sanitize_text_field( wp_unslash( $_REQUEST['drop_table_name'] ) ); // input var okay. $err_txt = ' ' . sprintf( __( '[cannot drop WordPress table `%s`]', 'wp-data-access' ), $drop_table_name ); if ( $this->process_bulk_action_check_is_wp_table( $drop_table_name, $err_txt ) ) { // Check if table exists. if ( $this->process_bulk_action_check_table_exists( $drop_table_name ) ) { // Check if drop is allowed. if ( $this->process_bulk_action_check_wpnonce( "wpda-drop-{$drop_table_name}", '_wpnonce' ) ) { $dbo_type = $this->get_dbo_type( $drop_table_name ); if ( false === $dbo_type || 'SYSTEM VIEW' === $dbo_type ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Cannot drop `%s`', 'wp-data-access' ), $drop_table_name ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); } else { if ( 'VIEW' === $dbo_type ) { // Drop view. if ( $this->drop_view( $drop_table_name ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'View `%s` dropped', 'wp-data-access' ), $drop_table_name ), )); $msg->box(); } } else { // Drop table. if ( $this->drop_table( $drop_table_name ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Table `%s` dropped', 'wp-data-access' ), $drop_table_name ), )); $msg->box(); $this->post_drop_table( $drop_table_name ); } } } } } } } } /** * Processes truncate table request. * * @since 1.5.0 */ protected function process_bulk_action_truncate() { // Check is there is anything to truncate. if ( $this->process_bulk_action_check_action( 'truncate_table_name', __( 'No table defined', 'wp-data-access' ) ) ) { // Truncate table is not allowed for WordPress tables (double check). $truncate_table_name = sanitize_text_field( wp_unslash( $_REQUEST['truncate_table_name'] ) ); // input var okay. $err_txt = ' ' . sprintf( __( '[cannot truncate WordPress table `%s`]', 'wp-data-access' ), $truncate_table_name ); if ( $this->process_bulk_action_check_is_wp_table( $truncate_table_name, $err_txt ) ) { // Check if table exists. if ( $this->process_bulk_action_check_table_exists( $truncate_table_name ) ) { // Check if truncate is allowed. if ( $this->process_bulk_action_check_wpnonce( "wpda-truncate-{$truncate_table_name}", '_wpnonce' ) ) { // Truncate table. if ( $this->truncate_table( $truncate_table_name ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => sprintf( __( 'Table `%s` truncated', 'wp-data-access' ), $truncate_table_name ), )); $msg->box(); } } } } } } protected function add_header_actions() { $this->add_db_containers(); } /** * Overwrite method: add bulk array to check ddl allowed */ public function show() { ?> <script type='text/javascript'> var wpda_bulk = []; function wpda_bulk_valid() { var wpda_bulk_selected_valid = true; jQuery("input[name='bulk-selected[]']:checked").each(function () { var wpda_bulk_selected = jQuery(this).val(); wpda_bulk.every(function (item) { if (item === wpda_bulk_selected) { alert("Action not allowed on WordPress tables!"); wpda_bulk_selected_valid = false; } }); if (wpda_bulk_selected_valid === false) { return false; } }); return wpda_bulk_selected_valid; } function wpda_check_bulk() { action = jQuery("select[name='action']").val(); action2 = jQuery("select[name='action2']").val(); if (action === '-1') { if (action2 === 'bulk-drop' || action2 === 'bulk-truncate') { return wpda_bulk_valid(); } else { return true; } } else { if (action === 'bulk-drop' || action === 'bulk-truncate') { return wpda_bulk_valid(); } else { return true; } } } </script> <?php parent::show(); ?> <script type='text/javascript'> function show_hide_column(show) { for (i = 0; i <<?php echo esc_attr( self::$list_number ); ?>; i++) { if (show) { jQuery('#rownum_' + i + '_x1').attr('colspan', parseInt(jQuery('#rownum_' + i + '_x1').attr('colspan')) + 1); jQuery('#rownum_' + i + '_x2').attr('colspan', parseInt(jQuery('#rownum_' + i + '_x2').attr('colspan')) + 1); } else { jQuery('#rownum_' + i + '_x1').attr('colspan', parseInt(jQuery('#rownum_' + i + '_x1').attr('colspan')) - 1); jQuery('#rownum_' + i + '_x2').attr('colspan', parseInt(jQuery('#rownum_' + i + '_x2').attr('colspan')) - 1); } } jQuery('.wp-list-table').removeClass('fixed'); } jQuery(function () { jQuery("#doaction").on("click", function (e) { return wpda_check_bulk(); }); jQuery("#doaction2").on("click", function (e) { return wpda_check_bulk(); }); jQuery('#table_name-hide').on("click", function (e) { show_hide_column(jQuery('#table_name-hide').is(":checked")); }); jQuery('#table_type-hide').on("click", function (e) { show_hide_column(jQuery('#table_type-hide').is(":checked")); }); jQuery('#create_time-hide').on("click", function (e) { show_hide_column(jQuery('#create_time-hide').is(":checked")); }); jQuery('#table_rows-hide').on("click", function (e) { show_hide_column(jQuery('#table_rows-hide').is(":checked")); }); jQuery('#auto_increment-hide').on("click", function (e) { show_hide_column(jQuery('#auto_increment-hide').is(":checked")); }); jQuery('#engine-hide').on("click", function (e) { show_hide_column(jQuery('#engine-hide').is(":checked")); }); jQuery('#total_size-hide').on("click", function (e) { show_hide_column(jQuery('#total_size-hide').is(":checked")); }); jQuery('#data_size-hide').on("click", function (e) { show_hide_column(jQuery('#data_size-hide').is(":checked")); }); jQuery('#index_size-hide').on("click", function (e) { show_hide_column(jQuery('#index_size-hide').is(":checked")); }); jQuery('#overhead-hide').on("click", function (e) { show_hide_column(jQuery('#overhead-hide').is(":checked")); }); jQuery('#table_collation-hide').on("click", function (e) { show_hide_column(jQuery('#table_collation-hide').is(":checked")); }); }); </script> <?php } /** * Overwrite construct_where_clause() * * @since 1.0.0 * * @see WPDA_List_Table::construct_where_clause() */ protected function construct_where_clause() { $wpdadb = WPDADB::get_db_connection( $this->schema_name ); if ( null === $wpdadb ) { wp_die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) ); } global $wpdb; // Make sure we're selecting only tables that are in the WordPress database. $where_or_and = ( '' === $this->where ? ' where ' : ' and ' ); $this->where .= $wpdadb->prepare( " {$where_or_and} table_schema = %s ", $wpdadb->dbname ); // Since we are using a view, the default behaviour of the parent will not work for us. We have to // define our where clause manually. $cols = array(array( 'column_name' => 'table_name', 'data_type' => 'varchar', 'extra' => '', 'column_type' => 'varchar(64)', 'is_nullable' => 'NO', 'column_default' => null, )); $where = WPDA::construct_where_clause( $this->schema_name, $this->table_name, $cols, $this->search_value ); if ( '' !== $where ) { $this->where .= ' and ' . $where; } if ( $wpdb->dbname === $this->schema_name || '' === $this->schema_name ) { $table_access = WPDA::get_option( WPDA::OPTION_BE_TABLE_ACCESS ); } else { $table_access = get_option( WPDA::BACKEND_OPTIONNAME_DATABASE_ACCESS . $this->schema_name ); if ( false === $table_access ) { $table_access = 'show'; } } if ( 'hide' === $table_access ) { // No access to WordPress tables: filter WordPress table. $this->where .= " and table_name not in ('" . implode( "','", $wpdb->tables( 'all', true ) ) . "')"; } elseif ( 'select' === $table_access ) { if ( $wpdb->dbname === $this->schema_name || '' === $this->schema_name ) { $option = WPDA::get_option( WPDA::OPTION_BE_TABLE_ACCESS_SELECTED ); } else { $option = get_option( WPDA::BACKEND_OPTIONNAME_DATABASE_SELECTED . $this->schema_name ); if ( false === $option ) { $option = ''; } } if ( '' !== $option ) { // Allow only access to selected tables. $this->where .= " and table_name in ('" . implode( "','", $option ) . "')"; } else { // No tables selected: no access. $this->where .= ' and 1=2'; } } if ( null != $this->wpda_main_favourites ) { if ( false === $this->favourites && 'show' === $this->wpda_main_favourites ) { $where_or_and = ( '' === $this->where ? ' where ' : ' and ' ); $this->where .= " {$where_or_and} 1=2 "; } elseif ( is_array( $this->favourites ) ) { if ( 0 < count( $this->favourites ) ) { //phpcs:ignore - 8.1 proof $where_or_and = ( '' === $this->where ? ' where ' : ' and ' ); $in_or_not_in = ( 'show' === $this->wpda_main_favourites ? 'in' : 'not in' ); $this->where .= " {$where_or_and} table_name {$in_or_not_in} ('" . implode( "','", $this->favourites ) . "') "; } } } } protected function get_order_by() { $orderby = parent::get_order_by(); if ( '' !== $orderby ) { return $orderby; } else { return ' order by table_name asc '; } } /** * Overwrite method: add button to design a table */ protected function add_header_button() { ?> <form method="post" action="?page=<?php echo esc_attr( \WP_Data_Access_Admin::PAGE_DESIGNER ); ?>" style="display: inline-block; vertical-align: baseline;" > <div> <input type="hidden" name="action" value="create_table"> <input type="hidden" name="caller" value="dataexplorer"> <button type="submit" class="page-title-action wpda_tooltip" title="Create a new table design" > <i class="fas fa-plus-circle wpda_icon_on_button"></i> <?php echo __( 'Design new table', 'wp-data-access' ); ?> </button> </div> </form> <form id="wpda_linkto_backup" style="display: none" method="post" action="?page=<?php echo esc_attr( WP_Data_Access_Admin::PAGE_MAIN ); ?>&page_action=wpda_backup"> <input type="hidden" name="wpdaschema_name" value="<?php echo esc_attr( $this->schema_name ); ?>"> </form> <?php $this->wpda_import->add_button(); ?> <a href="javascript:void(0)" onclick="jQuery('#wpda_linkto_backup').submit()" class="page-title-action wpda_tooltip" title="Create unattended export jobs" ><i class="fas fa-cloud-download wpda_icon_on_button"></i> <?php echo __( 'Data Backup' ); ?></a> <?php } /** * Add container to create new database */ protected function add_db_containers() { if ( 'rdb:' === substr( $this->schema_name, 0, 4 ) ) { $rdb = WPDADB::get_remote_database( $this->schema_name ); } $host = ( isset( $rdb['host'] ) ? $rdb['host'] : '' ); $user = ( isset( $rdb['username'] ) ? $rdb['username'] : '' ); $passwd = ( isset( $rdb['password'] ) ? $rdb['password'] : '' ); $port = ( isset( $rdb['port'] ) ? $rdb['port'] : '' ); $schema = ( isset( $rdb['database'] ) ? $rdb['database'] : '' ); $ssl = ( isset( $rdb['ssl'] ) ? $rdb['ssl'] : '' ); $ssl_key = ( isset( $rdb['ssl_key'] ) ? $rdb['ssl_key'] : '' ); $ssl_cert = ( isset( $rdb['ssl_cert'] ) ? $rdb['ssl_cert'] : '' ); $ssl_ca = ( isset( $rdb['ssl_ca'] ) ? $rdb['ssl_ca'] : '' ); $ssl_path = ( isset( $rdb['ssl_path'] ) ? $rdb['ssl_path'] : '' ); $ssl_cipher = ( isset( $rdb['ssl_cipher'] ) ? $rdb['ssl_cipher'] : '' ); $readonly = ''; ?> <div id="wpda_db_edit" style="display:none;padding-top:10px;"> <div class="wpda_upload"> <form method="post" action="?page=<?php echo esc_attr( $this->page ); ?>" onsubmit="return editdb_validate_form();"> <div style="height:10px;"></div> <strong><?php echo __( 'Edit Remote Database Connection', 'wp-data-access' ); ?></strong> <div style="height:10px;"></div> <div> <label for="edit_remote_database" style="vertical-align:baseline;" class="database_item_label">Database name:</label> <input type="text" name="edit_remote_database" id="edit_remote_database" value="<?php echo esc_attr( $this->schema_name ); ?>" <?php echo esc_attr( $readonly ); ?>> <input type="hidden" name="edit_remote_database_old" value="<?php echo esc_attr( $this->schema_name ); ?>"> <div style="height:10px;"></div> <label for="edit_remote_host" style="vertical-align:baseline;" class="database_item_label">MySQL host:</label> <input type="text" name="edit_remote_host" id="edit_remote_host" value="<?php echo esc_attr( $host ); ?>"> <br/> <label for="edit_remote_user" style="vertical-align:baseline;" class="database_item_label">MySQL username:</label> <input type="text" name="edit_remote_user" id="edit_remote_user" value="<?php echo esc_attr( $user ); ?>"> <br/> <label for="edit_remote_passwd" style="vertical-align:baseline;" class="database_item_label" >MySQL password:</label> <input type="password" name="edit_remote_passwd" id="edit_remote_passwd" value="<?php echo esc_attr( $passwd ); ?>" autocomplete="new-password"> <i class="fas fa-eye" onclick="wpda_toggle_password('edit_remote_passwd', event)" style="cursor:pointer"></i> <br/> <label for="edit_remote_port" style="vertical-align:baseline;" class="database_item_label">MySQL port:</label> <input type="text" name="edit_remote_port" id="edit_remote_port" value="<?php echo esc_attr( $port ); ?>"> <br/> <label for="edit_remote_schema" style="vertical-align:baseline;" class="database_item_label">MySQL schema:</label> <input type="text" name="edit_remote_schema" id="edit_remote_schema" value="<?php echo esc_attr( $schema ); ?>"> <br/> <label style="line-height:30px;" for="edit_remote_ssl" style="vertical-align:baseline;" class="database_item_label">SSL:</label> <input type="checkbox" name="edit_remote_ssl" id="edit_remote_ssl" <?php echo ( 'on' === $ssl ? 'checked' : 'unchecked' ); ?> onclick="jQuery('#edit_remote_database_block_ssl').toggle()"> <div id="edit_remote_database_block_ssl" <?php echo ( 'on' === $ssl ? '' : 'style="display:none;"' ); ?>> <label for="edit_remote_client_key" style="vertical-align:baseline;" class="database_item_label">Client key:</label> <input type="text" name="edit_remote_client_key" id="edit_remote_client_key" value="<?php echo esc_attr( $ssl_key ); ?>"> <br/> <label for="edit_remote_client_certificate" style="vertical-align:baseline;" class="database_item_label">Client certificate:</label> <input type="text" name="edit_remote_client_certificate" id="edit_remote_client_certificate" value="<?php echo esc_attr( $ssl_cert ); ?>"> <br/> <label for="edit_remote_ca_certificate" style="vertical-align:baseline;" class="database_item_label">CA certificate:</label> <input type="text" name="edit_remote_ca_certificate" id="edit_remote_ca_certificate" value="<?php echo esc_attr( $ssl_ca ); ?>"> <br/> <label for="edit_remote_certificate_path" style="vertical-align:baseline;" class="database_item_label">Certificate path:</label> <input type="text" name="edit_remote_certificate_path" id="edit_remote_certificate_path" value="<?php echo esc_attr( $ssl_path ); ?>"> <br/> <label for="edit_remote_specified_cipher" style="vertical-align:baseline;" class="database_item_label">Specified Cipher:</label> <input type="text" name="edit_remote_specified_cipher" id="edit_remote_specified_cipher" value="<?php echo esc_attr( $ssl_cipher ); ?>"> </div> <div style="height:10px;"></div> <label class="database_item_label"></label> <input type="button" value="Test" onclick="test_remote_connection('edit_'); return false;" id="edit_remote_test_button" class="button"> <input type="button" value="Clear" onclick="test_remote_clear('edit_'); return false;" id="edit_remote_clear_button" class="button" style="display:none;"> <div style="height:10px;"></div> </div> <div id="edit_remote_database_block_test" style="display:none;"> <div id="edit_remote_database_block_test_content" class="remote_database_block_test_content"></div> <div style="height:10px;"></div> </div> <input type="submit" class="button button-primary" value="<?php echo __( 'Save', 'wp-data-access' ); ?>"> <a href="javascript:void(0)" onclick="jQuery('#wpda_db_edit').hide()" class="button button-secondary"><i class="fas fa-times-circle wpda_icon_on_button"></i> <?php echo __( 'Cancel', 'wp-data-access' ); ?></a> <input type="hidden" name="action" value="edit_db"> <?php wp_nonce_field( 'wpda-edit-db-from-data-explorer-' . WPDA::get_current_user_login(), '_wpnonceeditdb', false ); ?> </form> </div> </div> <div id="wpda_db_container" style="display:none;padding-top:10px;"> <div class="wpda_upload"> <form method="post" action="?page=<?php echo esc_attr( $this->page ); ?>" onsubmit="return createdb_validate_form();"> <div style="height:10px;"></div> <?php if ( $this->user_can_create_db ) { ?> <select name="database_location" id="database_location"> <option value="local" selected><?php echo __( 'Create local database', 'wp-data-access' ); ?></option> <option value="remote"><?php echo __( 'Add remote database connection', 'wp-data-access' ); ?></option> </select> <div style="height:10px;"></div> <div id="local_database_block"> <label for="local_database" style="vertical-align:baseline;" class="database_item_label">Database name:</label> <input type="text" name="local_database" id="local_database"> <div style="height:10px;"></div> </div> <?php } else { ?> <strong><?php echo __( 'Add remote database connection', 'wp-data-access' ); ?></strong> <div style="height:10px;"></div> <?php } ?> <div id="remote_database_block"<?php echo ( $this->user_can_create_db ? 'style="display:none;"' : '' ); ?>> <div> <label for="remote_database" style="vertical-align:baseline;" class="database_item_label">Database name:</label> <input type="text" name="remote_database" id="remote_database" value="rdb:"> <div style="height:10px;"></div> <label for="remote_host" style="vertical-align:baseline;" class="database_item_label">MySQL host:</label> <input type="text" name="remote_host" id="remote_host"> <br/> <label for="remote_user" style="vertical-align:baseline;" class="database_item_label">MySQL username:</label> <input type="text" name="remote_user" id="remote_user"> <br/> <label for="remote_passwd" style="vertical-align:baseline;" class="database_item_label">MySQL password:</label> <input type="password" name="remote_passwd" id="remote_passwd" autocomplete="new-password"> <i class="fas fa-eye" onclick="wpda_toggle_password('remote_passwd', event)" style="cursor:pointer"></i> <br/> <label for="remote_port" style="vertical-align:baseline;" class="database_item_label">MySQL port:</label> <input type="text" name="remote_port" id="remote_port" value="3306"> <br/> <label for="remote_schema" style="vertical-align:baseline;" class="database_item_label">MySQL schema:</label> <input type="text" name="remote_schema" id="remote_schema"> <br/> <label style="line-height:30px;" for="remote_ssl" style="vertical-align:baseline;" class="database_item_label">SSL:</label> <input type="checkbox" name="remote_ssl" id="remote_ssl" unchecked onclick="jQuery('#remote_database_block_ssl').toggle()"> <div id="remote_database_block_ssl" style="display:none;"> <label for="remote_client_key" style="vertical-align:baseline;" class="database_item_label">Client key:</label> <input type="text" name="remote_client_key" id="remote_client_key"> <br/> <label for="remote_client_certificate" style="vertical-align:baseline;" class="database_item_label">Client certificate:</label> <input type="text" name="remote_client_certificate" id="remote_client_certificate"> <br/> <label for="remote_ca_certificate" style="vertical-align:baseline;" class="database_item_label">CA certificate:</label> <input type="text" name="remote_ca_certificate" id="remote_ca_certificate"> <br/> <label for="remote_certificate_path" style="vertical-align:baseline;" class="database_item_label">Certificate path:</label> <input type="text" name="remote_certificate_path" id="remote_certificate_path"> <br/> <label for="remote_specified_cipher" style="vertical-align:baseline;" class="database_item_label">Specified Cipher:</label> <input type="text" name="remote_specified_cipher" id="remote_specified_cipher"> </div> <div style="height:10px;"></div> <label class="database_item_label"></label> <input type="button" value="Test" onclick="test_remote_connection(); return false;" id="remote_test_button" class="button"> <input type="button" value="Clear" onclick="test_remote_clear(); return false;" id="remote_clear_button" class="button" style="display:none;"> <div style="height:10px;"></div> </div> <div id="remote_database_block_test" style="display:none;"> <div id="remote_database_block_test_content" class="remote_database_block_test_content"></div> <div style="height:10px;"></div> </div> </div> <a href="javascript:void(0)" onclick="jQuery(this).closest('form').submit()" class="button button-primary"><i class="fas fa-cloud-upload wpda_icon_on_button"></i> <?php echo __( 'Save', 'wp-data-access' ); ?></a> <a href="javascript:void(0)" onclick="jQuery('#wpda_db_container').hide()" class="button button-secondary"><i class="fas fa-times-circle wpda_icon_on_button"></i> <?php echo __( 'Cancel', 'wp-data-access' ); ?></a> <input type="hidden" name="action" value="create_db"> <?php wp_nonce_field( 'wpda-create-db-from-data-explorer-' . WPDA::get_current_user_login(), '_wpnoncecreatedb', false ); ?> </form> </div> </div> <div style="display:none;"> <form id="wpda_form_drop_db" method="post" action="?page=<?php echo esc_attr( $this->page ); ?>"> <input type="text" name="database" id="drop_database"> <input type="hidden" name="action" value="drop_db"> <?php wp_nonce_field( 'wpda-drop-db-from-data-explorer-' . WPDA::get_current_user_login(), '_wpnoncedropdb', false ); ?> </form> </div> <script type='text/javascript'> function editdb_validate_form() { if (jQuery('#edit_remote_database').val()==='' || jQuery('#edit_remote_database').val()==='rdb:') { alert('Database name must be entered'); return false; } if (jQuery('#edit_remote_host').val()==='') { alert('MySQL host must be entered'); return false; } if (jQuery('#edit_remote_user').val()==='') { alert('MySQL username must be entered'); return false; } if (jQuery('#edit_remote_schema').val()==='') { alert('MySQL schema must be entered'); return false; } return true; } function createdb_validate_form() { if (jQuery('#database_location').val() === 'remote') { if (jQuery('#remote_database').val()==='' || jQuery('#remote_database').val()==='rdb:') { alert('Database name must be entered'); return false; } if (jQuery('#remote_host').val()==='') { alert('MySQL host must be entered'); return false; } if (jQuery('#remote_user').val()==='') { alert('MySQL username must be entered'); return false; } if (jQuery('#remote_schema').val()==='') { alert('MySQL schema must be entered'); return false; } } else { if (jQuery('#local_database').val()==='') { alert('Database name must be entered'); return false; } } return true; } function test_remote_clear(mode = '') { jQuery('#' + mode + 'remote_database_block_test_content').html(''); jQuery('#' + mode + 'remote_database_block_test').hide(); jQuery('#' + mode + 'remote_clear_button').hide(); } function test_remote_connection(mode = '') { host = jQuery('#' + mode + 'remote_host').val(); user = jQuery('#' + mode + 'remote_user').val(); pass = jQuery('#' + mode + 'remote_passwd').val(); port = jQuery('#' + mode + 'remote_port').val(); dbs = jQuery('#' + mode + 'remote_schema').val(); ssl = jQuery('#' + mode + 'remote_ssl').val(); ssl_key = jQuery('#' + mode + 'remote_client_key').val(); ssl_cert = jQuery('#' + mode + 'remote_client_certificate').val(); ssl_ca = jQuery('#' + mode + 'remote_ca_certificate').val(); ssl_path = jQuery('#' + mode + 'remote_certificate_path').val(); ssl_cipher = jQuery('#' + mode + 'remote_specified_cipher').val(); url = '//' + window.location.host + window.location.pathname + '?action=wpda_check_remote_database_connection'; jQuery('#' + mode + 'remote_test_button').val('Testing...'); jQuery.ajax({ method: 'POST', url: url, data: { host: host, user: user, passwd: pass, port: port, schema: dbs, ssl: ssl, ssl_key: ssl_key, ssl_cert: ssl_cert, ssl_ca: ssl_ca, ssl_path: ssl_path, ssl_cipher: ssl_cipher } }).done( function (msg) { jQuery('#' + mode + 'remote_database_block_test_content').html(msg); jQuery('#' + mode + 'remote_database_block_test').show(); } ).fail( function () { jQuery('#' + mode + 'remote_database_block_test_content').html('Preparing connection...<br/>Establishing connection...<br/><br/><strong>Remote database connection invalid</strong>'); jQuery('#' + mode + 'remote_database_block_test').show(); } ).always( function () { jQuery('#' + mode + 'remote_test_button').val('Test'); jQuery('#' + mode + 'remote_clear_button').show(); } ); } jQuery(function () { jQuery('#database_location').on('change', function () { if (jQuery(this).val() === 'remote') { jQuery('#local_database_block').hide(); jQuery('#remote_database_block').show(); } else { jQuery('#remote_database_block').hide(); jQuery('#local_database_block').show(); } }); jQuery('#remote_database, #edit_remote_database').keydown(function(e) { var field = this; setTimeout(function () { if (field.value.indexOf('rdb:') !== 0) { jQuery(field).val('rdb:'); } }, 1); }); }); </script> <?php } /** * Display the search box * * @param string $text The 'submit' button label. * @param string $input_id ID attribute value for the search input field. * * @since 1.6.0 */ public function search_box( $text, $input_id ) { global $wpdb; $input_id = $input_id . '-search-input'; $schemas = WPDA_Dictionary_Lists::get_db_schemas(); ?> <div style="padding-top:10px;padding-bottom:0;"> <span style="font-weight: bold;"><?php echo __( 'Database', 'wp-data-access' ); ?>:</span> <select id="wpda_main_db_schema_list"> <?php foreach ( $schemas as $schema ) { if ( $this->schema_name === $schema['schema_name'] ) { $selected = ' selected'; } else { $selected = ''; } if ( $wpdb->dbname === $schema['schema_name'] ) { $printed_schema_name = "WordPress database ({$schema['schema_name']})"; } else { $printed_schema_name = $schema['schema_name']; } ?> <option value="<?php echo esc_attr( $schema['schema_name'] ); ?>" <?php echo esc_attr( $selected ); ?>> <?php echo esc_attr( $printed_schema_name ); ?> </option> <?php } ?> </select> <a class="dashicons dashicons-edit wpda_tooltip" href="javascript:void(0)" <?php if ( 'rdb:' === substr( $this->schema_name, 0, 4 ) ) { ?> onclick="jQuery('#wpda_db_edit').show();" <?php } ?> style="<?php echo ( 'rdb:' === substr( $this->schema_name, 0, 4 ) ? '' : 'color:grey;cursor:default;' ); ?>vertical-align:middle;" title="<?php echo ( 'rdb:' === substr( $this->schema_name, 0, 4 ) ? __( 'Edit remote database connection', 'wp-data-access' ) : __( 'Not available for local database', 'wp-data-access' ) ); ?>">&nbsp;</a> <a class="dashicons dashicons-plus-alt wpda_tooltip" href="javascript:void(0)" onclick="jQuery('#wpda_db_container').show(); jQuery('#local_database').focus();" style="vertical-align:middle;" title="<?php echo esc_attr( $this->user_create_db_hint ); ?>">&nbsp;</a> <a class="dashicons dashicons-dismiss wpda_tooltip" id="wpda_drop_database" href="javascript:void(0)" <?php if ( $this->user_can_drop_db ) { if ( 'rdb:' === substr( $this->schema_name, 0, 4 ) ) { $msg = __( 'Delete remote database connection?\\nDoes not drop the database! Only deletes the remote database connection definition.', 'wp-data-access' ); } else { $msg = __( 'Drop selected database?', 'wp-data-access' ); } ?> onclick="if (confirm('<?php echo $msg; // phpcs:ignore WordPress.Security.EscapeOutput ?>')) { jQuery('#drop_database').val(jQuery('#wpda_main_db_schema_list').val()); jQuery('#wpda_form_drop_db').submit(); }" <?php } ?> style="<?php echo ( $this->user_can_drop_db ? 'color:#a00;' : 'color:grey;cursor:default;' ); ?>vertical-align:middle;" title="<?php echo esc_attr( $this->user_drop_db_hint ); ?>">&nbsp;</a> <a class="dashicons dashicons-admin-plugins wpda_tooltip" href="javascript:void(0)" onclick="wpda_dbinit_admin( '<?php echo esc_attr( $this->schema_name ); ?>', '<?php echo wp_create_nonce( 'wpda_dbinit_admin_' . WPDA::get_current_user_login() ); ?>' )" style="vertical-align:middle;" title="<?php echo __( "Create function wpda_get_wp_user_id()\nto get access to the WordPress user ID from database views", 'wp-data-access' ); ?>">&nbsp;</a> &nbsp;<span style="font-weight: bold;"><?php echo __( 'Favourites', 'wp-data-access' ); // phpcs:ignore WordPress.Security.EscapeOutput ?>:</span> <select id="wpda_main_favourites_list"> <option value="" <?php echo ( '' === $this->wpda_main_favourites ? 'selected' : '' ); ?>>Show all </option> <option value="show" <?php echo ( 'show' === $this->wpda_main_favourites ? 'selected' : '' ); ?>>Show favourites only </option> <option value="hide" <?php echo ( 'hide' === $this->wpda_main_favourites ? 'selected' : '' ); ?>>Hide favourites </option> </select> <?php if ( !(null === $this->search_value && !$this->has_items()) ) { ?> <p class="search-box"> <input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="<?php echo esc_attr( $this->search_item_name ); ?>" value="<?php echo esc_attr( $this->search_value ); ?>"/> <?php submit_button( $text, '', '', false, array( 'id' => 'search-submit', ) ); ?> <input type="hidden" name="<?php echo esc_attr( $this->search_item_name ); ?>_old_value" value="<?php echo esc_attr( $this->search_value ); ?>"/> </p> <?php } ?> </div> <script type='text/javascript'> jQuery(function () { jQuery("#wpda_main_db_schema_list").on("change", function () { jQuery("#wpda_main_db_schema").val(jQuery(this).val()); jQuery("#wpda_main_form :input[name='action']").val('-1'); jQuery("#wpda_main_form :input[name='action2']").val('-1'); jQuery("#wpda_main_form").submit(); }); jQuery("#wpda_main_favourites_list").on("change", function () { jQuery("#wpda_main_favourites").val(jQuery(this).val()); jQuery("#wpda_main_form :input[name='action']").val('-1'); jQuery("#wpda_main_form :input[name='action2']").val('-1'); jQuery("#wpda_main_form").submit(); }); }); </script> <?php } /** * Get schema name from cookie or list * * @return null|string * @since 1.6.0 */ protected function get_schema_name() { $cookie_name = $this->page . '_schema_name'; $wpda_main_db_schema = null; if ( isset( $_REQUEST['wpda_main_db_schema'] ) && '' !== $_REQUEST['wpda_main_db_schema'] ) { $wpda_main_db_schema = sanitize_text_field( wp_unslash( $_REQUEST['wpda_main_db_schema'] ) ); // input var okay. } elseif ( isset( $_COOKIE[$cookie_name] ) ) { $wpda_main_db_schema = sanitize_text_field( wp_unslash( $_COOKIE[$cookie_name] ) ); // input var okay. } if ( null !== $wpda_main_db_schema ) { if ( '' !== $wpda_main_db_schema && 'rdb:' !== substr( $wpda_main_db_schema, 0, 4 ) ) { if ( !WPDA_Dictionary_Exist::schema_exists( $wpda_main_db_schema ) ) { return WPDA::get_user_default_scheme(); } } if ( WPDA::schema_disabled( $wpda_main_db_schema ) ) { return WPDA::get_user_default_scheme(); } if ( WPDA::schema_exists( $wpda_main_db_schema ) ) { return $wpda_main_db_schema; } echo '<p><strong>' . sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $wpda_main_db_schema ) ) . '</strong></p>'; } return WPDA::get_user_default_scheme(); } /** * Get favourite selection from cookie or list * * @return null|string * @since 1.6.0 */ protected function get_favourites() { $cookie_name = $this->page . '_favourites'; if ( isset( $_REQUEST['wpda_main_favourites'] ) ) { return sanitize_text_field( wp_unslash( $_REQUEST['wpda_main_favourites'] ) ); // input var okay. } elseif ( isset( $_COOKIE[$cookie_name] ) ) { return sanitize_text_field( wp_unslash( $_COOKIE[$cookie_name] ) ); } else { return null; } } /** * Add labels to static function to make it available to class WPDA_List _View. Allowing to hide columns in * Data Explorer main page. * * @return array * @since 2.0.3 */ public static function column_headers_labels() { return array( 'table_name' => __( 'Name', 'wp-data-access' ), 'icons' => '', 'table_type' => __( 'Type', 'wp-data-access' ), 'create_time' => __( 'Creation Date', 'wp-data-access' ), 'table_rows' => __( '#Rows', 'wp-data-access' ), 'auto_increment' => __( 'Increment', 'wp-data-access' ), 'engine' => __( 'Engine', 'wp-data-access' ), 'total_size' => __( 'Size', 'wp-data-access' ), 'data_size' => __( 'Data Size', 'wp-data-access' ), 'index_size' => __( 'Index Size', 'wp-data-access' ), 'overhead' => __( 'Overhead', 'wp-data-access' ), 'table_collation' => __( 'Collation', 'wp-data-access' ), ); } }